1*5a729246SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25fafed3eSFelipe Balbi /*
3722dc546SMarcin Niestroj  * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver
45fafed3eSFelipe Balbi  *
55fafed3eSFelipe Balbi  * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
65fafed3eSFelipe Balbi  * Author: Felipe Balbi <balbi@ti.com>
7722dc546SMarcin Niestroj  * Author: Marcin Niestroj <m.niestroj@grinn-global.com>
85fafed3eSFelipe Balbi  */
95fafed3eSFelipe Balbi 
105fafed3eSFelipe Balbi #include <linux/init.h>
115fafed3eSFelipe Balbi #include <linux/input.h>
125fafed3eSFelipe Balbi #include <linux/interrupt.h>
135fafed3eSFelipe Balbi #include <linux/kernel.h>
14722dc546SMarcin Niestroj #include <linux/mfd/tps65217.h>
155fafed3eSFelipe Balbi #include <linux/mfd/tps65218.h>
165fafed3eSFelipe Balbi #include <linux/module.h>
175fafed3eSFelipe Balbi #include <linux/of.h>
185fafed3eSFelipe Balbi #include <linux/platform_device.h>
19722dc546SMarcin Niestroj #include <linux/regmap.h>
205fafed3eSFelipe Balbi #include <linux/slab.h>
215fafed3eSFelipe Balbi 
22722dc546SMarcin Niestroj struct tps6521x_data {
23722dc546SMarcin Niestroj 	unsigned int reg_status;
24722dc546SMarcin Niestroj 	unsigned int pb_mask;
25722dc546SMarcin Niestroj 	const char *name;
265fafed3eSFelipe Balbi };
275fafed3eSFelipe Balbi 
28722dc546SMarcin Niestroj static const struct tps6521x_data tps65217_data = {
29722dc546SMarcin Niestroj 	.reg_status = TPS65217_REG_STATUS,
30722dc546SMarcin Niestroj 	.pb_mask = TPS65217_STATUS_PB,
31722dc546SMarcin Niestroj 	.name = "tps65217_pwrbutton",
32722dc546SMarcin Niestroj };
33722dc546SMarcin Niestroj 
34722dc546SMarcin Niestroj static const struct tps6521x_data tps65218_data = {
35722dc546SMarcin Niestroj 	.reg_status = TPS65218_REG_STATUS,
36722dc546SMarcin Niestroj 	.pb_mask = TPS65218_STATUS_PB_STATE,
37722dc546SMarcin Niestroj 	.name = "tps65218_pwrbutton",
38722dc546SMarcin Niestroj };
39722dc546SMarcin Niestroj 
40722dc546SMarcin Niestroj struct tps6521x_pwrbutton {
41722dc546SMarcin Niestroj 	struct device *dev;
42722dc546SMarcin Niestroj 	struct regmap *regmap;
43722dc546SMarcin Niestroj 	struct input_dev *idev;
44722dc546SMarcin Niestroj 	const struct tps6521x_data *data;
45722dc546SMarcin Niestroj 	char phys[32];
46722dc546SMarcin Niestroj };
47722dc546SMarcin Niestroj 
48722dc546SMarcin Niestroj static const struct of_device_id of_tps6521x_pb_match[] = {
49722dc546SMarcin Niestroj 	{ .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data },
50722dc546SMarcin Niestroj 	{ .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data },
51722dc546SMarcin Niestroj 	{ },
52722dc546SMarcin Niestroj };
53722dc546SMarcin Niestroj MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match);
54722dc546SMarcin Niestroj 
tps6521x_pb_irq(int irq,void * _pwr)55722dc546SMarcin Niestroj static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr)
565fafed3eSFelipe Balbi {
57722dc546SMarcin Niestroj 	struct tps6521x_pwrbutton *pwr = _pwr;
58722dc546SMarcin Niestroj 	const struct tps6521x_data *tps_data = pwr->data;
595fafed3eSFelipe Balbi 	unsigned int reg;
605fafed3eSFelipe Balbi 	int error;
615fafed3eSFelipe Balbi 
62722dc546SMarcin Niestroj 	error = regmap_read(pwr->regmap, tps_data->reg_status, &reg);
635fafed3eSFelipe Balbi 	if (error) {
645fafed3eSFelipe Balbi 		dev_err(pwr->dev, "can't read register: %d\n", error);
655fafed3eSFelipe Balbi 		goto out;
665fafed3eSFelipe Balbi 	}
675fafed3eSFelipe Balbi 
68722dc546SMarcin Niestroj 	if (reg & tps_data->pb_mask) {
695fafed3eSFelipe Balbi 		input_report_key(pwr->idev, KEY_POWER, 1);
705fafed3eSFelipe Balbi 		pm_wakeup_event(pwr->dev, 0);
715fafed3eSFelipe Balbi 	} else {
725fafed3eSFelipe Balbi 		input_report_key(pwr->idev, KEY_POWER, 0);
735fafed3eSFelipe Balbi 	}
745fafed3eSFelipe Balbi 
755fafed3eSFelipe Balbi 	input_sync(pwr->idev);
765fafed3eSFelipe Balbi 
775fafed3eSFelipe Balbi out:
785fafed3eSFelipe Balbi 	return IRQ_HANDLED;
795fafed3eSFelipe Balbi }
805fafed3eSFelipe Balbi 
tps6521x_pb_probe(struct platform_device * pdev)81722dc546SMarcin Niestroj static int tps6521x_pb_probe(struct platform_device *pdev)
825fafed3eSFelipe Balbi {
835fafed3eSFelipe Balbi 	struct device *dev = &pdev->dev;
84722dc546SMarcin Niestroj 	struct tps6521x_pwrbutton *pwr;
855fafed3eSFelipe Balbi 	struct input_dev *idev;
86722dc546SMarcin Niestroj 	const struct of_device_id *match;
875fafed3eSFelipe Balbi 	int error;
885fafed3eSFelipe Balbi 	int irq;
895fafed3eSFelipe Balbi 
908ace98dfSGuenter Roeck 	match = of_match_node(of_tps6521x_pb_match, dev->of_node);
91722dc546SMarcin Niestroj 	if (!match)
92722dc546SMarcin Niestroj 		return -ENXIO;
93722dc546SMarcin Niestroj 
945fafed3eSFelipe Balbi 	pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
955fafed3eSFelipe Balbi 	if (!pwr)
965fafed3eSFelipe Balbi 		return -ENOMEM;
975fafed3eSFelipe Balbi 
98722dc546SMarcin Niestroj 	pwr->data = match->data;
99722dc546SMarcin Niestroj 
1005fafed3eSFelipe Balbi 	idev = devm_input_allocate_device(dev);
1015fafed3eSFelipe Balbi 	if (!idev)
1025fafed3eSFelipe Balbi 		return -ENOMEM;
1035fafed3eSFelipe Balbi 
104722dc546SMarcin Niestroj 	idev->name = pwr->data->name;
105722dc546SMarcin Niestroj 	snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0",
106722dc546SMarcin Niestroj 		pwr->data->name);
107722dc546SMarcin Niestroj 	idev->phys = pwr->phys;
1085fafed3eSFelipe Balbi 	idev->dev.parent = dev;
1095fafed3eSFelipe Balbi 	idev->id.bustype = BUS_I2C;
1105fafed3eSFelipe Balbi 
1115fafed3eSFelipe Balbi 	input_set_capability(idev, EV_KEY, KEY_POWER);
1125fafed3eSFelipe Balbi 
1138ace98dfSGuenter Roeck 	pwr->regmap = dev_get_regmap(dev->parent, NULL);
1145fafed3eSFelipe Balbi 	pwr->dev = dev;
1155fafed3eSFelipe Balbi 	pwr->idev = idev;
1165fafed3eSFelipe Balbi 	device_init_wakeup(dev, true);
1175fafed3eSFelipe Balbi 
1185fafed3eSFelipe Balbi 	irq = platform_get_irq(pdev, 0);
1190bec8b7eSStephen Boyd 	if (irq < 0)
120722dc546SMarcin Niestroj 		return -EINVAL;
121722dc546SMarcin Niestroj 
122722dc546SMarcin Niestroj 	error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq,
1235fafed3eSFelipe Balbi 					  IRQF_TRIGGER_RISING |
1245fafed3eSFelipe Balbi 						IRQF_TRIGGER_FALLING |
1255fafed3eSFelipe Balbi 						IRQF_ONESHOT,
126722dc546SMarcin Niestroj 					  pwr->data->name, pwr);
1275fafed3eSFelipe Balbi 	if (error) {
1288ace98dfSGuenter Roeck 		dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error);
1295fafed3eSFelipe Balbi 		return error;
1305fafed3eSFelipe Balbi 	}
1315fafed3eSFelipe Balbi 
1325fafed3eSFelipe Balbi 	error= input_register_device(idev);
1335fafed3eSFelipe Balbi 	if (error) {
1345fafed3eSFelipe Balbi 		dev_err(dev, "Can't register power button: %d\n", error);
1355fafed3eSFelipe Balbi 		return error;
1365fafed3eSFelipe Balbi 	}
1375fafed3eSFelipe Balbi 
1385fafed3eSFelipe Balbi 	return 0;
1395fafed3eSFelipe Balbi }
1405fafed3eSFelipe Balbi 
14177aa9926SKeerthy static const struct platform_device_id tps6521x_pwrbtn_id_table[] = {
14277aa9926SKeerthy 	{ "tps65218-pwrbutton", },
14377aa9926SKeerthy 	{ "tps65217-pwrbutton", },
14477aa9926SKeerthy 	{ /* sentinel */ }
14577aa9926SKeerthy };
14677aa9926SKeerthy MODULE_DEVICE_TABLE(platform, tps6521x_pwrbtn_id_table);
14777aa9926SKeerthy 
148722dc546SMarcin Niestroj static struct platform_driver tps6521x_pb_driver = {
149722dc546SMarcin Niestroj 	.probe	= tps6521x_pb_probe,
1505fafed3eSFelipe Balbi 	.driver	= {
151722dc546SMarcin Niestroj 		.name	= "tps6521x_pwrbutton",
152722dc546SMarcin Niestroj 		.of_match_table = of_tps6521x_pb_match,
1535fafed3eSFelipe Balbi 	},
15477aa9926SKeerthy 	.id_table = tps6521x_pwrbtn_id_table,
1555fafed3eSFelipe Balbi };
156722dc546SMarcin Niestroj module_platform_driver(tps6521x_pb_driver);
1575fafed3eSFelipe Balbi 
158722dc546SMarcin Niestroj MODULE_DESCRIPTION("TPS6521X Power Button");
1595fafed3eSFelipe Balbi MODULE_LICENSE("GPL v2");
1605fafed3eSFelipe Balbi MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
161