blob: f9aed87cccd47cb5e68f6eec53cb2464fb46958b [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * ASRMICRO PM80x EXTON2 driver
3 *
4 * Copyright: (C) Copyright 2024 ASR Microelectronics (Shanghai) Co., Ltd.
5 *
6 * This file is subject to the terms and conditions of the GNU General
7 * Public License. See the file "COPYING" in the main directory of this
8 * archive for more details.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/input.h>
23#include <linux/mfd/88pm80x.h>
24#include <linux/regmap.h>
25#include <linux/slab.h>
26#include <linux/of.h>
27#include <linux/irq.h>
28
29struct pm80x_exton2_info {
30 struct input_dev *idev;
31 struct device *dev;
32 struct pm80x_chip *pm80x;
33 struct regmap *map;
34 int irq;
35};
36
37/* 88PM80x gives us an interrupt when EXTON2 is held */
38static irqreturn_t pm80x_exton2_handler(int irq, void *data)
39{
40 struct pm80x_exton2_info *info = data;
41 int ret = 0;
42 unsigned int val;
43
44 ret = regmap_read(info->map, PM800_STATUS_1, &val);
45 if (ret < 0) {
46 dev_err(info->idev->dev.parent,
47 "failed to read status: %d\n", ret);
48 return IRQ_NONE;
49 }
50 val &= PM800_CHG_STS1;
51 if (val) {
52 pm_stay_awake(info->dev);
53 } else {
54 pm_wakeup_event(info->dev, 8000);
55 }
56
57 pr_info("pm80x_exton2 press = %x\n", !!val); /* sys-sanity */
58
59 input_report_key(info->idev, KEY_POWER, val);
60 input_sync(info->idev);
61
62 return IRQ_HANDLED;
63}
64
65static SIMPLE_DEV_PM_OPS(pm80x_exton2_pm_ops, pm80x_dev_suspend,
66 pm80x_dev_resume);
67
68static int pm80x_exton2_dt_init(struct device_node *np,
69 struct pm80x_exton2_info *info)
70{
71 return 0;
72}
73
74static int pm80x_exton2_probe(struct platform_device *pdev)
75{
76 struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
77 struct device_node *node = pdev->dev.of_node;
78 struct pm80x_exton2_info *info;
79 int err;
80
81 if (CHIP_PM803 == chip->type || CHIP_PM801 == chip->type)
82 return -ENODEV;
83
84 info = kzalloc(sizeof(struct pm80x_exton2_info), GFP_KERNEL);
85 if (!info)
86 return -ENOMEM;
87
88 info->pm80x = chip;
89
90 info->irq = platform_get_irq(pdev, 0);
91 if (info->irq < 0) {
92 dev_err(&pdev->dev, "No IRQ resource!\n");
93 err = -EINVAL;
94 goto out;
95 }
96
97 info->map = info->pm80x->regmap;
98 if (!info->map) {
99 dev_err(&pdev->dev, "no regmap!\n");
100 err = -EINVAL;
101 goto out;
102 }
103
104 info->idev = input_allocate_device();
105 if (!info->idev) {
106 dev_err(&pdev->dev, "Failed to allocate input dev\n");
107 err = -ENOMEM;
108 goto out;
109 }
110
111 info->dev = &pdev->dev;
112 info->idev->name = "88pm80x_exton2";
113 info->idev->phys = "88pm80x_exton2/input0";
114 info->idev->id.bustype = BUS_I2C;
115 info->idev->dev.parent = &pdev->dev;
116 info->idev->evbit[0] = BIT_MASK(EV_KEY);
117 __set_bit(KEY_POWER, info->idev->keybit);
118
119 err = pm80x_exton2_dt_init(node, info);
120 if (err < 0) {
121 err = -ENODEV;
122 goto out_reg;
123 }
124
125 irq_set_status_flags(info->irq, IRQ_NOAUTOEN);
126 err = pm80x_request_irq(info->pm80x, info->irq, pm80x_exton2_handler,
127 IRQF_ONESHOT | IRQF_NO_SUSPEND,
128 "exton2", info);
129 if (err < 0) {
130 dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
131 info->irq, err);
132 goto out_reg;
133 }
134
135 err = input_register_device(info->idev);
136 if (err) {
137 dev_err(&pdev->dev, "Can't register input device: %d\n", err);
138 goto out_irq;
139 }
140
141 if (CHIP_PM813S_A0_ID == chip->chip_id ||
142 CHIP_PM813S_A1_ID == chip->chip_id)
143 regmap_update_bits(info->map, PM800_RTC_MISC4, 0xf0, 0x70);
144 else
145 regmap_update_bits(info->map, PM800_RTC_MISC4, 0xf0, 0xf0);
146
147 platform_set_drvdata(pdev, info);
148
149 /* set FAULT_WU_EN */
150 if (CHIP_PM803 == chip->type ||
151 CHIP_PM813 == chip->type)
152 regmap_update_bits(chip->regmap, PM800_RTC_MISC5, PM803_FAULT_WAKEUP_EN,
153 PM803_FAULT_WAKEUP_EN);
154 else
155 regmap_update_bits(chip->regmap, PM800_RTC_MISC5, PM800_FAULT_WAKEUP_EN,
156 PM800_FAULT_WAKEUP_EN);
157
158 /* need to set fault_wakeup for asr new pmics */
159 if (CHIP_PM802 == chip->type ||
160 CHIP_PM813_ID == chip->chip_id)
161 regmap_update_bits(chip->regmap, PM800_RTC_MISC5, PM800_FAULT_WAKEUP,
162 PM800_FAULT_WAKEUP);
163 else if (CHIP_PM803 == chip->type ||
164 CHIP_PM813S_A0_ID == chip->chip_id ||
165 CHIP_PM813S_A1_ID == chip->chip_id)
166 regmap_update_bits(chip->regmap, PM800_RTC_MISC5, PM803_FAULT_WAKEUP,
167 PM803_FAULT_WAKEUP);
168
169 /* OnKey runs timer => need wake-constraint {Press ... Release+mSec}
170 * (especially to avoid false-panic-emulation).
171 * device_init_wakeup() creates "88pm80x-ext2key"
172 * but pm_stay_awake(&info->idev->dev) doesn't as required
173 * Do NOT use the driver's name but the "exton2" instead.
174 */
175 device_init_wakeup(info->dev, true);
176 enable_irq(info->irq);
177 return 0;
178
179out_irq:
180 pm80x_free_irq(info->pm80x, info->irq, info);
181out_reg:
182 input_free_device(info->idev);
183out:
184 kfree(info);
185 return err;
186}
187
188static int pm80x_exton2_remove(struct platform_device *pdev)
189{
190 struct pm80x_exton2_info *info = platform_get_drvdata(pdev);
191
192 disable_irq(info->irq);
193 pm80x_free_irq(info->pm80x, info->irq, info);
194 input_unregister_device(info->idev);
195 kfree(info);
196 return 0;
197}
198
199static void pm80x_exton2_shutdown(struct platform_device *pdev)
200{
201 struct pm80x_exton2_info *info = platform_get_drvdata(pdev);
202 disable_irq(info->irq);
203 pm80x_free_irq(info->pm80x, info->irq, info);
204 return;
205}
206
207static const struct of_device_id pm80x_exton2_dt_match[] = {
208 { .compatible = "88pm80x-ext2key", },
209 { },
210};
211MODULE_DEVICE_TABLE(of, pm80x_exton2_dt_match);
212
213static struct platform_driver pm80x_exton2_driver = {
214 .driver = {
215 .name = "88pm80x-ext2key",
216 .owner = THIS_MODULE,
217 .pm = &pm80x_exton2_pm_ops,
218 .of_match_table = of_match_ptr(pm80x_exton2_dt_match),
219 },
220 .probe = pm80x_exton2_probe,
221 .remove = pm80x_exton2_remove,
222 .shutdown = pm80x_exton2_shutdown,
223};
224
225module_platform_driver(pm80x_exton2_driver);
226
227MODULE_LICENSE("GPL");
228MODULE_DESCRIPTION("ASRMICRO PM80x EXTON2 driver");
229MODULE_AUTHOR("Xuhong Gao <xuhonggao@asrmicro.com>");
230MODULE_ALIAS("platform:pm80x-exton2");