1 // SPDX-License-Identifier: GPL-2.0+
3 * Onkey driver for Actions Semi ATC260x PMICs.
5 * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
8 #include <linux/bitfield.h>
9 #include <linux/input.h>
10 #include <linux/interrupt.h>
11 #include <linux/mfd/atc260x/core.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
17 /* <2s for short press, >2s for long press */
18 #define KEY_PRESS_TIME_SEC 2
20 /* Driver internals */
21 enum atc260x_onkey_reset_status
{
27 struct atc260x_onkey_params
{
40 struct atc260x_onkey
{
41 struct atc260x
*atc260x
;
42 const struct atc260x_onkey_params
*params
;
43 struct input_dev
*input_dev
;
44 struct delayed_work work
;
48 static const struct atc260x_onkey_params atc2603c_onkey_params
= {
49 .reg_int_ctl
= ATC2603C_PMU_SYS_CTL2
,
50 .long_int_pnd_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS
,
51 .short_int_pnd_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS
,
52 .kdwn_int_pnd_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD
,
53 .press_int_en_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN
,
54 .kdwn_int_en_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN
,
55 .kdwn_state_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS
,
56 .press_time_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME
,
57 .reset_en_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN
,
58 .reset_time_bm
= ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL
,
61 static const struct atc260x_onkey_params atc2609a_onkey_params
= {
62 .reg_int_ctl
= ATC2609A_PMU_SYS_CTL2
,
63 .long_int_pnd_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS
,
64 .short_int_pnd_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS
,
65 .kdwn_int_pnd_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD
,
66 .press_int_en_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN
,
67 .kdwn_int_en_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN
,
68 .kdwn_state_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS
,
69 .press_time_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME
,
70 .reset_en_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN
,
71 .reset_time_bm
= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL
,
74 static int atc2603x_onkey_hw_init(struct atc260x_onkey
*onkey
,
75 enum atc260x_onkey_reset_status reset_status
,
76 u32 reset_time
, u32 press_time
)
80 reg_bm
= onkey
->params
->long_int_pnd_bm
|
81 onkey
->params
->short_int_pnd_bm
|
82 onkey
->params
->kdwn_int_pnd_bm
|
83 onkey
->params
->press_int_en_bm
|
84 onkey
->params
->kdwn_int_en_bm
;
86 reg_val
= reg_bm
| press_time
;
87 reg_bm
|= onkey
->params
->press_time_bm
;
89 if (reset_status
== KEY_RESET_DISABLED
) {
90 reg_bm
|= onkey
->params
->reset_en_bm
;
91 } else if (reset_status
== KEY_RESET_USER_SEL
) {
92 reg_bm
|= onkey
->params
->reset_en_bm
|
93 onkey
->params
->reset_time_bm
;
94 reg_val
|= onkey
->params
->reset_en_bm
| reset_time
;
97 return regmap_update_bits(onkey
->atc260x
->regmap
,
98 onkey
->params
->reg_int_ctl
, reg_bm
, reg_val
);
101 static void atc260x_onkey_query(struct atc260x_onkey
*onkey
)
106 ret
= regmap_read(onkey
->atc260x
->regmap
,
107 onkey
->params
->reg_int_ctl
, &key_down
);
110 dev_err(onkey
->atc260x
->dev
,
111 "Failed to read onkey status: %d\n", ret
);
113 key_down
&= onkey
->params
->kdwn_state_bm
;
117 * The hardware generates interrupt only when the onkey pin is
118 * asserted. Hence, the deassertion of the pin is simulated through
122 schedule_delayed_work(&onkey
->work
, msecs_to_jiffies(200));
127 * The key-down status bit is cleared when the On/Off button
130 input_report_key(onkey
->input_dev
, KEY_POWER
, 0);
131 input_sync(onkey
->input_dev
);
133 reg_bits
= onkey
->params
->long_int_pnd_bm
|
134 onkey
->params
->short_int_pnd_bm
|
135 onkey
->params
->kdwn_int_pnd_bm
|
136 onkey
->params
->press_int_en_bm
|
137 onkey
->params
->kdwn_int_en_bm
;
139 /* Clear key press pending events and enable key press interrupts. */
140 regmap_update_bits(onkey
->atc260x
->regmap
, onkey
->params
->reg_int_ctl
,
144 static void atc260x_onkey_work(struct work_struct
*work
)
146 struct atc260x_onkey
*onkey
= container_of(work
, struct atc260x_onkey
,
148 atc260x_onkey_query(onkey
);
151 static irqreturn_t
atc260x_onkey_irq(int irq
, void *data
)
153 struct atc260x_onkey
*onkey
= data
;
156 /* Disable key press interrupts. */
157 ret
= regmap_update_bits(onkey
->atc260x
->regmap
,
158 onkey
->params
->reg_int_ctl
,
159 onkey
->params
->press_int_en_bm
|
160 onkey
->params
->kdwn_int_en_bm
, 0);
162 dev_err(onkey
->atc260x
->dev
,
163 "Failed to disable interrupts: %d\n", ret
);
165 input_report_key(onkey
->input_dev
, KEY_POWER
, 1);
166 input_sync(onkey
->input_dev
);
168 atc260x_onkey_query(onkey
);
173 static int atc260x_onkey_open(struct input_dev
*dev
)
175 struct atc260x_onkey
*onkey
= input_get_drvdata(dev
);
177 enable_irq(onkey
->irq
);
182 static void atc260x_onkey_close(struct input_dev
*dev
)
184 struct atc260x_onkey
*onkey
= input_get_drvdata(dev
);
186 disable_irq(onkey
->irq
);
187 cancel_delayed_work_sync(&onkey
->work
);
190 static int atc260x_onkey_probe(struct platform_device
*pdev
)
192 struct atc260x
*atc260x
= dev_get_drvdata(pdev
->dev
.parent
);
193 struct atc260x_onkey
*onkey
;
194 struct input_dev
*input_dev
;
195 enum atc260x_onkey_reset_status reset_status
;
196 u32 press_time
= KEY_PRESS_TIME_SEC
, reset_time
= 0;
199 onkey
= devm_kzalloc(&pdev
->dev
, sizeof(*onkey
), GFP_KERNEL
);
203 error
= device_property_read_u32(pdev
->dev
.parent
,
204 "reset-time-sec", &val
);
206 reset_status
= KEY_RESET_HW_DEFAULT
;
208 if (val
< 6 || val
> 12) {
209 dev_err(&pdev
->dev
, "reset-time-sec out of range\n");
213 reset_status
= KEY_RESET_USER_SEL
;
214 reset_time
= (val
- 6) / 2;
216 reset_status
= KEY_RESET_DISABLED
;
217 dev_dbg(&pdev
->dev
, "Disabled reset on long-press\n");
220 switch (atc260x
->ic_type
) {
222 onkey
->params
= &atc2603c_onkey_params
;
223 press_time
= FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME
,
225 reset_time
= FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL
,
229 onkey
->params
= &atc2609a_onkey_params
;
230 press_time
= FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME
,
232 reset_time
= FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL
,
237 "OnKey not supported for ATC260x PMIC type: %u\n",
242 input_dev
= devm_input_allocate_device(&pdev
->dev
);
244 dev_err(&pdev
->dev
, "Failed to allocate input device\n");
248 onkey
->input_dev
= input_dev
;
249 onkey
->atc260x
= atc260x
;
251 input_dev
->name
= "atc260x-onkey";
252 input_dev
->phys
= "atc260x-onkey/input0";
253 input_dev
->open
= atc260x_onkey_open
;
254 input_dev
->close
= atc260x_onkey_close
;
256 input_set_capability(input_dev
, EV_KEY
, KEY_POWER
);
257 input_set_drvdata(input_dev
, onkey
);
259 INIT_DELAYED_WORK(&onkey
->work
, atc260x_onkey_work
);
261 onkey
->irq
= platform_get_irq(pdev
, 0);
265 error
= devm_request_threaded_irq(&pdev
->dev
, onkey
->irq
, NULL
,
266 atc260x_onkey_irq
, IRQF_ONESHOT
,
267 dev_name(&pdev
->dev
), onkey
);
270 "Failed to register IRQ %d: %d\n", onkey
->irq
, error
);
274 /* Keep IRQ disabled until atc260x_onkey_open() is called. */
275 disable_irq(onkey
->irq
);
277 error
= input_register_device(input_dev
);
280 "Failed to register input device: %d\n", error
);
284 error
= atc2603x_onkey_hw_init(onkey
, reset_status
,
285 reset_time
, press_time
);
289 device_init_wakeup(&pdev
->dev
, true);
294 static struct platform_driver atc260x_onkey_driver
= {
295 .probe
= atc260x_onkey_probe
,
297 .name
= "atc260x-onkey",
301 module_platform_driver(atc260x_onkey_driver
);
303 MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs");
304 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
305 MODULE_LICENSE("GPL");