1*44f6b6a5SCristian Ciocaltea // SPDX-License-Identifier: GPL-2.0+
2*44f6b6a5SCristian Ciocaltea /*
3*44f6b6a5SCristian Ciocaltea  * Onkey driver for Actions Semi ATC260x PMICs.
4*44f6b6a5SCristian Ciocaltea  *
5*44f6b6a5SCristian Ciocaltea  * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
6*44f6b6a5SCristian Ciocaltea  */
7*44f6b6a5SCristian Ciocaltea 
8*44f6b6a5SCristian Ciocaltea #include <linux/bitfield.h>
9*44f6b6a5SCristian Ciocaltea #include <linux/input.h>
10*44f6b6a5SCristian Ciocaltea #include <linux/interrupt.h>
11*44f6b6a5SCristian Ciocaltea #include <linux/mfd/atc260x/core.h>
12*44f6b6a5SCristian Ciocaltea #include <linux/module.h>
13*44f6b6a5SCristian Ciocaltea #include <linux/of.h>
14*44f6b6a5SCristian Ciocaltea #include <linux/platform_device.h>
15*44f6b6a5SCristian Ciocaltea #include <linux/regmap.h>
16*44f6b6a5SCristian Ciocaltea 
17*44f6b6a5SCristian Ciocaltea /* <2s for short press, >2s for long press */
18*44f6b6a5SCristian Ciocaltea #define KEY_PRESS_TIME_SEC	2
19*44f6b6a5SCristian Ciocaltea 
20*44f6b6a5SCristian Ciocaltea /* Driver internals */
21*44f6b6a5SCristian Ciocaltea enum atc260x_onkey_reset_status {
22*44f6b6a5SCristian Ciocaltea 	KEY_RESET_HW_DEFAULT,
23*44f6b6a5SCristian Ciocaltea 	KEY_RESET_DISABLED,
24*44f6b6a5SCristian Ciocaltea 	KEY_RESET_USER_SEL,
25*44f6b6a5SCristian Ciocaltea };
26*44f6b6a5SCristian Ciocaltea 
27*44f6b6a5SCristian Ciocaltea struct atc260x_onkey_params {
28*44f6b6a5SCristian Ciocaltea 	u32 reg_int_ctl;
29*44f6b6a5SCristian Ciocaltea 	u32 kdwn_state_bm;
30*44f6b6a5SCristian Ciocaltea 	u32 long_int_pnd_bm;
31*44f6b6a5SCristian Ciocaltea 	u32 short_int_pnd_bm;
32*44f6b6a5SCristian Ciocaltea 	u32 kdwn_int_pnd_bm;
33*44f6b6a5SCristian Ciocaltea 	u32 press_int_en_bm;
34*44f6b6a5SCristian Ciocaltea 	u32 kdwn_int_en_bm;
35*44f6b6a5SCristian Ciocaltea 	u32 press_time_bm;
36*44f6b6a5SCristian Ciocaltea 	u32 reset_en_bm;
37*44f6b6a5SCristian Ciocaltea 	u32 reset_time_bm;
38*44f6b6a5SCristian Ciocaltea };
39*44f6b6a5SCristian Ciocaltea 
40*44f6b6a5SCristian Ciocaltea struct atc260x_onkey {
41*44f6b6a5SCristian Ciocaltea 	struct atc260x *atc260x;
42*44f6b6a5SCristian Ciocaltea 	const struct atc260x_onkey_params *params;
43*44f6b6a5SCristian Ciocaltea 	struct input_dev *input_dev;
44*44f6b6a5SCristian Ciocaltea 	struct delayed_work work;
45*44f6b6a5SCristian Ciocaltea 	int irq;
46*44f6b6a5SCristian Ciocaltea };
47*44f6b6a5SCristian Ciocaltea 
48*44f6b6a5SCristian Ciocaltea static const struct atc260x_onkey_params atc2603c_onkey_params = {
49*44f6b6a5SCristian Ciocaltea 	.reg_int_ctl		= ATC2603C_PMU_SYS_CTL2,
50*44f6b6a5SCristian Ciocaltea 	.long_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
51*44f6b6a5SCristian Ciocaltea 	.short_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
52*44f6b6a5SCristian Ciocaltea 	.kdwn_int_pnd_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD,
53*44f6b6a5SCristian Ciocaltea 	.press_int_en_bm	= ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN,
54*44f6b6a5SCristian Ciocaltea 	.kdwn_int_en_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
55*44f6b6a5SCristian Ciocaltea 	.kdwn_state_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS,
56*44f6b6a5SCristian Ciocaltea 	.press_time_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
57*44f6b6a5SCristian Ciocaltea 	.reset_en_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN,
58*44f6b6a5SCristian Ciocaltea 	.reset_time_bm		= ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
59*44f6b6a5SCristian Ciocaltea };
60*44f6b6a5SCristian Ciocaltea 
61*44f6b6a5SCristian Ciocaltea static const struct atc260x_onkey_params atc2609a_onkey_params = {
62*44f6b6a5SCristian Ciocaltea 	.reg_int_ctl		= ATC2609A_PMU_SYS_CTL2,
63*44f6b6a5SCristian Ciocaltea 	.long_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
64*44f6b6a5SCristian Ciocaltea 	.short_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
65*44f6b6a5SCristian Ciocaltea 	.kdwn_int_pnd_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD,
66*44f6b6a5SCristian Ciocaltea 	.press_int_en_bm	= ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN,
67*44f6b6a5SCristian Ciocaltea 	.kdwn_int_en_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
68*44f6b6a5SCristian Ciocaltea 	.kdwn_state_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS,
69*44f6b6a5SCristian Ciocaltea 	.press_time_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
70*44f6b6a5SCristian Ciocaltea 	.reset_en_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN,
71*44f6b6a5SCristian Ciocaltea 	.reset_time_bm		= ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
72*44f6b6a5SCristian Ciocaltea };
73*44f6b6a5SCristian Ciocaltea 
atc2603x_onkey_hw_init(struct atc260x_onkey * onkey,enum atc260x_onkey_reset_status reset_status,u32 reset_time,u32 press_time)74*44f6b6a5SCristian Ciocaltea static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey,
75*44f6b6a5SCristian Ciocaltea 				  enum atc260x_onkey_reset_status reset_status,
76*44f6b6a5SCristian Ciocaltea 				  u32 reset_time, u32 press_time)
77*44f6b6a5SCristian Ciocaltea {
78*44f6b6a5SCristian Ciocaltea 	u32 reg_bm, reg_val;
79*44f6b6a5SCristian Ciocaltea 
80*44f6b6a5SCristian Ciocaltea 	reg_bm = onkey->params->long_int_pnd_bm |
81*44f6b6a5SCristian Ciocaltea 		 onkey->params->short_int_pnd_bm |
82*44f6b6a5SCristian Ciocaltea 		 onkey->params->kdwn_int_pnd_bm |
83*44f6b6a5SCristian Ciocaltea 		 onkey->params->press_int_en_bm |
84*44f6b6a5SCristian Ciocaltea 		 onkey->params->kdwn_int_en_bm;
85*44f6b6a5SCristian Ciocaltea 
86*44f6b6a5SCristian Ciocaltea 	reg_val = reg_bm | press_time;
87*44f6b6a5SCristian Ciocaltea 	reg_bm |= onkey->params->press_time_bm;
88*44f6b6a5SCristian Ciocaltea 
89*44f6b6a5SCristian Ciocaltea 	if (reset_status == KEY_RESET_DISABLED) {
90*44f6b6a5SCristian Ciocaltea 		reg_bm |= onkey->params->reset_en_bm;
91*44f6b6a5SCristian Ciocaltea 	} else if (reset_status == KEY_RESET_USER_SEL) {
92*44f6b6a5SCristian Ciocaltea 		reg_bm |= onkey->params->reset_en_bm |
93*44f6b6a5SCristian Ciocaltea 			  onkey->params->reset_time_bm;
94*44f6b6a5SCristian Ciocaltea 		reg_val |= onkey->params->reset_en_bm | reset_time;
95*44f6b6a5SCristian Ciocaltea 	}
96*44f6b6a5SCristian Ciocaltea 
97*44f6b6a5SCristian Ciocaltea 	return regmap_update_bits(onkey->atc260x->regmap,
98*44f6b6a5SCristian Ciocaltea 				  onkey->params->reg_int_ctl, reg_bm, reg_val);
99*44f6b6a5SCristian Ciocaltea }
100*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_query(struct atc260x_onkey * onkey)101*44f6b6a5SCristian Ciocaltea static void atc260x_onkey_query(struct atc260x_onkey *onkey)
102*44f6b6a5SCristian Ciocaltea {
103*44f6b6a5SCristian Ciocaltea 	u32 reg_bits;
104*44f6b6a5SCristian Ciocaltea 	int ret, key_down;
105*44f6b6a5SCristian Ciocaltea 
106*44f6b6a5SCristian Ciocaltea 	ret = regmap_read(onkey->atc260x->regmap,
107*44f6b6a5SCristian Ciocaltea 			  onkey->params->reg_int_ctl, &key_down);
108*44f6b6a5SCristian Ciocaltea 	if (ret) {
109*44f6b6a5SCristian Ciocaltea 		key_down = 1;
110*44f6b6a5SCristian Ciocaltea 		dev_err(onkey->atc260x->dev,
111*44f6b6a5SCristian Ciocaltea 			"Failed to read onkey status: %d\n", ret);
112*44f6b6a5SCristian Ciocaltea 	} else {
113*44f6b6a5SCristian Ciocaltea 		key_down &= onkey->params->kdwn_state_bm;
114*44f6b6a5SCristian Ciocaltea 	}
115*44f6b6a5SCristian Ciocaltea 
116*44f6b6a5SCristian Ciocaltea 	/*
117*44f6b6a5SCristian Ciocaltea 	 * The hardware generates interrupt only when the onkey pin is
118*44f6b6a5SCristian Ciocaltea 	 * asserted. Hence, the deassertion of the pin is simulated through
119*44f6b6a5SCristian Ciocaltea 	 * work queue.
120*44f6b6a5SCristian Ciocaltea 	 */
121*44f6b6a5SCristian Ciocaltea 	if (key_down) {
122*44f6b6a5SCristian Ciocaltea 		schedule_delayed_work(&onkey->work, msecs_to_jiffies(200));
123*44f6b6a5SCristian Ciocaltea 		return;
124*44f6b6a5SCristian Ciocaltea 	}
125*44f6b6a5SCristian Ciocaltea 
126*44f6b6a5SCristian Ciocaltea 	/*
127*44f6b6a5SCristian Ciocaltea 	 * The key-down status bit is cleared when the On/Off button
128*44f6b6a5SCristian Ciocaltea 	 * is released.
129*44f6b6a5SCristian Ciocaltea 	 */
130*44f6b6a5SCristian Ciocaltea 	input_report_key(onkey->input_dev, KEY_POWER, 0);
131*44f6b6a5SCristian Ciocaltea 	input_sync(onkey->input_dev);
132*44f6b6a5SCristian Ciocaltea 
133*44f6b6a5SCristian Ciocaltea 	reg_bits = onkey->params->long_int_pnd_bm |
134*44f6b6a5SCristian Ciocaltea 		   onkey->params->short_int_pnd_bm |
135*44f6b6a5SCristian Ciocaltea 		   onkey->params->kdwn_int_pnd_bm |
136*44f6b6a5SCristian Ciocaltea 		   onkey->params->press_int_en_bm |
137*44f6b6a5SCristian Ciocaltea 		   onkey->params->kdwn_int_en_bm;
138*44f6b6a5SCristian Ciocaltea 
139*44f6b6a5SCristian Ciocaltea 	/* Clear key press pending events and enable key press interrupts. */
140*44f6b6a5SCristian Ciocaltea 	regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl,
141*44f6b6a5SCristian Ciocaltea 			   reg_bits, reg_bits);
142*44f6b6a5SCristian Ciocaltea }
143*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_work(struct work_struct * work)144*44f6b6a5SCristian Ciocaltea static void atc260x_onkey_work(struct work_struct *work)
145*44f6b6a5SCristian Ciocaltea {
146*44f6b6a5SCristian Ciocaltea 	struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey,
147*44f6b6a5SCristian Ciocaltea 						   work.work);
148*44f6b6a5SCristian Ciocaltea 	atc260x_onkey_query(onkey);
149*44f6b6a5SCristian Ciocaltea }
150*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_irq(int irq,void * data)151*44f6b6a5SCristian Ciocaltea static irqreturn_t atc260x_onkey_irq(int irq, void *data)
152*44f6b6a5SCristian Ciocaltea {
153*44f6b6a5SCristian Ciocaltea 	struct atc260x_onkey *onkey = data;
154*44f6b6a5SCristian Ciocaltea 	int ret;
155*44f6b6a5SCristian Ciocaltea 
156*44f6b6a5SCristian Ciocaltea 	/* Disable key press interrupts. */
157*44f6b6a5SCristian Ciocaltea 	ret = regmap_update_bits(onkey->atc260x->regmap,
158*44f6b6a5SCristian Ciocaltea 				 onkey->params->reg_int_ctl,
159*44f6b6a5SCristian Ciocaltea 				 onkey->params->press_int_en_bm |
160*44f6b6a5SCristian Ciocaltea 				 onkey->params->kdwn_int_en_bm, 0);
161*44f6b6a5SCristian Ciocaltea 	if (ret)
162*44f6b6a5SCristian Ciocaltea 		dev_err(onkey->atc260x->dev,
163*44f6b6a5SCristian Ciocaltea 			"Failed to disable interrupts: %d\n", ret);
164*44f6b6a5SCristian Ciocaltea 
165*44f6b6a5SCristian Ciocaltea 	input_report_key(onkey->input_dev, KEY_POWER, 1);
166*44f6b6a5SCristian Ciocaltea 	input_sync(onkey->input_dev);
167*44f6b6a5SCristian Ciocaltea 
168*44f6b6a5SCristian Ciocaltea 	atc260x_onkey_query(onkey);
169*44f6b6a5SCristian Ciocaltea 
170*44f6b6a5SCristian Ciocaltea 	return IRQ_HANDLED;
171*44f6b6a5SCristian Ciocaltea }
172*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_open(struct input_dev * dev)173*44f6b6a5SCristian Ciocaltea static int atc260x_onkey_open(struct input_dev *dev)
174*44f6b6a5SCristian Ciocaltea {
175*44f6b6a5SCristian Ciocaltea 	struct atc260x_onkey *onkey = input_get_drvdata(dev);
176*44f6b6a5SCristian Ciocaltea 
177*44f6b6a5SCristian Ciocaltea 	enable_irq(onkey->irq);
178*44f6b6a5SCristian Ciocaltea 
179*44f6b6a5SCristian Ciocaltea 	return 0;
180*44f6b6a5SCristian Ciocaltea }
181*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_close(struct input_dev * dev)182*44f6b6a5SCristian Ciocaltea static void atc260x_onkey_close(struct input_dev *dev)
183*44f6b6a5SCristian Ciocaltea {
184*44f6b6a5SCristian Ciocaltea 	struct atc260x_onkey *onkey = input_get_drvdata(dev);
185*44f6b6a5SCristian Ciocaltea 
186*44f6b6a5SCristian Ciocaltea 	disable_irq(onkey->irq);
187*44f6b6a5SCristian Ciocaltea 	cancel_delayed_work_sync(&onkey->work);
188*44f6b6a5SCristian Ciocaltea }
189*44f6b6a5SCristian Ciocaltea 
atc260x_onkey_probe(struct platform_device * pdev)190*44f6b6a5SCristian Ciocaltea static int atc260x_onkey_probe(struct platform_device *pdev)
191*44f6b6a5SCristian Ciocaltea {
192*44f6b6a5SCristian Ciocaltea 	struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
193*44f6b6a5SCristian Ciocaltea 	struct atc260x_onkey *onkey;
194*44f6b6a5SCristian Ciocaltea 	struct input_dev *input_dev;
195*44f6b6a5SCristian Ciocaltea 	enum atc260x_onkey_reset_status reset_status;
196*44f6b6a5SCristian Ciocaltea 	u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0;
197*44f6b6a5SCristian Ciocaltea 	int val, error;
198*44f6b6a5SCristian Ciocaltea 
199*44f6b6a5SCristian Ciocaltea 	onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
200*44f6b6a5SCristian Ciocaltea 	if (!onkey)
201*44f6b6a5SCristian Ciocaltea 		return -ENOMEM;
202*44f6b6a5SCristian Ciocaltea 
203*44f6b6a5SCristian Ciocaltea 	error = device_property_read_u32(pdev->dev.parent,
204*44f6b6a5SCristian Ciocaltea 					 "reset-time-sec", &val);
205*44f6b6a5SCristian Ciocaltea 	if (error) {
206*44f6b6a5SCristian Ciocaltea 		reset_status = KEY_RESET_HW_DEFAULT;
207*44f6b6a5SCristian Ciocaltea 	} else if (val) {
208*44f6b6a5SCristian Ciocaltea 		if (val < 6 || val > 12) {
209*44f6b6a5SCristian Ciocaltea 			dev_err(&pdev->dev, "reset-time-sec out of range\n");
210*44f6b6a5SCristian Ciocaltea 			return -EINVAL;
211*44f6b6a5SCristian Ciocaltea 		}
212*44f6b6a5SCristian Ciocaltea 
213*44f6b6a5SCristian Ciocaltea 		reset_status = KEY_RESET_USER_SEL;
214*44f6b6a5SCristian Ciocaltea 		reset_time = (val - 6) / 2;
215*44f6b6a5SCristian Ciocaltea 	} else {
216*44f6b6a5SCristian Ciocaltea 		reset_status = KEY_RESET_DISABLED;
217*44f6b6a5SCristian Ciocaltea 		dev_dbg(&pdev->dev, "Disabled reset on long-press\n");
218*44f6b6a5SCristian Ciocaltea 	}
219*44f6b6a5SCristian Ciocaltea 
220*44f6b6a5SCristian Ciocaltea 	switch (atc260x->ic_type) {
221*44f6b6a5SCristian Ciocaltea 	case ATC2603C:
222*44f6b6a5SCristian Ciocaltea 		onkey->params = &atc2603c_onkey_params;
223*44f6b6a5SCristian Ciocaltea 		press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
224*44f6b6a5SCristian Ciocaltea 					press_time);
225*44f6b6a5SCristian Ciocaltea 		reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
226*44f6b6a5SCristian Ciocaltea 					reset_time);
227*44f6b6a5SCristian Ciocaltea 		break;
228*44f6b6a5SCristian Ciocaltea 	case ATC2609A:
229*44f6b6a5SCristian Ciocaltea 		onkey->params = &atc2609a_onkey_params;
230*44f6b6a5SCristian Ciocaltea 		press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
231*44f6b6a5SCristian Ciocaltea 					press_time);
232*44f6b6a5SCristian Ciocaltea 		reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
233*44f6b6a5SCristian Ciocaltea 					reset_time);
234*44f6b6a5SCristian Ciocaltea 		break;
235*44f6b6a5SCristian Ciocaltea 	default:
236*44f6b6a5SCristian Ciocaltea 		dev_err(&pdev->dev,
237*44f6b6a5SCristian Ciocaltea 			"OnKey not supported for ATC260x PMIC type: %u\n",
238*44f6b6a5SCristian Ciocaltea 			atc260x->ic_type);
239*44f6b6a5SCristian Ciocaltea 		return -EINVAL;
240*44f6b6a5SCristian Ciocaltea 	}
241*44f6b6a5SCristian Ciocaltea 
242*44f6b6a5SCristian Ciocaltea 	input_dev = devm_input_allocate_device(&pdev->dev);
243*44f6b6a5SCristian Ciocaltea 	if (!input_dev) {
244*44f6b6a5SCristian Ciocaltea 		dev_err(&pdev->dev, "Failed to allocate input device\n");
245*44f6b6a5SCristian Ciocaltea 		return -ENOMEM;
246*44f6b6a5SCristian Ciocaltea 	}
247*44f6b6a5SCristian Ciocaltea 
248*44f6b6a5SCristian Ciocaltea 	onkey->input_dev = input_dev;
249*44f6b6a5SCristian Ciocaltea 	onkey->atc260x = atc260x;
250*44f6b6a5SCristian Ciocaltea 
251*44f6b6a5SCristian Ciocaltea 	input_dev->name = "atc260x-onkey";
252*44f6b6a5SCristian Ciocaltea 	input_dev->phys = "atc260x-onkey/input0";
253*44f6b6a5SCristian Ciocaltea 	input_dev->open = atc260x_onkey_open;
254*44f6b6a5SCristian Ciocaltea 	input_dev->close = atc260x_onkey_close;
255*44f6b6a5SCristian Ciocaltea 
256*44f6b6a5SCristian Ciocaltea 	input_set_capability(input_dev, EV_KEY, KEY_POWER);
257*44f6b6a5SCristian Ciocaltea 	input_set_drvdata(input_dev, onkey);
258*44f6b6a5SCristian Ciocaltea 
259*44f6b6a5SCristian Ciocaltea 	INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work);
260*44f6b6a5SCristian Ciocaltea 
261*44f6b6a5SCristian Ciocaltea 	onkey->irq = platform_get_irq(pdev, 0);
262*44f6b6a5SCristian Ciocaltea 	if (onkey->irq < 0)
263*44f6b6a5SCristian Ciocaltea 		return onkey->irq;
264*44f6b6a5SCristian Ciocaltea 
265*44f6b6a5SCristian Ciocaltea 	error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL,
266*44f6b6a5SCristian Ciocaltea 					  atc260x_onkey_irq, IRQF_ONESHOT,
267*44f6b6a5SCristian Ciocaltea 					  dev_name(&pdev->dev), onkey);
268*44f6b6a5SCristian Ciocaltea 	if (error) {
269*44f6b6a5SCristian Ciocaltea 		dev_err(&pdev->dev,
270*44f6b6a5SCristian Ciocaltea 			"Failed to register IRQ %d: %d\n", onkey->irq, error);
271*44f6b6a5SCristian Ciocaltea 		return error;
272*44f6b6a5SCristian Ciocaltea 	}
273*44f6b6a5SCristian Ciocaltea 
274*44f6b6a5SCristian Ciocaltea 	/* Keep IRQ disabled until atc260x_onkey_open() is called. */
275*44f6b6a5SCristian Ciocaltea 	disable_irq(onkey->irq);
276*44f6b6a5SCristian Ciocaltea 
277*44f6b6a5SCristian Ciocaltea 	error = input_register_device(input_dev);
278*44f6b6a5SCristian Ciocaltea 	if (error) {
279*44f6b6a5SCristian Ciocaltea 		dev_err(&pdev->dev,
280*44f6b6a5SCristian Ciocaltea 			"Failed to register input device: %d\n", error);
281*44f6b6a5SCristian Ciocaltea 		return error;
282*44f6b6a5SCristian Ciocaltea 	}
283*44f6b6a5SCristian Ciocaltea 
284*44f6b6a5SCristian Ciocaltea 	error = atc2603x_onkey_hw_init(onkey, reset_status,
285*44f6b6a5SCristian Ciocaltea 				       reset_time, press_time);
286*44f6b6a5SCristian Ciocaltea 	if (error)
287*44f6b6a5SCristian Ciocaltea 		return error;
288*44f6b6a5SCristian Ciocaltea 
289*44f6b6a5SCristian Ciocaltea 	device_init_wakeup(&pdev->dev, true);
290*44f6b6a5SCristian Ciocaltea 
291*44f6b6a5SCristian Ciocaltea 	return 0;
292*44f6b6a5SCristian Ciocaltea }
293*44f6b6a5SCristian Ciocaltea 
294*44f6b6a5SCristian Ciocaltea static struct platform_driver atc260x_onkey_driver = {
295*44f6b6a5SCristian Ciocaltea 	.probe	= atc260x_onkey_probe,
296*44f6b6a5SCristian Ciocaltea 	.driver	= {
297*44f6b6a5SCristian Ciocaltea 		.name = "atc260x-onkey",
298*44f6b6a5SCristian Ciocaltea 	},
299*44f6b6a5SCristian Ciocaltea };
300*44f6b6a5SCristian Ciocaltea 
301*44f6b6a5SCristian Ciocaltea module_platform_driver(atc260x_onkey_driver);
302*44f6b6a5SCristian Ciocaltea 
303*44f6b6a5SCristian Ciocaltea MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs");
304*44f6b6a5SCristian Ciocaltea MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
305*44f6b6a5SCristian Ciocaltea MODULE_LICENSE("GPL");
306