2 * axp20x power button driver.
4 * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
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.
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.
16 #include <linux/acpi.h>
17 #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>
25 #include <linux/platform_device.h>
26 #include <linux/regmap.h>
27 #include <linux/slab.h>
29 #define AXP20X_PEK_STARTUP_MASK (0xc0)
30 #define AXP20X_PEK_SHUTDOWN_MASK (0x03)
33 const struct axp20x_time
*startup_time
;
34 unsigned int startup_mask
;
35 const struct axp20x_time
*shutdown_time
;
36 unsigned int shutdown_mask
;
40 struct axp20x_dev
*axp20x
;
41 struct input_dev
*input
;
42 struct axp20x_info
*info
;
52 static const struct axp20x_time startup_time
[] = {
53 { .time
= 128, .idx
= 0 },
54 { .time
= 1000, .idx
= 2 },
55 { .time
= 3000, .idx
= 1 },
56 { .time
= 2000, .idx
= 3 },
59 static const struct axp20x_time axp221_startup_time
[] = {
60 { .time
= 128, .idx
= 0 },
61 { .time
= 1000, .idx
= 1 },
62 { .time
= 2000, .idx
= 2 },
63 { .time
= 3000, .idx
= 3 },
66 static const struct axp20x_time shutdown_time
[] = {
67 { .time
= 4000, .idx
= 0 },
68 { .time
= 6000, .idx
= 1 },
69 { .time
= 8000, .idx
= 2 },
70 { .time
= 10000, .idx
= 3 },
73 static const struct axp20x_info axp20x_info
= {
74 .startup_time
= startup_time
,
75 .startup_mask
= AXP20X_PEK_STARTUP_MASK
,
76 .shutdown_time
= shutdown_time
,
77 .shutdown_mask
= AXP20X_PEK_SHUTDOWN_MASK
,
80 static const struct axp20x_info axp221_info
= {
81 .startup_time
= axp221_startup_time
,
82 .startup_mask
= AXP20X_PEK_STARTUP_MASK
,
83 .shutdown_time
= shutdown_time
,
84 .shutdown_mask
= AXP20X_PEK_SHUTDOWN_MASK
,
87 static ssize_t
axp20x_show_attr(struct device
*dev
,
88 const struct axp20x_time
*time
,
89 unsigned int mask
, char *buf
)
91 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
95 ret
= regmap_read(axp20x_pek
->axp20x
->regmap
, AXP20X_PEK_KEY
, &val
);
100 val
>>= ffs(mask
) - 1;
102 for (i
= 0; i
< 4; i
++)
103 if (val
== time
[i
].idx
)
106 return sprintf(buf
, "%u\n", val
);
109 static ssize_t
axp20x_show_attr_startup(struct device
*dev
,
110 struct device_attribute
*attr
,
113 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
115 return axp20x_show_attr(dev
, axp20x_pek
->info
->startup_time
,
116 axp20x_pek
->info
->startup_mask
, buf
);
119 static ssize_t
axp20x_show_attr_shutdown(struct device
*dev
,
120 struct device_attribute
*attr
,
123 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
125 return axp20x_show_attr(dev
, axp20x_pek
->info
->shutdown_time
,
126 axp20x_pek
->info
->shutdown_mask
, buf
);
129 static ssize_t
axp20x_store_attr(struct device
*dev
,
130 const struct axp20x_time
*time
,
131 unsigned int mask
, const char *buf
,
134 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
138 unsigned int val
, idx
= 0;
139 unsigned int best_err
= UINT_MAX
;
141 val_str
[sizeof(val_str
) - 1] = '\0';
142 strncpy(val_str
, buf
, sizeof(val_str
) - 1);
143 len
= strlen(val_str
);
145 if (len
&& val_str
[len
- 1] == '\n')
146 val_str
[len
- 1] = '\0';
148 ret
= kstrtouint(val_str
, 10, &val
);
152 for (i
= 3; i
>= 0; i
--) {
155 err
= abs(time
[i
].time
- val
);
156 if (err
< best_err
) {
165 idx
<<= ffs(mask
) - 1;
166 ret
= regmap_update_bits(axp20x_pek
->axp20x
->regmap
, AXP20X_PEK_KEY
,
174 static ssize_t
axp20x_store_attr_startup(struct device
*dev
,
175 struct device_attribute
*attr
,
176 const char *buf
, size_t count
)
178 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
180 return axp20x_store_attr(dev
, axp20x_pek
->info
->startup_time
,
181 axp20x_pek
->info
->startup_mask
, buf
, count
);
184 static ssize_t
axp20x_store_attr_shutdown(struct device
*dev
,
185 struct device_attribute
*attr
,
186 const char *buf
, size_t count
)
188 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
190 return axp20x_store_attr(dev
, axp20x_pek
->info
->shutdown_time
,
191 axp20x_pek
->info
->shutdown_mask
, buf
, count
);
194 static DEVICE_ATTR(startup
, 0644, axp20x_show_attr_startup
,
195 axp20x_store_attr_startup
);
196 static DEVICE_ATTR(shutdown
, 0644, axp20x_show_attr_shutdown
,
197 axp20x_store_attr_shutdown
);
199 static struct attribute
*axp20x_attrs
[] = {
200 &dev_attr_startup
.attr
,
201 &dev_attr_shutdown
.attr
,
204 ATTRIBUTE_GROUPS(axp20x
);
206 static irqreturn_t
axp20x_pek_irq(int irq
, void *pwr
)
208 struct axp20x_pek
*axp20x_pek
= pwr
;
209 struct input_dev
*idev
= axp20x_pek
->input
;
215 * The power-button is connected to ground so a falling edge (dbf)
216 * means it is pressed.
218 if (irq
== axp20x_pek
->irq_dbf
)
219 input_report_key(idev
, KEY_POWER
, true);
220 else if (irq
== axp20x_pek
->irq_dbr
)
221 input_report_key(idev
, KEY_POWER
, false);
228 static int axp20x_pek_probe_input_device(struct axp20x_pek
*axp20x_pek
,
229 struct platform_device
*pdev
)
231 struct input_dev
*idev
;
234 axp20x_pek
->input
= devm_input_allocate_device(&pdev
->dev
);
235 if (!axp20x_pek
->input
)
238 idev
= axp20x_pek
->input
;
240 idev
->name
= "axp20x-pek";
241 idev
->phys
= "m1kbd/input2";
242 idev
->dev
.parent
= &pdev
->dev
;
244 input_set_capability(idev
, EV_KEY
, KEY_POWER
);
246 input_set_drvdata(idev
, axp20x_pek
);
248 error
= input_register_device(idev
);
250 dev_err(&pdev
->dev
, "Can't register input device: %d\n",
259 static bool axp20x_pek_should_register_input(struct axp20x_pek
*axp20x_pek
,
260 struct platform_device
*pdev
)
262 unsigned long long hrv
= 0;
265 if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY
) &&
266 axp20x_pek
->axp20x
->variant
== AXP288_ID
) {
267 status
= acpi_evaluate_integer(ACPI_HANDLE(pdev
->dev
.parent
),
269 if (ACPI_FAILURE(status
))
270 dev_err(&pdev
->dev
, "Failed to get PMIC hardware revision\n");
273 * On Cherry Trail platforms (hrv == 3), do not register the
274 * input device if there is an "INTCFD9" or "ACPI0011" gpio
275 * button ACPI device, as that handles the power button too,
276 * and otherwise we end up reporting all presses twice.
278 if (hrv
== 3 && (acpi_dev_present("INTCFD9", NULL
, -1) ||
279 acpi_dev_present("ACPI0011", NULL
, -1)))
287 static bool axp20x_pek_should_register_input(struct axp20x_pek
*axp20x_pek
,
288 struct platform_device
*pdev
)
294 static int axp20x_pek_probe(struct platform_device
*pdev
)
296 struct axp20x_pek
*axp20x_pek
;
297 const struct platform_device_id
*match
= platform_get_device_id(pdev
);
301 dev_err(&pdev
->dev
, "Failed to get platform_device_id\n");
305 axp20x_pek
= devm_kzalloc(&pdev
->dev
, sizeof(struct axp20x_pek
),
310 axp20x_pek
->axp20x
= dev_get_drvdata(pdev
->dev
.parent
);
312 axp20x_pek
->irq_dbr
= platform_get_irq_byname(pdev
, "PEK_DBR");
313 if (axp20x_pek
->irq_dbr
< 0)
314 return axp20x_pek
->irq_dbr
;
315 axp20x_pek
->irq_dbr
= regmap_irq_get_virq(
316 axp20x_pek
->axp20x
->regmap_irqc
, axp20x_pek
->irq_dbr
);
318 axp20x_pek
->irq_dbf
= platform_get_irq_byname(pdev
, "PEK_DBF");
319 if (axp20x_pek
->irq_dbf
< 0)
320 return axp20x_pek
->irq_dbf
;
321 axp20x_pek
->irq_dbf
= regmap_irq_get_virq(
322 axp20x_pek
->axp20x
->regmap_irqc
, axp20x_pek
->irq_dbf
);
324 if (axp20x_pek_should_register_input(axp20x_pek
, pdev
)) {
325 error
= axp20x_pek_probe_input_device(axp20x_pek
, pdev
);
330 axp20x_pek
->info
= (struct axp20x_info
*)match
->driver_data
;
332 error
= devm_request_any_context_irq(&pdev
->dev
, axp20x_pek
->irq_dbr
,
334 "axp20x-pek-dbr", axp20x_pek
);
336 dev_err(&pdev
->dev
, "Failed to request dbr IRQ#%d: %d\n",
337 axp20x_pek
->irq_dbr
, error
);
341 error
= devm_request_any_context_irq(&pdev
->dev
, axp20x_pek
->irq_dbf
,
343 "axp20x-pek-dbf", axp20x_pek
);
345 dev_err(&pdev
->dev
, "Failed to request dbf IRQ#%d: %d\n",
346 axp20x_pek
->irq_dbf
, error
);
350 device_init_wakeup(&pdev
->dev
, true);
352 platform_set_drvdata(pdev
, axp20x_pek
);
357 static int __maybe_unused
axp20x_pek_suspend(struct device
*dev
)
359 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
362 * As nested threaded IRQs are not automatically disabled during
363 * suspend, we must explicitly disable non-wakeup IRQs.
365 if (device_may_wakeup(dev
)) {
366 enable_irq_wake(axp20x_pek
->irq_dbf
);
367 enable_irq_wake(axp20x_pek
->irq_dbr
);
369 disable_irq(axp20x_pek
->irq_dbf
);
370 disable_irq(axp20x_pek
->irq_dbr
);
376 static int __maybe_unused
axp20x_pek_resume(struct device
*dev
)
378 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
380 if (device_may_wakeup(dev
)) {
381 disable_irq_wake(axp20x_pek
->irq_dbf
);
382 disable_irq_wake(axp20x_pek
->irq_dbr
);
384 enable_irq(axp20x_pek
->irq_dbf
);
385 enable_irq(axp20x_pek
->irq_dbr
);
391 static int __maybe_unused
axp20x_pek_resume_noirq(struct device
*dev
)
393 struct axp20x_pek
*axp20x_pek
= dev_get_drvdata(dev
);
395 if (axp20x_pek
->axp20x
->variant
!= AXP288_ID
)
399 * Clear interrupts from button presses during suspend, to avoid
400 * a wakeup power-button press getting reported to userspace.
402 regmap_write(axp20x_pek
->axp20x
->regmap
,
403 AXP20X_IRQ1_STATE
+ AXP288_IRQ_POKN
/ 8,
404 BIT(AXP288_IRQ_POKN
% 8));
409 static const struct dev_pm_ops axp20x_pek_pm_ops
= {
410 SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend
, axp20x_pek_resume
)
411 #ifdef CONFIG_PM_SLEEP
412 .resume_noirq
= axp20x_pek_resume_noirq
,
416 static const struct platform_device_id axp_pek_id_match
[] = {
418 .name
= "axp20x-pek",
419 .driver_data
= (kernel_ulong_t
)&axp20x_info
,
422 .name
= "axp221-pek",
423 .driver_data
= (kernel_ulong_t
)&axp221_info
,
427 MODULE_DEVICE_TABLE(platform
, axp_pek_id_match
);
429 static struct platform_driver axp20x_pek_driver
= {
430 .probe
= axp20x_pek_probe
,
431 .id_table
= axp_pek_id_match
,
433 .name
= "axp20x-pek",
434 .pm
= &axp20x_pek_pm_ops
,
435 .dev_groups
= axp20x_groups
,
438 module_platform_driver(axp20x_pek_driver
);
440 MODULE_DESCRIPTION("axp20x Power Button");
441 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
442 MODULE_LICENSE("GPL");