diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 594 |
3 files changed, 604 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c72283c6916f..616a3916d187 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -292,6 +292,15 @@ config KEYBOARD_MAX7359 | |||
292 | To compile this driver as a module, choose M here: the | 292 | To compile this driver as a module, choose M here: the |
293 | module will be called max7359_keypad. | 293 | module will be called max7359_keypad. |
294 | 294 | ||
295 | config KEYBOARD_IMX | ||
296 | tristate "IMX keypad support" | ||
297 | depends on ARCH_MXC | ||
298 | help | ||
299 | Enable support for IMX keypad port. | ||
300 | |||
301 | To compile this driver as a module, choose M here: the | ||
302 | module will be called imx_keypad. | ||
303 | |||
295 | config KEYBOARD_NEWTON | 304 | config KEYBOARD_NEWTON |
296 | tristate "Newton keyboard" | 305 | tristate "Newton keyboard" |
297 | select SERIO | 306 | select SERIO |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 78654ef65206..706c6b5ed5f4 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o | |||
17 | obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o | 17 | obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o |
18 | obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o | 18 | obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o |
19 | obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o | 19 | obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o |
20 | obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o | ||
20 | obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o | 21 | obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o |
21 | obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o | 22 | obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o |
22 | obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o | 23 | obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o |
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c new file mode 100644 index 000000000000..2ee5b798024d --- /dev/null +++ b/drivers/input/keyboard/imx_keypad.c | |||
@@ -0,0 +1,594 @@ | |||
1 | /* | ||
2 | * Driver for the IMX keypad port. | ||
3 | * Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * <<Power management needs to be implemented>>. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/input/matrix_keypad.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/jiffies.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/timer.h> | ||
25 | |||
26 | /* | ||
27 | * Keypad Controller registers (halfword) | ||
28 | */ | ||
29 | #define KPCR 0x00 /* Keypad Control Register */ | ||
30 | |||
31 | #define KPSR 0x02 /* Keypad Status Register */ | ||
32 | #define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */ | ||
33 | #define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */ | ||
34 | #define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/ | ||
35 | #define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/ | ||
36 | #define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */ | ||
37 | #define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */ | ||
38 | #define KBD_STAT_KPPEN (0x1 << 10) /* Keypad Clock Enable */ | ||
39 | |||
40 | #define KDDR 0x04 /* Keypad Data Direction Register */ | ||
41 | #define KPDR 0x06 /* Keypad Data Register */ | ||
42 | |||
43 | #define MAX_MATRIX_KEY_ROWS 8 | ||
44 | #define MAX_MATRIX_KEY_COLS 8 | ||
45 | #define MATRIX_ROW_SHIFT 3 | ||
46 | |||
47 | #define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) | ||
48 | |||
49 | struct imx_keypad { | ||
50 | |||
51 | struct clk *clk; | ||
52 | struct input_dev *input_dev; | ||
53 | void __iomem *mmio_base; | ||
54 | |||
55 | int irq; | ||
56 | struct timer_list check_matrix_timer; | ||
57 | |||
58 | /* | ||
59 | * The matrix is stable only if no changes are detected after | ||
60 | * IMX_KEYPAD_SCANS_FOR_STABILITY scans | ||
61 | */ | ||
62 | #define IMX_KEYPAD_SCANS_FOR_STABILITY 3 | ||
63 | int stable_count; | ||
64 | |||
65 | bool enabled; | ||
66 | |||
67 | /* Masks for enabled rows/cols */ | ||
68 | unsigned short rows_en_mask; | ||
69 | unsigned short cols_en_mask; | ||
70 | |||
71 | unsigned short keycodes[MAX_MATRIX_KEY_NUM]; | ||
72 | |||
73 | /* | ||
74 | * Matrix states: | ||
75 | * -stable: achieved after a complete debounce process. | ||
76 | * -unstable: used in the debouncing process. | ||
77 | */ | ||
78 | unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS]; | ||
79 | unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS]; | ||
80 | }; | ||
81 | |||
82 | /* Scan the matrix and return the new state in *matrix_volatile_state. */ | ||
83 | static void imx_keypad_scan_matrix(struct imx_keypad *keypad, | ||
84 | unsigned short *matrix_volatile_state) | ||
85 | { | ||
86 | int col; | ||
87 | unsigned short reg_val; | ||
88 | |||
89 | for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { | ||
90 | if ((keypad->cols_en_mask & (1 << col)) == 0) | ||
91 | continue; | ||
92 | /* | ||
93 | * Discharge keypad capacitance: | ||
94 | * 2. write 1s on column data. | ||
95 | * 3. configure columns as totem-pole to discharge capacitance. | ||
96 | * 4. configure columns as open-drain. | ||
97 | */ | ||
98 | reg_val = readw(keypad->mmio_base + KPDR); | ||
99 | reg_val |= 0xff00; | ||
100 | writew(reg_val, keypad->mmio_base + KPDR); | ||
101 | |||
102 | reg_val = readw(keypad->mmio_base + KPCR); | ||
103 | reg_val &= ~((keypad->cols_en_mask & 0xff) << 8); | ||
104 | writew(reg_val, keypad->mmio_base + KPCR); | ||
105 | |||
106 | udelay(2); | ||
107 | |||
108 | reg_val = readw(keypad->mmio_base + KPCR); | ||
109 | reg_val |= (keypad->cols_en_mask & 0xff) << 8; | ||
110 | writew(reg_val, keypad->mmio_base + KPCR); | ||
111 | |||
112 | /* | ||
113 | * 5. Write a single column to 0, others to 1. | ||
114 | * 6. Sample row inputs and save data. | ||
115 | * 7. Repeat steps 2 - 6 for remaining columns. | ||
116 | */ | ||
117 | reg_val = readw(keypad->mmio_base + KPDR); | ||
118 | reg_val &= ~(1 << (8 + col)); | ||
119 | writew(reg_val, keypad->mmio_base + KPDR); | ||
120 | |||
121 | /* | ||
122 | * Delay added to avoid propagating the 0 from column to row | ||
123 | * when scanning. | ||
124 | */ | ||
125 | udelay(5); | ||
126 | |||
127 | /* | ||
128 | * 1s in matrix_volatile_state[col] means key pressures | ||
129 | * throw data from non enabled rows. | ||
130 | */ | ||
131 | reg_val = readw(keypad->mmio_base + KPDR); | ||
132 | matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Return in standby mode: | ||
137 | * 9. write 0s to columns | ||
138 | */ | ||
139 | reg_val = readw(keypad->mmio_base + KPDR); | ||
140 | reg_val &= 0x00ff; | ||
141 | writew(reg_val, keypad->mmio_base + KPDR); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Compare the new matrix state (volatile) with the stable one stored in | ||
146 | * keypad->matrix_stable_state and fire events if changes are detected. | ||
147 | */ | ||
148 | static void imx_keypad_fire_events(struct imx_keypad *keypad, | ||
149 | unsigned short *matrix_volatile_state) | ||
150 | { | ||
151 | struct input_dev *input_dev = keypad->input_dev; | ||
152 | int row, col; | ||
153 | |||
154 | for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { | ||
155 | unsigned short bits_changed; | ||
156 | int code; | ||
157 | |||
158 | if ((keypad->cols_en_mask & (1 << col)) == 0) | ||
159 | continue; /* Column is not enabled */ | ||
160 | |||
161 | bits_changed = keypad->matrix_stable_state[col] ^ | ||
162 | matrix_volatile_state[col]; | ||
163 | |||
164 | if (bits_changed == 0) | ||
165 | continue; /* Column does not contain changes */ | ||
166 | |||
167 | for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) { | ||
168 | if ((keypad->rows_en_mask & (1 << row)) == 0) | ||
169 | continue; /* Row is not enabled */ | ||
170 | if ((bits_changed & (1 << row)) == 0) | ||
171 | continue; /* Row does not contain changes */ | ||
172 | |||
173 | code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); | ||
174 | input_event(input_dev, EV_MSC, MSC_SCAN, code); | ||
175 | input_report_key(input_dev, keypad->keycodes[code], | ||
176 | matrix_volatile_state[col] & (1 << row)); | ||
177 | dev_dbg(&input_dev->dev, "Event code: %d, val: %d", | ||
178 | keypad->keycodes[code], | ||
179 | matrix_volatile_state[col] & (1 << row)); | ||
180 | } | ||
181 | } | ||
182 | input_sync(input_dev); | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * imx_keypad_check_for_events is the timer handler. | ||
187 | */ | ||
188 | static void imx_keypad_check_for_events(unsigned long data) | ||
189 | { | ||
190 | struct imx_keypad *keypad = (struct imx_keypad *) data; | ||
191 | unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS]; | ||
192 | unsigned short reg_val; | ||
193 | bool state_changed, is_zero_matrix; | ||
194 | int i; | ||
195 | |||
196 | memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state)); | ||
197 | |||
198 | imx_keypad_scan_matrix(keypad, matrix_volatile_state); | ||
199 | |||
200 | state_changed = false; | ||
201 | for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) { | ||
202 | if ((keypad->cols_en_mask & (1 << i)) == 0) | ||
203 | continue; | ||
204 | |||
205 | if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) { | ||
206 | state_changed = true; | ||
207 | break; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * If the matrix state is changed from the previous scan | ||
213 | * (Re)Begin the debouncing process, saving the new state in | ||
214 | * keypad->matrix_unstable_state. | ||
215 | * else | ||
216 | * Increase the count of number of scans with a stable state. | ||
217 | */ | ||
218 | if (state_changed) { | ||
219 | memcpy(keypad->matrix_unstable_state, matrix_volatile_state, | ||
220 | sizeof(matrix_volatile_state)); | ||
221 | keypad->stable_count = 0; | ||
222 | } else | ||
223 | keypad->stable_count++; | ||
224 | |||
225 | /* | ||
226 | * If the matrix is not as stable as we want reschedule scan | ||
227 | * in the near future. | ||
228 | */ | ||
229 | if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) { | ||
230 | mod_timer(&keypad->check_matrix_timer, | ||
231 | jiffies + msecs_to_jiffies(10)); | ||
232 | return; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * If the matrix state is stable, fire the events and save the new | ||
237 | * stable state. Note, if the matrix is kept stable for longer | ||
238 | * (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all | ||
239 | * events have already been generated. | ||
240 | */ | ||
241 | if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) { | ||
242 | imx_keypad_fire_events(keypad, matrix_volatile_state); | ||
243 | |||
244 | memcpy(keypad->matrix_stable_state, matrix_volatile_state, | ||
245 | sizeof(matrix_volatile_state)); | ||
246 | } | ||
247 | |||
248 | is_zero_matrix = true; | ||
249 | for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) { | ||
250 | if (matrix_volatile_state[i] != 0) { | ||
251 | is_zero_matrix = false; | ||
252 | break; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | |||
257 | if (is_zero_matrix) { | ||
258 | /* | ||
259 | * All keys have been released. Enable only the KDI | ||
260 | * interrupt for future key presses (clear the KDI | ||
261 | * status bit and its sync chain before that). | ||
262 | */ | ||
263 | reg_val = readw(keypad->mmio_base + KPSR); | ||
264 | reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC; | ||
265 | writew(reg_val, keypad->mmio_base + KPSR); | ||
266 | |||
267 | reg_val = readw(keypad->mmio_base + KPSR); | ||
268 | reg_val |= KBD_STAT_KDIE; | ||
269 | reg_val &= ~KBD_STAT_KRIE; | ||
270 | writew(reg_val, keypad->mmio_base + KPSR); | ||
271 | } else { | ||
272 | /* | ||
273 | * Some keys are still pressed. Schedule a rescan in | ||
274 | * attempt to detect multiple key presses and enable | ||
275 | * the KRI interrupt to react quickly to key release | ||
276 | * event. | ||
277 | */ | ||
278 | mod_timer(&keypad->check_matrix_timer, | ||
279 | jiffies + msecs_to_jiffies(60)); | ||
280 | |||
281 | reg_val = readw(keypad->mmio_base + KPSR); | ||
282 | reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS; | ||
283 | writew(reg_val, keypad->mmio_base + KPSR); | ||
284 | |||
285 | reg_val = readw(keypad->mmio_base + KPSR); | ||
286 | reg_val |= KBD_STAT_KRIE; | ||
287 | reg_val &= ~KBD_STAT_KDIE; | ||
288 | writew(reg_val, keypad->mmio_base + KPSR); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id) | ||
293 | { | ||
294 | struct imx_keypad *keypad = dev_id; | ||
295 | unsigned short reg_val; | ||
296 | |||
297 | reg_val = readw(keypad->mmio_base + KPSR); | ||
298 | |||
299 | /* Disable both interrupt types */ | ||
300 | reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); | ||
301 | /* Clear interrupts status bits */ | ||
302 | reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; | ||
303 | writew(reg_val, keypad->mmio_base + KPSR); | ||
304 | |||
305 | if (keypad->enabled) { | ||
306 | /* The matrix is supposed to be changed */ | ||
307 | keypad->stable_count = 0; | ||
308 | |||
309 | /* Schedule the scanning procedure near in the future */ | ||
310 | mod_timer(&keypad->check_matrix_timer, | ||
311 | jiffies + msecs_to_jiffies(2)); | ||
312 | } | ||
313 | |||
314 | return IRQ_HANDLED; | ||
315 | } | ||
316 | |||
317 | static void imx_keypad_config(struct imx_keypad *keypad) | ||
318 | { | ||
319 | unsigned short reg_val; | ||
320 | |||
321 | /* | ||
322 | * Include enabled rows in interrupt generation (KPCR[7:0]) | ||
323 | * Configure keypad columns as open-drain (KPCR[15:8]) | ||
324 | */ | ||
325 | reg_val = readw(keypad->mmio_base + KPCR); | ||
326 | reg_val |= keypad->rows_en_mask & 0xff; /* rows */ | ||
327 | reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */ | ||
328 | writew(reg_val, keypad->mmio_base + KPCR); | ||
329 | |||
330 | /* Write 0's to KPDR[15:8] (Colums) */ | ||
331 | reg_val = readw(keypad->mmio_base + KPDR); | ||
332 | reg_val &= 0x00ff; | ||
333 | writew(reg_val, keypad->mmio_base + KPDR); | ||
334 | |||
335 | /* Configure columns as output, rows as input (KDDR[15:0]) */ | ||
336 | writew(0xff00, keypad->mmio_base + KDDR); | ||
337 | |||
338 | /* | ||
339 | * Clear Key Depress and Key Release status bit. | ||
340 | * Clear both synchronizer chain. | ||
341 | */ | ||
342 | reg_val = readw(keypad->mmio_base + KPSR); | ||
343 | reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD | | ||
344 | KBD_STAT_KDSC | KBD_STAT_KRSS; | ||
345 | writew(reg_val, keypad->mmio_base + KPSR); | ||
346 | |||
347 | /* Enable KDI and disable KRI (avoid false release events). */ | ||
348 | reg_val |= KBD_STAT_KDIE; | ||
349 | reg_val &= ~KBD_STAT_KRIE; | ||
350 | writew(reg_val, keypad->mmio_base + KPSR); | ||
351 | } | ||
352 | |||
353 | static void imx_keypad_inhibit(struct imx_keypad *keypad) | ||
354 | { | ||
355 | unsigned short reg_val; | ||
356 | |||
357 | /* Inhibit KDI and KRI interrupts. */ | ||
358 | reg_val = readw(keypad->mmio_base + KPSR); | ||
359 | reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); | ||
360 | writew(reg_val, keypad->mmio_base + KPSR); | ||
361 | |||
362 | /* Colums as open drain and disable all rows */ | ||
363 | writew(0xff00, keypad->mmio_base + KPCR); | ||
364 | } | ||
365 | |||
366 | static void imx_keypad_close(struct input_dev *dev) | ||
367 | { | ||
368 | struct imx_keypad *keypad = input_get_drvdata(dev); | ||
369 | |||
370 | dev_dbg(&dev->dev, ">%s\n", __func__); | ||
371 | |||
372 | /* Mark keypad as being inactive */ | ||
373 | keypad->enabled = false; | ||
374 | synchronize_irq(keypad->irq); | ||
375 | del_timer_sync(&keypad->check_matrix_timer); | ||
376 | |||
377 | imx_keypad_inhibit(keypad); | ||
378 | |||
379 | /* Disable clock unit */ | ||
380 | clk_disable(keypad->clk); | ||
381 | } | ||
382 | |||
383 | static int imx_keypad_open(struct input_dev *dev) | ||
384 | { | ||
385 | struct imx_keypad *keypad = input_get_drvdata(dev); | ||
386 | |||
387 | dev_dbg(&dev->dev, ">%s\n", __func__); | ||
388 | |||
389 | /* We became active from now */ | ||
390 | keypad->enabled = true; | ||
391 | |||
392 | /* Enable the kpp clock */ | ||
393 | clk_enable(keypad->clk); | ||
394 | imx_keypad_config(keypad); | ||
395 | |||
396 | /* Sanity control, not all the rows must be actived now. */ | ||
397 | if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) { | ||
398 | dev_err(&dev->dev, | ||
399 | "too many keys pressed, control pins initialisation\n"); | ||
400 | goto open_err; | ||
401 | } | ||
402 | |||
403 | return 0; | ||
404 | |||
405 | open_err: | ||
406 | imx_keypad_close(dev); | ||
407 | return -EIO; | ||
408 | } | ||
409 | |||
410 | static int __devinit imx_keypad_probe(struct platform_device *pdev) | ||
411 | { | ||
412 | const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; | ||
413 | struct imx_keypad *keypad; | ||
414 | struct input_dev *input_dev; | ||
415 | struct resource *res; | ||
416 | int irq, error, i; | ||
417 | |||
418 | if (keymap_data == NULL) { | ||
419 | dev_err(&pdev->dev, "no keymap defined\n"); | ||
420 | return -EINVAL; | ||
421 | } | ||
422 | |||
423 | irq = platform_get_irq(pdev, 0); | ||
424 | if (irq < 0) { | ||
425 | dev_err(&pdev->dev, "no irq defined in platform data\n"); | ||
426 | return -EINVAL; | ||
427 | } | ||
428 | |||
429 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
430 | if (res == NULL) { | ||
431 | dev_err(&pdev->dev, "no I/O memory defined in platform data\n"); | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | res = request_mem_region(res->start, resource_size(res), pdev->name); | ||
436 | if (res == NULL) { | ||
437 | dev_err(&pdev->dev, "failed to request I/O memory\n"); | ||
438 | return -EBUSY; | ||
439 | } | ||
440 | |||
441 | input_dev = input_allocate_device(); | ||
442 | if (!input_dev) { | ||
443 | dev_err(&pdev->dev, "failed to allocate the input device\n"); | ||
444 | error = -ENOMEM; | ||
445 | goto failed_rel_mem; | ||
446 | } | ||
447 | |||
448 | keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL); | ||
449 | if (!keypad) { | ||
450 | dev_err(&pdev->dev, "not enough memory for driver data\n"); | ||
451 | error = -ENOMEM; | ||
452 | goto failed_free_input; | ||
453 | } | ||
454 | |||
455 | keypad->input_dev = input_dev; | ||
456 | keypad->irq = irq; | ||
457 | keypad->stable_count = 0; | ||
458 | |||
459 | setup_timer(&keypad->check_matrix_timer, | ||
460 | imx_keypad_check_for_events, (unsigned long) keypad); | ||
461 | |||
462 | keypad->mmio_base = ioremap(res->start, resource_size(res)); | ||
463 | if (keypad->mmio_base == NULL) { | ||
464 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | ||
465 | error = -ENOMEM; | ||
466 | goto failed_free_priv; | ||
467 | } | ||
468 | |||
469 | keypad->clk = clk_get(&pdev->dev, "kpp"); | ||
470 | if (IS_ERR(keypad->clk)) { | ||
471 | dev_err(&pdev->dev, "failed to get keypad clock\n"); | ||
472 | error = PTR_ERR(keypad->clk); | ||
473 | goto failed_unmap; | ||
474 | } | ||
475 | |||
476 | /* Search for rows and cols enabled */ | ||
477 | for (i = 0; i < keymap_data->keymap_size; i++) { | ||
478 | keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]); | ||
479 | keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]); | ||
480 | } | ||
481 | |||
482 | if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || | ||
483 | keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { | ||
484 | dev_err(&pdev->dev, | ||
485 | "invalid key data (too many rows or colums)\n"); | ||
486 | error = -EINVAL; | ||
487 | goto failed_clock_put; | ||
488 | } | ||
489 | dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); | ||
490 | dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); | ||
491 | |||
492 | /* Init the Input device */ | ||
493 | input_dev->name = pdev->name; | ||
494 | input_dev->id.bustype = BUS_HOST; | ||
495 | input_dev->dev.parent = &pdev->dev; | ||
496 | input_dev->open = imx_keypad_open; | ||
497 | input_dev->close = imx_keypad_close; | ||
498 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); | ||
499 | input_dev->keycode = keypad->keycodes; | ||
500 | input_dev->keycodesize = sizeof(keypad->keycodes[0]); | ||
501 | input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); | ||
502 | |||
503 | matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT, | ||
504 | keypad->keycodes, input_dev->keybit); | ||
505 | |||
506 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); | ||
507 | input_set_drvdata(input_dev, keypad); | ||
508 | |||
509 | /* Ensure that the keypad will stay dormant until opened */ | ||
510 | imx_keypad_inhibit(keypad); | ||
511 | |||
512 | error = request_irq(irq, imx_keypad_irq_handler, IRQF_DISABLED, | ||
513 | pdev->name, keypad); | ||
514 | if (error) { | ||
515 | dev_err(&pdev->dev, "failed to request IRQ\n"); | ||
516 | goto failed_clock_put; | ||
517 | } | ||
518 | |||
519 | /* Register the input device */ | ||
520 | error = input_register_device(input_dev); | ||
521 | if (error) { | ||
522 | dev_err(&pdev->dev, "failed to register input device\n"); | ||
523 | goto failed_free_irq; | ||
524 | } | ||
525 | |||
526 | platform_set_drvdata(pdev, keypad); | ||
527 | device_init_wakeup(&pdev->dev, 1); | ||
528 | |||
529 | return 0; | ||
530 | |||
531 | failed_free_irq: | ||
532 | free_irq(irq, pdev); | ||
533 | failed_clock_put: | ||
534 | clk_put(keypad->clk); | ||
535 | failed_unmap: | ||
536 | iounmap(keypad->mmio_base); | ||
537 | failed_free_priv: | ||
538 | kfree(keypad); | ||
539 | failed_free_input: | ||
540 | input_free_device(input_dev); | ||
541 | failed_rel_mem: | ||
542 | release_mem_region(res->start, resource_size(res)); | ||
543 | return error; | ||
544 | } | ||
545 | |||
546 | static int __devexit imx_keypad_remove(struct platform_device *pdev) | ||
547 | { | ||
548 | struct imx_keypad *keypad = platform_get_drvdata(pdev); | ||
549 | struct resource *res; | ||
550 | |||
551 | dev_dbg(&pdev->dev, ">%s\n", __func__); | ||
552 | |||
553 | platform_set_drvdata(pdev, NULL); | ||
554 | |||
555 | input_unregister_device(keypad->input_dev); | ||
556 | |||
557 | free_irq(keypad->irq, keypad); | ||
558 | clk_put(keypad->clk); | ||
559 | |||
560 | iounmap(keypad->mmio_base); | ||
561 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
562 | release_mem_region(res->start, resource_size(res)); | ||
563 | |||
564 | kfree(keypad); | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static struct platform_driver imx_keypad_driver = { | ||
570 | .driver = { | ||
571 | .name = "imx-keypad", | ||
572 | .owner = THIS_MODULE, | ||
573 | }, | ||
574 | .probe = imx_keypad_probe, | ||
575 | .remove = __devexit_p(imx_keypad_remove), | ||
576 | }; | ||
577 | |||
578 | static int __init imx_keypad_init(void) | ||
579 | { | ||
580 | return platform_driver_register(&imx_keypad_driver); | ||
581 | } | ||
582 | |||
583 | static void __exit imx_keypad_exit(void) | ||
584 | { | ||
585 | platform_driver_unregister(&imx_keypad_driver); | ||
586 | } | ||
587 | |||
588 | module_init(imx_keypad_init); | ||
589 | module_exit(imx_keypad_exit); | ||
590 | |||
591 | MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>"); | ||
592 | MODULE_DESCRIPTION("IMX Keypad Port Driver"); | ||
593 | MODULE_LICENSE("GPL v2"); | ||
594 | MODULE_ALIAS("platform:imx-keypad"); | ||