blob: 1d92cb9bde051c96318da1767ad4d9935ac2758d [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * ASRMICRO PM80x EXTON1N driver
3 *
4 * Copyright: (C) Copyright 2023 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_exton1n_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 EXTON1N is held */
38static irqreturn_t pm80x_exton1n_handler(int irq, void *data)
39{
40 struct pm80x_exton1n_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_EXTON_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_exton1n 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_exton1n_pm_ops, pm80x_dev_suspend,
66 pm80x_dev_resume);
67
68static int pm80x_exton1n_dt_init(struct device_node *np,
69 struct pm80x_exton1n_info *info)
70{
71 return 0;
72}
73
74static int pm80x_exton1n_probe(struct platform_device *pdev)
75{
76
77 struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
78 struct device_node *node = pdev->dev.of_node;
79 struct pm80x_exton1n_info *info;
80 int err;
81
82 info = kzalloc(sizeof(struct pm80x_exton1n_info), GFP_KERNEL);
83 if (!info)
84 return -ENOMEM;
85
86 info->pm80x = chip;
87
88 info->irq = platform_get_irq(pdev, 0);
89 if (info->irq < 0) {
90 dev_err(&pdev->dev, "No IRQ resource!\n");
91 err = -EINVAL;
92 goto out;
93 }
94
95 info->map = info->pm80x->regmap;
96 if (!info->map) {
97 dev_err(&pdev->dev, "no regmap!\n");
98 err = -EINVAL;
99 goto out;
100 }
101
102 info->idev = input_allocate_device();
103 if (!info->idev) {
104 dev_err(&pdev->dev, "Failed to allocate input dev\n");
105 err = -ENOMEM;
106 goto out;
107 }
108
109 info->dev = &pdev->dev;
110 info->idev->name = "88pm80x_exton1n";
111 info->idev->phys = "88pm80x_exton1n/input0";
112 info->idev->id.bustype = BUS_I2C;
113 info->idev->dev.parent = &pdev->dev;
114 info->idev->evbit[0] = BIT_MASK(EV_KEY);
115 __set_bit(KEY_POWER, info->idev->keybit);
116
117 err = pm80x_exton1n_dt_init(node, info);
118 if (err < 0) {
119 err = -ENODEV;
120 goto out_reg;
121 }
122
123 irq_set_status_flags(info->irq, IRQ_NOAUTOEN);
124 err = pm80x_request_irq(info->pm80x, info->irq, pm80x_exton1n_handler,
125 IRQF_ONESHOT | IRQF_NO_SUSPEND,
126 "exton1n", info);
127 if (err < 0) {
128 dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
129 info->irq, err);
130 goto out_reg;
131 }
132
133 err = input_register_device(info->idev);
134 if (err) {
135 dev_err(&pdev->dev, "Can't register input device: %d\n", err);
136 goto out_irq;
137 }
138
139 /* exton1n db time: 16ms */
140#if defined(CONFIG_MFD_PM802) || defined(CONFIG_MFD_PM803)
141 if (CHIP_PM802 == info->pm80x->type || CHIP_PM803 == info->pm80x->type)
142 regmap_update_bits(info->map, PM800_RTC_MISC3, 0x0f, 0x0f);
143 else
144#endif
145 regmap_update_bits(info->map, PM800_RTC_MISC4, 0x30, 0x30);
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-exton1n"
172 * but pm_stay_awake(&info->idev->dev) doesn't as required
173 * Do NOT use the driver's name but the "exton1n" 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_exton1n_remove(struct platform_device *pdev)
189{
190 struct pm80x_exton1n_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_exton1n_shutdown(struct platform_device *pdev)
200{
201 struct pm80x_exton1n_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_exton1n_dt_match[] = {
208 { .compatible = "marvell,88pm80x-usb", },
209 { },
210};
211MODULE_DEVICE_TABLE(of, pm80x_exton1n_dt_match);
212
213static struct platform_driver pm80x_exton1n_driver = {
214 .driver = {
215 .name = "88pm80x-exton1n",
216 .owner = THIS_MODULE,
217 .pm = &pm80x_exton1n_pm_ops,
218 .of_match_table = of_match_ptr(pm80x_exton1n_dt_match),
219 },
220 .probe = pm80x_exton1n_probe,
221 .remove = pm80x_exton1n_remove,
222 .shutdown = pm80x_exton1n_shutdown,
223};
224
225module_platform_driver(pm80x_exton1n_driver);
226
227MODULE_LICENSE("GPL");
228MODULE_DESCRIPTION("ASRMICRO PM80x EXTON1N driver");
229MODULE_AUTHOR("Xuhong Gao <xuhonggao@asrmicro.com>");
230MODULE_ALIAS("platform:pm80x-exton1n");