/* * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver * * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Author: Felipe Balbi * Author: Marcin Niestroj * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include struct tps6521x_data { unsigned int reg_status; unsigned int pb_mask; const char *name; }; static const struct tps6521x_data tps65217_data = { .reg_status = TPS65217_REG_STATUS, .pb_mask = TPS65217_STATUS_PB, .name = "tps65217_pwrbutton", }; static const struct tps6521x_data tps65218_data = { .reg_status = TPS65218_REG_STATUS, .pb_mask = TPS65218_STATUS_PB_STATE, .name = "tps65218_pwrbutton", }; struct tps6521x_pwrbutton { struct device *dev; struct regmap *regmap; struct input_dev *idev; const struct tps6521x_data *data; char phys[32]; }; static const struct of_device_id of_tps6521x_pb_match[] = { { .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data }, { .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data }, { }, }; MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) { struct tps6521x_pwrbutton *pwr = _pwr; const struct tps6521x_data *tps_data = pwr->data; unsigned int reg; int error; error = regmap_read(pwr->regmap, tps_data->reg_status, ®); if (error) { dev_err(pwr->dev, "can't read register: %d\n", error); goto out; } if (reg & tps_data->pb_mask) { input_report_key(pwr->idev, KEY_POWER, 1); pm_wakeup_event(pwr->dev, 0); } else { input_report_key(pwr->idev, KEY_POWER, 0); } input_sync(pwr->idev); out: return IRQ_HANDLED; } static int tps6521x_pb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tps6521x_pwrbutton *pwr; struct input_dev *idev; const struct of_device_id *match; int error; int irq; match = of_match_node(of_tps6521x_pb_match, dev->of_node); if (!match) return -ENXIO; pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); if (!pwr) return -ENOMEM; pwr->data = match->data; idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; idev->name = pwr->data->name; snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", pwr->data->name); idev->phys = pwr->phys; idev->dev.parent = dev; idev->id.bustype = BUS_I2C; input_set_capability(idev, EV_KEY, KEY_POWER); pwr->regmap = dev_get_regmap(dev->parent, NULL); pwr->dev = dev; pwr->idev = idev; device_init_wakeup(dev, true); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "No IRQ resource!\n"); return -EINVAL; } error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pwr->data->name, pwr); if (error) { dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error); return error; } error= input_register_device(idev); if (error) { dev_err(dev, "Can't register power button: %d\n", error); return error; } return 0; } static const struct platform_device_id tps6521x_pwrbtn_id_table[] = { { "tps65218-pwrbutton", }, { "tps65217-pwrbutton", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps6521x_pwrbtn_id_table); static struct platform_driver tps6521x_pb_driver = { .probe = tps6521x_pb_probe, .driver = { .name = "tps6521x_pwrbutton", .of_match_table = of_tps6521x_pb_match, }, .id_table = tps6521x_pwrbtn_id_table, }; module_platform_driver(tps6521x_pb_driver); MODULE_DESCRIPTION("TPS6521X Power Button"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Felipe Balbi ");