blob: e09b1fae42e10228dda29594c4bcdd588613a98e [file] [log] [blame]
Carlo Caione5b6c26a2014-12-29 11:20:54 -08001/*
2 * axp20x power button driver.
3 *
4 * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
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
Hans de Goede9b13a4c2017-03-09 09:55:49 -080016#include <linux/acpi.h>
Carlo Caione5b6c26a2014-12-29 11:20:54 -080017#include <linux/errno.h>
18#include <linux/irq.h>
19#include <linux/init.h>
20#include <linux/input.h>
21#include <linux/interrupt.h>
22#include <linux/kernel.h>
23#include <linux/mfd/axp20x.h>
24#include <linux/module.h>
Hans de Goede5ecc1e92021-10-18 16:33:24 +020025#include <linux/platform_data/x86/soc.h>
Carlo Caione5b6c26a2014-12-29 11:20:54 -080026#include <linux/platform_device.h>
27#include <linux/regmap.h>
28#include <linux/slab.h>
29
30#define AXP20X_PEK_STARTUP_MASK (0xc0)
31#define AXP20X_PEK_SHUTDOWN_MASK (0x03)
32
Quentin Schulzfbc1b322017-08-14 22:16:27 -070033struct axp20x_info {
34 const struct axp20x_time *startup_time;
35 unsigned int startup_mask;
36 const struct axp20x_time *shutdown_time;
37 unsigned int shutdown_mask;
38};
39
Carlo Caione5b6c26a2014-12-29 11:20:54 -080040struct axp20x_pek {
41 struct axp20x_dev *axp20x;
42 struct input_dev *input;
Quentin Schulzfbc1b322017-08-14 22:16:27 -070043 struct axp20x_info *info;
Carlo Caione5b6c26a2014-12-29 11:20:54 -080044 int irq_dbr;
45 int irq_dbf;
46};
47
48struct axp20x_time {
49 unsigned int time;
50 unsigned int idx;
51};
52
53static const struct axp20x_time startup_time[] = {
54 { .time = 128, .idx = 0 },
55 { .time = 1000, .idx = 2 },
56 { .time = 3000, .idx = 1 },
57 { .time = 2000, .idx = 3 },
58};
59
Quentin Schulzc3cc9442017-08-14 22:19:42 -070060static const struct axp20x_time axp221_startup_time[] = {
61 { .time = 128, .idx = 0 },
62 { .time = 1000, .idx = 1 },
63 { .time = 2000, .idx = 2 },
64 { .time = 3000, .idx = 3 },
65};
66
Carlo Caione5b6c26a2014-12-29 11:20:54 -080067static const struct axp20x_time shutdown_time[] = {
68 { .time = 4000, .idx = 0 },
69 { .time = 6000, .idx = 1 },
70 { .time = 8000, .idx = 2 },
71 { .time = 10000, .idx = 3 },
72};
73
Quentin Schulzfbc1b322017-08-14 22:16:27 -070074static const struct axp20x_info axp20x_info = {
75 .startup_time = startup_time,
76 .startup_mask = AXP20X_PEK_STARTUP_MASK,
77 .shutdown_time = shutdown_time,
78 .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
Carlo Caione5b6c26a2014-12-29 11:20:54 -080079};
80
Quentin Schulzc3cc9442017-08-14 22:19:42 -070081static const struct axp20x_info axp221_info = {
82 .startup_time = axp221_startup_time,
83 .startup_mask = AXP20X_PEK_STARTUP_MASK,
84 .shutdown_time = shutdown_time,
85 .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
86};
87
Quentin Schulzfbc1b322017-08-14 22:16:27 -070088static ssize_t axp20x_show_attr(struct device *dev,
89 const struct axp20x_time *time,
90 unsigned int mask, char *buf)
Carlo Caione5b6c26a2014-12-29 11:20:54 -080091{
92 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
Carlo Caione5b6c26a2014-12-29 11:20:54 -080093 unsigned int val;
94 int ret, i;
95
96 ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
97 if (ret != 0)
98 return ret;
99
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700100 val &= mask;
101 val >>= ffs(mask) - 1;
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800102
103 for (i = 0; i < 4; i++)
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700104 if (val == time[i].idx)
105 val = time[i].time;
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800106
107 return sprintf(buf, "%u\n", val);
108}
109
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700110static ssize_t axp20x_show_attr_startup(struct device *dev,
111 struct device_attribute *attr,
112 char *buf)
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800113{
114 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700115
116 return axp20x_show_attr(dev, axp20x_pek->info->startup_time,
117 axp20x_pek->info->startup_mask, buf);
118}
119
120static ssize_t axp20x_show_attr_shutdown(struct device *dev,
121 struct device_attribute *attr,
122 char *buf)
123{
124 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
125
126 return axp20x_show_attr(dev, axp20x_pek->info->shutdown_time,
127 axp20x_pek->info->shutdown_mask, buf);
128}
129
130static ssize_t axp20x_store_attr(struct device *dev,
131 const struct axp20x_time *time,
132 unsigned int mask, const char *buf,
133 size_t count)
134{
135 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800136 char val_str[20];
137 size_t len;
138 int ret, i;
139 unsigned int val, idx = 0;
140 unsigned int best_err = UINT_MAX;
141
142 val_str[sizeof(val_str) - 1] = '\0';
143 strncpy(val_str, buf, sizeof(val_str) - 1);
144 len = strlen(val_str);
145
146 if (len && val_str[len - 1] == '\n')
147 val_str[len - 1] = '\0';
148
149 ret = kstrtouint(val_str, 10, &val);
150 if (ret)
151 return ret;
152
153 for (i = 3; i >= 0; i--) {
154 unsigned int err;
155
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700156 err = abs(time[i].time - val);
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800157 if (err < best_err) {
158 best_err = err;
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700159 idx = time[i].idx;
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800160 }
161
162 if (!err)
163 break;
164 }
165
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700166 idx <<= ffs(mask) - 1;
167 ret = regmap_update_bits(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY,
168 mask, idx);
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800169 if (ret != 0)
170 return -EINVAL;
Dmitry Torokhovb388de82014-12-29 15:13:06 -0800171
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800172 return count;
173}
174
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700175static ssize_t axp20x_store_attr_startup(struct device *dev,
176 struct device_attribute *attr,
177 const char *buf, size_t count)
178{
179 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800180
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700181 return axp20x_store_attr(dev, axp20x_pek->info->startup_time,
182 axp20x_pek->info->startup_mask, buf, count);
183}
184
185static ssize_t axp20x_store_attr_shutdown(struct device *dev,
186 struct device_attribute *attr,
187 const char *buf, size_t count)
188{
189 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
190
191 return axp20x_store_attr(dev, axp20x_pek->info->shutdown_time,
192 axp20x_pek->info->shutdown_mask, buf, count);
193}
194
Ben Dooks (Codethink)cbe821a2019-12-19 11:13:13 -0800195static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup,
196 axp20x_store_attr_startup);
197static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown,
198 axp20x_store_attr_shutdown);
Dmitry Torokhovb388de82014-12-29 15:13:06 -0800199
Greg Kroah-Hartmand99995a2019-08-11 23:42:31 -0700200static struct attribute *axp20x_attrs[] = {
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700201 &dev_attr_startup.attr,
202 &dev_attr_shutdown.attr,
Dmitry Torokhovb388de82014-12-29 15:13:06 -0800203 NULL,
204};
Greg Kroah-Hartmand99995a2019-08-11 23:42:31 -0700205ATTRIBUTE_GROUPS(axp20x);
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800206
207static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
208{
Hans de Goede97470702020-05-05 19:06:32 -0700209 struct axp20x_pek *axp20x_pek = pwr;
210 struct input_dev *idev = axp20x_pek->input;
211
212 if (!idev)
213 return IRQ_HANDLED;
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800214
Hans de Goedeeeeee402015-06-24 14:25:54 -0700215 /*
216 * The power-button is connected to ground so a falling edge (dbf)
217 * means it is pressed.
218 */
219 if (irq == axp20x_pek->irq_dbf)
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800220 input_report_key(idev, KEY_POWER, true);
Hans de Goedeeeeee402015-06-24 14:25:54 -0700221 else if (irq == axp20x_pek->irq_dbr)
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800222 input_report_key(idev, KEY_POWER, false);
223
224 input_sync(idev);
225
226 return IRQ_HANDLED;
227}
228
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800229static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
230 struct platform_device *pdev)
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800231{
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800232 struct input_dev *idev;
233 int error;
234
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800235 axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
236 if (!axp20x_pek->input)
237 return -ENOMEM;
238
239 idev = axp20x_pek->input;
240
241 idev->name = "axp20x-pek";
242 idev->phys = "m1kbd/input2";
243 idev->dev.parent = &pdev->dev;
244
245 input_set_capability(idev, EV_KEY, KEY_POWER);
246
247 input_set_drvdata(idev, axp20x_pek);
248
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800249 error = input_register_device(idev);
250 if (error) {
251 dev_err(&pdev->dev, "Can't register input device: %d\n",
252 error);
253 return error;
254 }
255
256 return 0;
257}
258
Hans de Goede5ecc1e92021-10-18 16:33:24 +0200259static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek)
Hans de Goede8d4b3132017-06-02 17:18:47 -0700260{
Hans de Goede8d4b3132017-06-02 17:18:47 -0700261 if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) &&
262 axp20x_pek->axp20x->variant == AXP288_ID) {
Hans de Goede8d4b3132017-06-02 17:18:47 -0700263 /*
264 * On Cherry Trail platforms (hrv == 3), do not register the
Hans de Goede0fd5f222017-06-02 17:50:22 -0700265 * input device if there is an "INTCFD9" or "ACPI0011" gpio
Hans de Goede8d4b3132017-06-02 17:18:47 -0700266 * button ACPI device, as that handles the power button too,
267 * and otherwise we end up reporting all presses twice.
268 */
Hans de Goede5ecc1e92021-10-18 16:33:24 +0200269 if (soc_intel_is_cht() &&
270 (acpi_dev_present("INTCFD9", NULL, -1) ||
Hans de Goede0fd5f222017-06-02 17:50:22 -0700271 acpi_dev_present("ACPI0011", NULL, -1)))
Hans de Goede8d4b3132017-06-02 17:18:47 -0700272 return false;
Hans de Goede8d4b3132017-06-02 17:18:47 -0700273 }
274
275 return true;
276}
Hans de Goede8d4b3132017-06-02 17:18:47 -0700277
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800278static int axp20x_pek_probe(struct platform_device *pdev)
279{
280 struct axp20x_pek *axp20x_pek;
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700281 const struct platform_device_id *match = platform_get_device_id(pdev);
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800282 int error;
283
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700284 if (!match) {
285 dev_err(&pdev->dev, "Failed to get platform_device_id\n");
286 return -EINVAL;
287 }
288
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800289 axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
290 GFP_KERNEL);
291 if (!axp20x_pek)
292 return -ENOMEM;
293
294 axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
295
Hans de Goede97470702020-05-05 19:06:32 -0700296 axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
297 if (axp20x_pek->irq_dbr < 0)
298 return axp20x_pek->irq_dbr;
299 axp20x_pek->irq_dbr = regmap_irq_get_virq(
300 axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbr);
301
302 axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
303 if (axp20x_pek->irq_dbf < 0)
304 return axp20x_pek->irq_dbf;
305 axp20x_pek->irq_dbf = regmap_irq_get_virq(
306 axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbf);
307
Hans de Goede5ecc1e92021-10-18 16:33:24 +0200308 if (axp20x_pek_should_register_input(axp20x_pek)) {
Hans de Goede9b13a4c2017-03-09 09:55:49 -0800309 error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
310 if (error)
311 return error;
312 }
Hans de Goedef2bd5a92017-03-09 09:47:24 -0800313
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700314 axp20x_pek->info = (struct axp20x_info *)match->driver_data;
315
Hans de Goede97470702020-05-05 19:06:32 -0700316 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
317 axp20x_pek_irq, 0,
318 "axp20x-pek-dbr", axp20x_pek);
319 if (error < 0) {
320 dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
321 axp20x_pek->irq_dbr, error);
322 return error;
323 }
324
325 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
326 axp20x_pek_irq, 0,
327 "axp20x-pek-dbf", axp20x_pek);
328 if (error < 0) {
329 dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
330 axp20x_pek->irq_dbf, error);
331 return error;
332 }
333
334 device_init_wakeup(&pdev->dev, true);
335
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800336 platform_set_drvdata(pdev, axp20x_pek);
337
338 return 0;
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800339}
340
Samuel Hollandfe77f9b2020-01-21 22:03:14 -0800341static int __maybe_unused axp20x_pek_suspend(struct device *dev)
342{
343 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
344
345 /*
346 * As nested threaded IRQs are not automatically disabled during
347 * suspend, we must explicitly disable non-wakeup IRQs.
348 */
349 if (device_may_wakeup(dev)) {
350 enable_irq_wake(axp20x_pek->irq_dbf);
351 enable_irq_wake(axp20x_pek->irq_dbr);
352 } else {
353 disable_irq(axp20x_pek->irq_dbf);
354 disable_irq(axp20x_pek->irq_dbr);
355 }
356
357 return 0;
358}
359
360static int __maybe_unused axp20x_pek_resume(struct device *dev)
361{
362 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
363
364 if (device_may_wakeup(dev)) {
365 disable_irq_wake(axp20x_pek->irq_dbf);
366 disable_irq_wake(axp20x_pek->irq_dbr);
367 } else {
368 enable_irq(axp20x_pek->irq_dbf);
369 enable_irq(axp20x_pek->irq_dbr);
370 }
371
372 return 0;
373}
374
Hans de Goede58be7682017-06-02 17:51:42 -0700375static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev)
376{
377 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
378
379 if (axp20x_pek->axp20x->variant != AXP288_ID)
380 return 0;
381
382 /*
383 * Clear interrupts from button presses during suspend, to avoid
384 * a wakeup power-button press getting reported to userspace.
385 */
386 regmap_write(axp20x_pek->axp20x->regmap,
387 AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8,
388 BIT(AXP288_IRQ_POKN % 8));
389
390 return 0;
391}
392
393static const struct dev_pm_ops axp20x_pek_pm_ops = {
Samuel Hollandfe77f9b2020-01-21 22:03:14 -0800394 SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume)
Hans de Goede58be7682017-06-02 17:51:42 -0700395#ifdef CONFIG_PM_SLEEP
396 .resume_noirq = axp20x_pek_resume_noirq,
397#endif
398};
399
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700400static const struct platform_device_id axp_pek_id_match[] = {
401 {
402 .name = "axp20x-pek",
403 .driver_data = (kernel_ulong_t)&axp20x_info,
404 },
Quentin Schulzc3cc9442017-08-14 22:19:42 -0700405 {
406 .name = "axp221-pek",
407 .driver_data = (kernel_ulong_t)&axp221_info,
408 },
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700409 { /* sentinel */ }
410};
Hans de Goede481c2092017-10-19 15:38:50 -0700411MODULE_DEVICE_TABLE(platform, axp_pek_id_match);
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700412
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800413static struct platform_driver axp20x_pek_driver = {
414 .probe = axp20x_pek_probe,
Quentin Schulzfbc1b322017-08-14 22:16:27 -0700415 .id_table = axp_pek_id_match,
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800416 .driver = {
417 .name = "axp20x-pek",
Hans de Goede58be7682017-06-02 17:51:42 -0700418 .pm = &axp20x_pek_pm_ops,
Greg Kroah-Hartmand99995a2019-08-11 23:42:31 -0700419 .dev_groups = axp20x_groups,
Carlo Caione5b6c26a2014-12-29 11:20:54 -0800420 },
421};
422module_platform_driver(axp20x_pek_driver);
423
424MODULE_DESCRIPTION("axp20x Power Button");
425MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
426MODULE_LICENSE("GPL");