1*2479191cSSebastian Reichel // SPDX-License-Identifier: GPL-2.0-only
2afbc67a9SRandy Dunlap /*
36d999718SSebastian Reichel  * CPCAP Power Button Input Driver
46d999718SSebastian Reichel  *
56d999718SSebastian Reichel  * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
66d999718SSebastian Reichel  */
76d999718SSebastian Reichel 
86d999718SSebastian Reichel #include <linux/module.h>
96d999718SSebastian Reichel #include <linux/init.h>
106d999718SSebastian Reichel #include <linux/kernel.h>
116d999718SSebastian Reichel #include <linux/errno.h>
126d999718SSebastian Reichel #include <linux/input.h>
136d999718SSebastian Reichel #include <linux/interrupt.h>
146d999718SSebastian Reichel #include <linux/regmap.h>
156d999718SSebastian Reichel #include <linux/of.h>
166d999718SSebastian Reichel #include <linux/platform_device.h>
176d999718SSebastian Reichel #include <linux/mfd/motorola-cpcap.h>
186d999718SSebastian Reichel 
196d999718SSebastian Reichel #define CPCAP_IRQ_ON 23
206d999718SSebastian Reichel #define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16))
216d999718SSebastian Reichel 
226d999718SSebastian Reichel struct cpcap_power_button {
236d999718SSebastian Reichel 	struct regmap *regmap;
246d999718SSebastian Reichel 	struct input_dev *idev;
256d999718SSebastian Reichel 	struct device *dev;
266d999718SSebastian Reichel };
276d999718SSebastian Reichel 
powerbutton_irq(int irq,void * _button)286d999718SSebastian Reichel static irqreturn_t powerbutton_irq(int irq, void *_button)
296d999718SSebastian Reichel {
306d999718SSebastian Reichel 	struct cpcap_power_button *button = _button;
316d999718SSebastian Reichel 	int val;
326d999718SSebastian Reichel 
336d999718SSebastian Reichel 	val = cpcap_sense_virq(button->regmap, irq);
346d999718SSebastian Reichel 	if (val < 0) {
356d999718SSebastian Reichel 		dev_err(button->dev, "irq read failed: %d", val);
366d999718SSebastian Reichel 		return IRQ_HANDLED;
376d999718SSebastian Reichel 	}
386d999718SSebastian Reichel 
396d999718SSebastian Reichel 	pm_wakeup_event(button->dev, 0);
406d999718SSebastian Reichel 	input_report_key(button->idev, KEY_POWER, val);
416d999718SSebastian Reichel 	input_sync(button->idev);
426d999718SSebastian Reichel 
436d999718SSebastian Reichel 	return IRQ_HANDLED;
446d999718SSebastian Reichel }
456d999718SSebastian Reichel 
cpcap_power_button_probe(struct platform_device * pdev)466d999718SSebastian Reichel static int cpcap_power_button_probe(struct platform_device *pdev)
476d999718SSebastian Reichel {
486d999718SSebastian Reichel 	struct cpcap_power_button *button;
4958ae4004STang Bin 	int irq;
506d999718SSebastian Reichel 	int err;
516d999718SSebastian Reichel 
5258ae4004STang Bin 	irq = platform_get_irq(pdev, 0);
5358ae4004STang Bin 	if (irq < 0)
5458ae4004STang Bin 		return irq;
5558ae4004STang Bin 
566d999718SSebastian Reichel 	button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
576d999718SSebastian Reichel 	if (!button)
586d999718SSebastian Reichel 		return -ENOMEM;
596d999718SSebastian Reichel 
606d999718SSebastian Reichel 	button->idev = devm_input_allocate_device(&pdev->dev);
616d999718SSebastian Reichel 	if (!button->idev)
626d999718SSebastian Reichel 		return -ENOMEM;
636d999718SSebastian Reichel 
646d999718SSebastian Reichel 	button->regmap = dev_get_regmap(pdev->dev.parent, NULL);
656d999718SSebastian Reichel 	if (!button->regmap)
666d999718SSebastian Reichel 		return -ENODEV;
676d999718SSebastian Reichel 
686d999718SSebastian Reichel 	button->dev = &pdev->dev;
696d999718SSebastian Reichel 
706d999718SSebastian Reichel 	button->idev->name = "cpcap-pwrbutton";
716d999718SSebastian Reichel 	button->idev->phys = "cpcap-pwrbutton/input0";
726d999718SSebastian Reichel 	input_set_capability(button->idev, EV_KEY, KEY_POWER);
736d999718SSebastian Reichel 
746d999718SSebastian Reichel 	err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
756d999718SSebastian Reichel 		powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button);
766d999718SSebastian Reichel 	if (err < 0) {
776d999718SSebastian Reichel 		dev_err(&pdev->dev, "IRQ request failed: %d\n", err);
786d999718SSebastian Reichel 		return err;
796d999718SSebastian Reichel 	}
806d999718SSebastian Reichel 
816d999718SSebastian Reichel 	err = input_register_device(button->idev);
826d999718SSebastian Reichel 	if (err) {
836d999718SSebastian Reichel 		dev_err(&pdev->dev, "Input register failed: %d\n", err);
846d999718SSebastian Reichel 		return err;
856d999718SSebastian Reichel 	}
866d999718SSebastian Reichel 
876d999718SSebastian Reichel 	device_init_wakeup(&pdev->dev, true);
886d999718SSebastian Reichel 
896d999718SSebastian Reichel 	return 0;
906d999718SSebastian Reichel }
916d999718SSebastian Reichel 
926d999718SSebastian Reichel #ifdef CONFIG_OF
936d999718SSebastian Reichel static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = {
946d999718SSebastian Reichel 	{ .compatible = "motorola,cpcap-pwrbutton" },
956d999718SSebastian Reichel 	{},
966d999718SSebastian Reichel };
976d999718SSebastian Reichel MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table);
986d999718SSebastian Reichel #endif
996d999718SSebastian Reichel 
1006d999718SSebastian Reichel static struct platform_driver cpcap_power_button_driver = {
1016d999718SSebastian Reichel 	.probe		= cpcap_power_button_probe,
1026d999718SSebastian Reichel 	.driver		= {
1036d999718SSebastian Reichel 		.name	= "cpcap-pwrbutton",
1046d999718SSebastian Reichel 		.of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table),
1056d999718SSebastian Reichel 	},
1066d999718SSebastian Reichel };
1076d999718SSebastian Reichel module_platform_driver(cpcap_power_button_driver);
1086d999718SSebastian Reichel 
1096d999718SSebastian Reichel MODULE_ALIAS("platform:cpcap-pwrbutton");
1106d999718SSebastian Reichel MODULE_DESCRIPTION("CPCAP Power Button");
1116d999718SSebastian Reichel MODULE_LICENSE("GPL");
1126d999718SSebastian Reichel MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
113