xref: /linux/drivers/input/misc/88pm80x_onkey.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Marvell 88PM80x ONKEY driver
4  *
5  * Copyright (C) 2012 Marvell International Ltd.
6  * Haojian Zhuang <haojian.zhuang@marvell.com>
7  * Qiao Zhou <zhouqiao@marvell.com>
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/input.h>
13 #include <linux/mfd/88pm80x.h>
14 #include <linux/regmap.h>
15 #include <linux/slab.h>
16 
17 #define PM800_LONG_ONKEY_EN		(1 << 0)
18 #define PM800_LONG_KEY_DELAY		(8)	/* 1 .. 16 seconds */
19 #define PM800_LONKEY_PRESS_TIME		((PM800_LONG_KEY_DELAY-1) << 4)
20 #define PM800_LONKEY_PRESS_TIME_MASK	(0xF0)
21 #define PM800_SW_PDOWN			(1 << 5)
22 
23 struct pm80x_onkey_info {
24 	struct input_dev *idev;
25 	struct pm80x_chip *pm80x;
26 	struct regmap *map;
27 	int irq;
28 };
29 
30 /* 88PM80x gives us an interrupt when ONKEY is held */
pm80x_onkey_handler(int irq,void * data)31 static irqreturn_t pm80x_onkey_handler(int irq, void *data)
32 {
33 	struct pm80x_onkey_info *info = data;
34 	int ret = 0;
35 	unsigned int val;
36 
37 	ret = regmap_read(info->map, PM800_STATUS_1, &val);
38 	if (ret < 0) {
39 		dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
40 		return IRQ_NONE;
41 	}
42 	val &= PM800_ONKEY_STS1;
43 
44 	input_report_key(info->idev, KEY_POWER, val);
45 	input_sync(info->idev);
46 
47 	return IRQ_HANDLED;
48 }
49 
50 static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
51 			 pm80x_dev_resume);
52 
pm80x_onkey_probe(struct platform_device * pdev)53 static int pm80x_onkey_probe(struct platform_device *pdev)
54 {
55 
56 	struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
57 	struct pm80x_onkey_info *info;
58 	int err;
59 
60 	info = kzalloc(sizeof(*info), GFP_KERNEL);
61 	if (!info)
62 		return -ENOMEM;
63 
64 	info->pm80x = chip;
65 
66 	info->irq = platform_get_irq(pdev, 0);
67 	if (info->irq < 0) {
68 		err = -EINVAL;
69 		goto out;
70 	}
71 
72 	info->map = info->pm80x->regmap;
73 	if (!info->map) {
74 		dev_err(&pdev->dev, "no regmap!\n");
75 		err = -EINVAL;
76 		goto out;
77 	}
78 
79 	info->idev = input_allocate_device();
80 	if (!info->idev) {
81 		dev_err(&pdev->dev, "Failed to allocate input dev\n");
82 		err = -ENOMEM;
83 		goto out;
84 	}
85 
86 	info->idev->name = "88pm80x_on";
87 	info->idev->phys = "88pm80x_on/input0";
88 	info->idev->id.bustype = BUS_I2C;
89 	info->idev->dev.parent = &pdev->dev;
90 	info->idev->evbit[0] = BIT_MASK(EV_KEY);
91 	__set_bit(KEY_POWER, info->idev->keybit);
92 
93 	err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
94 					    IRQF_ONESHOT, "onkey", info);
95 	if (err < 0) {
96 		dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
97 			info->irq, err);
98 		goto out_reg;
99 	}
100 
101 	err = input_register_device(info->idev);
102 	if (err) {
103 		dev_err(&pdev->dev, "Can't register input device: %d\n", err);
104 		goto out_irq;
105 	}
106 
107 	platform_set_drvdata(pdev, info);
108 
109 	/* Enable long onkey detection */
110 	regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
111 			   PM800_LONG_ONKEY_EN);
112 	/* Set 8-second interval */
113 	regmap_update_bits(info->map, PM800_RTC_MISC3,
114 			   PM800_LONKEY_PRESS_TIME_MASK,
115 			   PM800_LONKEY_PRESS_TIME);
116 
117 	device_init_wakeup(&pdev->dev, 1);
118 	return 0;
119 
120 out_irq:
121 	pm80x_free_irq(info->pm80x, info->irq, info);
122 out_reg:
123 	input_free_device(info->idev);
124 out:
125 	kfree(info);
126 	return err;
127 }
128 
pm80x_onkey_remove(struct platform_device * pdev)129 static void pm80x_onkey_remove(struct platform_device *pdev)
130 {
131 	struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
132 
133 	pm80x_free_irq(info->pm80x, info->irq, info);
134 	input_unregister_device(info->idev);
135 	kfree(info);
136 }
137 
138 static struct platform_driver pm80x_onkey_driver = {
139 	.driver = {
140 		   .name = "88pm80x-onkey",
141 		   .pm = &pm80x_onkey_pm_ops,
142 		   },
143 	.probe = pm80x_onkey_probe,
144 	.remove_new = pm80x_onkey_remove,
145 };
146 
147 module_platform_driver(pm80x_onkey_driver);
148 
149 MODULE_LICENSE("GPL");
150 MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
151 MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
152 MODULE_ALIAS("platform:88pm80x-onkey");
153