1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) ST-Ericsson SA 2010 |
4 | * |
5 | * Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com> |
6 | * Author: Sundar Iyer <sundar.iyer@stericsson.com> |
7 | * |
8 | * TC35893 MFD Keypad Controller driver |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/input.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/input/matrix_keypad.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/mfd/tc3589x.h> |
19 | #include <linux/device.h> |
20 | |
21 | /* Maximum supported keypad matrix row/columns size */ |
22 | #define TC3589x_MAX_KPROW 8 |
23 | #define TC3589x_MAX_KPCOL 12 |
24 | |
25 | /* keypad related Constants */ |
26 | #define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF |
27 | #define DEDICATED_KEY_VAL 0xFF |
28 | |
29 | /* Pull up/down masks */ |
30 | #define TC3589x_NO_PULL_MASK 0x0 |
31 | #define TC3589x_PULL_DOWN_MASK 0x1 |
32 | #define TC3589x_PULL_UP_MASK 0x2 |
33 | #define TC3589x_PULLUP_ALL_MASK 0xAA |
34 | #define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2) |
35 | |
36 | /* Bit masks for IOCFG register */ |
37 | #define IOCFG_BALLCFG 0x01 |
38 | #define IOCFG_IG 0x08 |
39 | |
40 | #define KP_EVCODE_COL_MASK 0x0F |
41 | #define KP_EVCODE_ROW_MASK 0x70 |
42 | #define KP_RELEASE_EVT_MASK 0x80 |
43 | |
44 | #define KP_ROW_SHIFT 4 |
45 | |
46 | #define KP_NO_VALID_KEY_MASK 0x7F |
47 | |
48 | /* bit masks for RESTCTRL register */ |
49 | #define TC3589x_KBDRST 0x2 |
50 | #define TC3589x_IRQRST 0x10 |
51 | #define TC3589x_RESET_ALL 0x1B |
52 | |
53 | /* KBDMFS register bit mask */ |
54 | #define TC3589x_KBDMFS_EN 0x1 |
55 | |
56 | /* CLKEN register bitmask */ |
57 | #define KPD_CLK_EN 0x1 |
58 | |
59 | /* RSTINTCLR register bit mask */ |
60 | #define IRQ_CLEAR 0x1 |
61 | |
62 | /* bit masks for keyboard interrupts*/ |
63 | #define TC3589x_EVT_LOSS_INT 0x8 |
64 | #define TC3589x_EVT_INT 0x4 |
65 | #define TC3589x_KBD_LOSS_INT 0x2 |
66 | #define TC3589x_KBD_INT 0x1 |
67 | |
68 | /* bit masks for keyboard interrupt clear*/ |
69 | #define TC3589x_EVT_INT_CLR 0x2 |
70 | #define TC3589x_KBD_INT_CLR 0x1 |
71 | |
72 | /** |
73 | * struct tc3589x_keypad_platform_data - platform specific keypad data |
74 | * @keymap_data: matrix scan code table for keycodes |
75 | * @krow: mask for available rows, value is 0xFF |
76 | * @kcol: mask for available columns, value is 0xFF |
77 | * @debounce_period: platform specific debounce time |
78 | * @settle_time: platform specific settle down time |
79 | * @irqtype: type of interrupt, falling or rising edge |
80 | * @enable_wakeup: specifies if keypad event can wake up system from sleep |
81 | * @no_autorepeat: flag for auto repetition |
82 | */ |
83 | struct tc3589x_keypad_platform_data { |
84 | const struct matrix_keymap_data *keymap_data; |
85 | u8 krow; |
86 | u8 kcol; |
87 | u8 debounce_period; |
88 | u8 settle_time; |
89 | unsigned long irqtype; |
90 | bool enable_wakeup; |
91 | bool no_autorepeat; |
92 | }; |
93 | |
94 | /** |
95 | * struct tc_keypad - data structure used by keypad driver |
96 | * @tc3589x: pointer to tc35893 |
97 | * @input: pointer to input device object |
98 | * @board: keypad platform device |
99 | * @krow: number of rows |
100 | * @kcol: number of columns |
101 | * @keymap: matrix scan code table for keycodes |
102 | * @keypad_stopped: holds keypad status |
103 | */ |
104 | struct tc_keypad { |
105 | struct tc3589x *tc3589x; |
106 | struct input_dev *input; |
107 | const struct tc3589x_keypad_platform_data *board; |
108 | unsigned int krow; |
109 | unsigned int kcol; |
110 | unsigned short *keymap; |
111 | bool keypad_stopped; |
112 | }; |
113 | |
114 | static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) |
115 | { |
116 | int ret; |
117 | struct tc3589x *tc3589x = keypad->tc3589x; |
118 | const struct tc3589x_keypad_platform_data *board = keypad->board; |
119 | |
120 | /* validate platform configuration */ |
121 | if (board->kcol > TC3589x_MAX_KPCOL || board->krow > TC3589x_MAX_KPROW) |
122 | return -EINVAL; |
123 | |
124 | /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ |
125 | ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE, |
126 | data: (board->krow << KP_ROW_SHIFT) | board->kcol); |
127 | if (ret < 0) |
128 | return ret; |
129 | |
130 | /* configure dedicated key config, no dedicated key selected */ |
131 | ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL); |
132 | if (ret < 0) |
133 | return ret; |
134 | |
135 | ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL); |
136 | if (ret < 0) |
137 | return ret; |
138 | |
139 | /* Configure settle time */ |
140 | ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, |
141 | data: board->settle_time); |
142 | if (ret < 0) |
143 | return ret; |
144 | |
145 | /* Configure debounce time */ |
146 | ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, |
147 | data: board->debounce_period); |
148 | if (ret < 0) |
149 | return ret; |
150 | |
151 | /* Start of initialise keypad GPIOs */ |
152 | ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, mask: 0x0, IOCFG_IG); |
153 | if (ret < 0) |
154 | return ret; |
155 | |
156 | /* Configure pull-up resistors for all row GPIOs */ |
157 | ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB, |
158 | TC3589x_PULLUP_ALL_MASK); |
159 | if (ret < 0) |
160 | return ret; |
161 | |
162 | ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB, |
163 | TC3589x_PULLUP_ALL_MASK); |
164 | if (ret < 0) |
165 | return ret; |
166 | |
167 | /* Configure pull-up resistors for all column GPIOs */ |
168 | ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB, |
169 | TC3589x_PULLUP_ALL_MASK); |
170 | if (ret < 0) |
171 | return ret; |
172 | |
173 | ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB, |
174 | TC3589x_PULLUP_ALL_MASK); |
175 | if (ret < 0) |
176 | return ret; |
177 | |
178 | ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB, |
179 | TC3589x_PULLUP_ALL_MASK); |
180 | |
181 | return ret; |
182 | } |
183 | |
184 | #define TC35893_DATA_REGS 4 |
185 | #define TC35893_KEYCODE_FIFO_EMPTY 0x7f |
186 | #define TC35893_KEYCODE_FIFO_CLEAR 0xff |
187 | #define TC35893_KEYPAD_ROW_SHIFT 0x3 |
188 | |
189 | static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) |
190 | { |
191 | struct tc_keypad *keypad = dev; |
192 | struct tc3589x *tc3589x = keypad->tc3589x; |
193 | u8 i, row_index, col_index, kbd_code, up; |
194 | u8 code; |
195 | |
196 | for (i = 0; i < TC35893_DATA_REGS * 2; i++) { |
197 | kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); |
198 | |
199 | /* loop till fifo is empty and no more keys are pressed */ |
200 | if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY || |
201 | kbd_code == TC35893_KEYCODE_FIFO_CLEAR) |
202 | continue; |
203 | |
204 | /* valid key is found */ |
205 | col_index = kbd_code & KP_EVCODE_COL_MASK; |
206 | row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT; |
207 | code = MATRIX_SCAN_CODE(row_index, col_index, |
208 | TC35893_KEYPAD_ROW_SHIFT); |
209 | up = kbd_code & KP_RELEASE_EVT_MASK; |
210 | |
211 | input_event(dev: keypad->input, EV_MSC, MSC_SCAN, value: code); |
212 | input_report_key(dev: keypad->input, code: keypad->keymap[code], value: !up); |
213 | input_sync(dev: keypad->input); |
214 | } |
215 | |
216 | /* clear IRQ */ |
217 | tc3589x_set_bits(tc3589x, TC3589x_KBDIC, |
218 | mask: 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); |
219 | /* enable IRQ */ |
220 | tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, |
221 | mask: 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); |
222 | |
223 | return IRQ_HANDLED; |
224 | } |
225 | |
226 | static int tc3589x_keypad_enable(struct tc_keypad *keypad) |
227 | { |
228 | struct tc3589x *tc3589x = keypad->tc3589x; |
229 | int ret; |
230 | |
231 | /* pull the keypad module out of reset */ |
232 | ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, val: 0x0); |
233 | if (ret < 0) |
234 | return ret; |
235 | |
236 | /* configure KBDMFS */ |
237 | ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, mask: 0x0, TC3589x_KBDMFS_EN); |
238 | if (ret < 0) |
239 | return ret; |
240 | |
241 | /* enable the keypad clock */ |
242 | ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, mask: 0x0, KPD_CLK_EN); |
243 | if (ret < 0) |
244 | return ret; |
245 | |
246 | /* clear pending IRQs */ |
247 | ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, mask: 0x0, val: 0x1); |
248 | if (ret < 0) |
249 | return ret; |
250 | |
251 | /* enable the IRQs */ |
252 | ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, mask: 0x0, |
253 | TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); |
254 | if (ret < 0) |
255 | return ret; |
256 | |
257 | keypad->keypad_stopped = false; |
258 | |
259 | return ret; |
260 | } |
261 | |
262 | static int tc3589x_keypad_disable(struct tc_keypad *keypad) |
263 | { |
264 | struct tc3589x *tc3589x = keypad->tc3589x; |
265 | int ret; |
266 | |
267 | /* clear IRQ */ |
268 | ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, |
269 | mask: 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); |
270 | if (ret < 0) |
271 | return ret; |
272 | |
273 | /* disable all interrupts */ |
274 | ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, |
275 | mask: ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), val: 0x0); |
276 | if (ret < 0) |
277 | return ret; |
278 | |
279 | /* disable the keypad module */ |
280 | ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, mask: 0x1, val: 0x0); |
281 | if (ret < 0) |
282 | return ret; |
283 | |
284 | /* put the keypad module into reset */ |
285 | ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, val: 0x1); |
286 | |
287 | keypad->keypad_stopped = true; |
288 | |
289 | return ret; |
290 | } |
291 | |
292 | static int tc3589x_keypad_open(struct input_dev *input) |
293 | { |
294 | int error; |
295 | struct tc_keypad *keypad = input_get_drvdata(dev: input); |
296 | |
297 | /* enable the keypad module */ |
298 | error = tc3589x_keypad_enable(keypad); |
299 | if (error < 0) { |
300 | dev_err(&input->dev, "failed to enable keypad module\n" ); |
301 | return error; |
302 | } |
303 | |
304 | error = tc3589x_keypad_init_key_hardware(keypad); |
305 | if (error < 0) { |
306 | dev_err(&input->dev, "failed to configure keypad module\n" ); |
307 | return error; |
308 | } |
309 | |
310 | return 0; |
311 | } |
312 | |
313 | static void tc3589x_keypad_close(struct input_dev *input) |
314 | { |
315 | struct tc_keypad *keypad = input_get_drvdata(dev: input); |
316 | |
317 | /* disable the keypad module */ |
318 | tc3589x_keypad_disable(keypad); |
319 | } |
320 | |
321 | static const struct tc3589x_keypad_platform_data * |
322 | tc3589x_keypad_of_probe(struct device *dev) |
323 | { |
324 | struct device_node *np = dev->of_node; |
325 | struct tc3589x_keypad_platform_data *plat; |
326 | u32 cols, rows; |
327 | u32 debounce_ms; |
328 | int proplen; |
329 | |
330 | if (!np) |
331 | return ERR_PTR(error: -ENODEV); |
332 | |
333 | plat = devm_kzalloc(dev, size: sizeof(*plat), GFP_KERNEL); |
334 | if (!plat) |
335 | return ERR_PTR(error: -ENOMEM); |
336 | |
337 | of_property_read_u32(np, propname: "keypad,num-columns" , out_value: &cols); |
338 | of_property_read_u32(np, propname: "keypad,num-rows" , out_value: &rows); |
339 | plat->kcol = (u8) cols; |
340 | plat->krow = (u8) rows; |
341 | if (!plat->krow || !plat->kcol || |
342 | plat->krow > TC_KPD_ROWS || plat->kcol > TC_KPD_COLUMNS) { |
343 | dev_err(dev, |
344 | "keypad columns/rows not properly specified (%ux%u)\n" , |
345 | plat->kcol, plat->krow); |
346 | return ERR_PTR(error: -EINVAL); |
347 | } |
348 | |
349 | if (!of_get_property(node: np, name: "linux,keymap" , lenp: &proplen)) { |
350 | dev_err(dev, "property linux,keymap not found\n" ); |
351 | return ERR_PTR(error: -ENOENT); |
352 | } |
353 | |
354 | plat->no_autorepeat = of_property_read_bool(np, propname: "linux,no-autorepeat" ); |
355 | |
356 | plat->enable_wakeup = of_property_read_bool(np, propname: "wakeup-source" ) || |
357 | /* legacy name */ |
358 | of_property_read_bool(np, propname: "linux,wakeup" ); |
359 | |
360 | /* The custom delay format is ms/16 */ |
361 | of_property_read_u32(np, propname: "debounce-delay-ms" , out_value: &debounce_ms); |
362 | if (debounce_ms) |
363 | plat->debounce_period = debounce_ms * 16; |
364 | else |
365 | plat->debounce_period = TC_KPD_DEBOUNCE_PERIOD; |
366 | |
367 | plat->settle_time = TC_KPD_SETTLE_TIME; |
368 | /* FIXME: should be property of the IRQ resource? */ |
369 | plat->irqtype = IRQF_TRIGGER_FALLING; |
370 | |
371 | return plat; |
372 | } |
373 | |
374 | static int tc3589x_keypad_probe(struct platform_device *pdev) |
375 | { |
376 | struct tc3589x *tc3589x = dev_get_drvdata(dev: pdev->dev.parent); |
377 | struct tc_keypad *keypad; |
378 | struct input_dev *input; |
379 | const struct tc3589x_keypad_platform_data *plat; |
380 | int error, irq; |
381 | |
382 | plat = tc3589x_keypad_of_probe(dev: &pdev->dev); |
383 | if (IS_ERR(ptr: plat)) { |
384 | dev_err(&pdev->dev, "invalid keypad platform data\n" ); |
385 | return PTR_ERR(ptr: plat); |
386 | } |
387 | |
388 | irq = platform_get_irq(pdev, 0); |
389 | if (irq < 0) |
390 | return irq; |
391 | |
392 | keypad = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct tc_keypad), |
393 | GFP_KERNEL); |
394 | if (!keypad) |
395 | return -ENOMEM; |
396 | |
397 | input = devm_input_allocate_device(&pdev->dev); |
398 | if (!input) { |
399 | dev_err(&pdev->dev, "failed to allocate input device\n" ); |
400 | return -ENOMEM; |
401 | } |
402 | |
403 | keypad->board = plat; |
404 | keypad->input = input; |
405 | keypad->tc3589x = tc3589x; |
406 | |
407 | input->id.bustype = BUS_I2C; |
408 | input->name = pdev->name; |
409 | input->dev.parent = &pdev->dev; |
410 | |
411 | input->open = tc3589x_keypad_open; |
412 | input->close = tc3589x_keypad_close; |
413 | |
414 | error = matrix_keypad_build_keymap(keymap_data: plat->keymap_data, NULL, |
415 | TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL, |
416 | NULL, input_dev: input); |
417 | if (error) { |
418 | dev_err(&pdev->dev, "Failed to build keymap\n" ); |
419 | return error; |
420 | } |
421 | |
422 | keypad->keymap = input->keycode; |
423 | |
424 | input_set_capability(dev: input, EV_MSC, MSC_SCAN); |
425 | if (!plat->no_autorepeat) |
426 | __set_bit(EV_REP, input->evbit); |
427 | |
428 | input_set_drvdata(dev: input, data: keypad); |
429 | |
430 | tc3589x_keypad_disable(keypad); |
431 | |
432 | error = devm_request_threaded_irq(dev: &pdev->dev, irq, |
433 | NULL, thread_fn: tc3589x_keypad_irq, |
434 | irqflags: plat->irqtype | IRQF_ONESHOT, |
435 | devname: "tc3589x-keypad" , dev_id: keypad); |
436 | if (error) { |
437 | dev_err(&pdev->dev, |
438 | "Could not allocate irq %d,error %d\n" , |
439 | irq, error); |
440 | return error; |
441 | } |
442 | |
443 | error = input_register_device(input); |
444 | if (error) { |
445 | dev_err(&pdev->dev, "Could not register input device\n" ); |
446 | return error; |
447 | } |
448 | |
449 | /* let platform decide if keypad is a wakeup source or not */ |
450 | device_init_wakeup(dev: &pdev->dev, enable: plat->enable_wakeup); |
451 | device_set_wakeup_capable(dev: &pdev->dev, capable: plat->enable_wakeup); |
452 | |
453 | platform_set_drvdata(pdev, data: keypad); |
454 | |
455 | return 0; |
456 | } |
457 | |
458 | static int tc3589x_keypad_suspend(struct device *dev) |
459 | { |
460 | struct platform_device *pdev = to_platform_device(dev); |
461 | struct tc_keypad *keypad = platform_get_drvdata(pdev); |
462 | int irq = platform_get_irq(pdev, 0); |
463 | |
464 | /* keypad is already off; we do nothing */ |
465 | if (keypad->keypad_stopped) |
466 | return 0; |
467 | |
468 | /* if device is not a wakeup source, disable it for powersave */ |
469 | if (!device_may_wakeup(dev: &pdev->dev)) |
470 | tc3589x_keypad_disable(keypad); |
471 | else |
472 | enable_irq_wake(irq); |
473 | |
474 | return 0; |
475 | } |
476 | |
477 | static int tc3589x_keypad_resume(struct device *dev) |
478 | { |
479 | struct platform_device *pdev = to_platform_device(dev); |
480 | struct tc_keypad *keypad = platform_get_drvdata(pdev); |
481 | int irq = platform_get_irq(pdev, 0); |
482 | |
483 | if (!keypad->keypad_stopped) |
484 | return 0; |
485 | |
486 | /* enable the device to resume normal operations */ |
487 | if (!device_may_wakeup(dev: &pdev->dev)) |
488 | tc3589x_keypad_enable(keypad); |
489 | else |
490 | disable_irq_wake(irq); |
491 | |
492 | return 0; |
493 | } |
494 | |
495 | static DEFINE_SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops, |
496 | tc3589x_keypad_suspend, tc3589x_keypad_resume); |
497 | |
498 | static struct platform_driver tc3589x_keypad_driver = { |
499 | .driver = { |
500 | .name = "tc3589x-keypad" , |
501 | .pm = pm_sleep_ptr(&tc3589x_keypad_dev_pm_ops), |
502 | }, |
503 | .probe = tc3589x_keypad_probe, |
504 | }; |
505 | module_platform_driver(tc3589x_keypad_driver); |
506 | |
507 | MODULE_LICENSE("GPL v2" ); |
508 | MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer" ); |
509 | MODULE_DESCRIPTION("TC35893 Keypad Driver" ); |
510 | MODULE_ALIAS("platform:tc3589x-keypad" ); |
511 | |