diff options
Diffstat (limited to 'drivers/input/keyboard/lpc32xx-keys.c')
-rw-r--r-- | drivers/input/keyboard/lpc32xx-keys.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c new file mode 100644 index 000000000000..dd786c8a7584 --- /dev/null +++ b/drivers/input/keyboard/lpc32xx-keys.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * NXP LPC32xx SoC Key Scan Interface | ||
3 | * | ||
4 | * Authors: | ||
5 | * Kevin Wells <kevin.wells@nxp.com> | ||
6 | * Roland Stigge <stigge@antcom.de> | ||
7 | * | ||
8 | * Copyright (C) 2010 NXP Semiconductors | ||
9 | * Copyright (C) 2012 Roland Stigge | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * | ||
22 | * This controller supports square key matrices from 1x1 up to 8x8 | ||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/irq.h> | ||
29 | #include <linux/pm.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/input.h> | ||
32 | #include <linux/clk.h> | ||
33 | #include <linux/io.h> | ||
34 | #include <linux/of.h> | ||
35 | #include <linux/input/matrix_keypad.h> | ||
36 | |||
37 | #define DRV_NAME "lpc32xx_keys" | ||
38 | |||
39 | /* | ||
40 | * Key scanner register offsets | ||
41 | */ | ||
42 | #define LPC32XX_KS_DEB(x) ((x) + 0x00) | ||
43 | #define LPC32XX_KS_STATE_COND(x) ((x) + 0x04) | ||
44 | #define LPC32XX_KS_IRQ(x) ((x) + 0x08) | ||
45 | #define LPC32XX_KS_SCAN_CTL(x) ((x) + 0x0C) | ||
46 | #define LPC32XX_KS_FAST_TST(x) ((x) + 0x10) | ||
47 | #define LPC32XX_KS_MATRIX_DIM(x) ((x) + 0x14) /* 1..8 */ | ||
48 | #define LPC32XX_KS_DATA(x, y) ((x) + 0x40 + ((y) << 2)) | ||
49 | |||
50 | #define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n) ((n) & 0xFF) | ||
51 | |||
52 | #define LPC32XX_KSCAN_SCOND_IN_IDLE 0x0 | ||
53 | #define LPC32XX_KSCAN_SCOND_IN_SCANONCE 0x1 | ||
54 | #define LPC32XX_KSCAN_SCOND_IN_IRQGEN 0x2 | ||
55 | #define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX 0x3 | ||
56 | |||
57 | #define LPC32XX_KSCAN_IRQ_PENDING_CLR 0x1 | ||
58 | |||
59 | #define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n) ((n) & 0xFF) | ||
60 | |||
61 | #define LPC32XX_KSCAN_FTST_FORCESCANONCE 0x1 | ||
62 | #define LPC32XX_KSCAN_FTST_USE32K_CLK 0x2 | ||
63 | |||
64 | #define LPC32XX_KSCAN_MSEL_SELECT(n) ((n) & 0xF) | ||
65 | |||
66 | struct lpc32xx_kscan_drv { | ||
67 | struct input_dev *input; | ||
68 | struct clk *clk; | ||
69 | struct resource *iores; | ||
70 | void __iomem *kscan_base; | ||
71 | unsigned int irq; | ||
72 | |||
73 | u32 matrix_sz; /* Size of matrix in XxY, ie. 3 = 3x3 */ | ||
74 | u32 deb_clks; /* Debounce clocks (based on 32KHz clock) */ | ||
75 | u32 scan_delay; /* Scan delay (based on 32KHz clock) */ | ||
76 | |||
77 | unsigned short *keymap; /* Pointer to key map for the scan matrix */ | ||
78 | unsigned int row_shift; | ||
79 | |||
80 | u8 lastkeystates[8]; | ||
81 | }; | ||
82 | |||
83 | static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col) | ||
84 | { | ||
85 | struct input_dev *input = kscandat->input; | ||
86 | unsigned row, changed, scancode, keycode; | ||
87 | u8 key; | ||
88 | |||
89 | key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col)); | ||
90 | changed = key ^ kscandat->lastkeystates[col]; | ||
91 | kscandat->lastkeystates[col] = key; | ||
92 | |||
93 | for (row = 0; changed; row++, changed >>= 1) { | ||
94 | if (changed & 1) { | ||
95 | /* Key state changed, signal an event */ | ||
96 | scancode = MATRIX_SCAN_CODE(row, col, | ||
97 | kscandat->row_shift); | ||
98 | keycode = kscandat->keymap[scancode]; | ||
99 | input_event(input, EV_MSC, MSC_SCAN, scancode); | ||
100 | input_report_key(input, keycode, key & (1 << row)); | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id) | ||
106 | { | ||
107 | struct lpc32xx_kscan_drv *kscandat = dev_id; | ||
108 | int i; | ||
109 | |||
110 | for (i = 0; i < kscandat->matrix_sz; i++) | ||
111 | lpc32xx_mod_states(kscandat, i); | ||
112 | |||
113 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
114 | |||
115 | input_sync(kscandat->input); | ||
116 | |||
117 | return IRQ_HANDLED; | ||
118 | } | ||
119 | |||
120 | static int lpc32xx_kscan_open(struct input_dev *dev) | ||
121 | { | ||
122 | struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev); | ||
123 | int error; | ||
124 | |||
125 | error = clk_prepare_enable(kscandat->clk); | ||
126 | if (error) | ||
127 | return error; | ||
128 | |||
129 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static void lpc32xx_kscan_close(struct input_dev *dev) | ||
135 | { | ||
136 | struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev); | ||
137 | |||
138 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
139 | clk_disable_unprepare(kscandat->clk); | ||
140 | } | ||
141 | |||
142 | static int __devinit lpc32xx_parse_dt(struct device *dev, | ||
143 | struct lpc32xx_kscan_drv *kscandat) | ||
144 | { | ||
145 | struct device_node *np = dev->of_node; | ||
146 | u32 rows = 0, columns = 0; | ||
147 | |||
148 | of_property_read_u32(np, "keypad,num-rows", &rows); | ||
149 | of_property_read_u32(np, "keypad,num-columns", &columns); | ||
150 | if (!rows || rows != columns) { | ||
151 | dev_err(dev, | ||
152 | "rows and columns must be specified and be equal!\n"); | ||
153 | return -EINVAL; | ||
154 | } | ||
155 | |||
156 | kscandat->matrix_sz = rows; | ||
157 | kscandat->row_shift = get_count_order(columns); | ||
158 | |||
159 | of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks); | ||
160 | of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay); | ||
161 | if (!kscandat->deb_clks || !kscandat->scan_delay) { | ||
162 | dev_err(dev, "debounce or scan delay not specified\n"); | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int __devinit lpc32xx_kscan_probe(struct platform_device *pdev) | ||
170 | { | ||
171 | struct lpc32xx_kscan_drv *kscandat; | ||
172 | struct input_dev *input; | ||
173 | struct resource *res; | ||
174 | size_t keymap_size; | ||
175 | int error; | ||
176 | int irq; | ||
177 | |||
178 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
179 | if (!res) { | ||
180 | dev_err(&pdev->dev, "failed to get platform I/O memory\n"); | ||
181 | return -EINVAL; | ||
182 | } | ||
183 | |||
184 | irq = platform_get_irq(pdev, 0); | ||
185 | if (irq < 0 || irq >= NR_IRQS) { | ||
186 | dev_err(&pdev->dev, "failed to get platform irq\n"); | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | |||
190 | kscandat = kzalloc(sizeof(struct lpc32xx_kscan_drv), GFP_KERNEL); | ||
191 | if (!kscandat) { | ||
192 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
193 | return -ENOMEM; | ||
194 | } | ||
195 | |||
196 | error = lpc32xx_parse_dt(&pdev->dev, kscandat); | ||
197 | if (error) { | ||
198 | dev_err(&pdev->dev, "failed to parse device tree\n"); | ||
199 | goto err_free_mem; | ||
200 | } | ||
201 | |||
202 | keymap_size = sizeof(kscandat->keymap[0]) * | ||
203 | (kscandat->matrix_sz << kscandat->row_shift); | ||
204 | kscandat->keymap = kzalloc(keymap_size, GFP_KERNEL); | ||
205 | if (!kscandat->keymap) { | ||
206 | dev_err(&pdev->dev, "could not allocate memory for keymap\n"); | ||
207 | error = -ENOMEM; | ||
208 | goto err_free_mem; | ||
209 | } | ||
210 | |||
211 | kscandat->input = input = input_allocate_device(); | ||
212 | if (!input) { | ||
213 | dev_err(&pdev->dev, "failed to allocate input device\n"); | ||
214 | error = -ENOMEM; | ||
215 | goto err_free_keymap; | ||
216 | } | ||
217 | |||
218 | /* Setup key input */ | ||
219 | input->name = pdev->name; | ||
220 | input->phys = "lpc32xx/input0"; | ||
221 | input->id.vendor = 0x0001; | ||
222 | input->id.product = 0x0001; | ||
223 | input->id.version = 0x0100; | ||
224 | input->open = lpc32xx_kscan_open; | ||
225 | input->close = lpc32xx_kscan_close; | ||
226 | input->dev.parent = &pdev->dev; | ||
227 | |||
228 | input_set_capability(input, EV_MSC, MSC_SCAN); | ||
229 | |||
230 | error = matrix_keypad_build_keymap(NULL, NULL, | ||
231 | kscandat->matrix_sz, | ||
232 | kscandat->matrix_sz, | ||
233 | kscandat->keymap, kscandat->input); | ||
234 | if (error) { | ||
235 | dev_err(&pdev->dev, "failed to build keymap\n"); | ||
236 | goto err_free_input; | ||
237 | } | ||
238 | |||
239 | input_set_drvdata(kscandat->input, kscandat); | ||
240 | |||
241 | kscandat->iores = request_mem_region(res->start, resource_size(res), | ||
242 | pdev->name); | ||
243 | if (!kscandat->iores) { | ||
244 | dev_err(&pdev->dev, "failed to request I/O memory\n"); | ||
245 | error = -EBUSY; | ||
246 | goto err_free_input; | ||
247 | } | ||
248 | |||
249 | kscandat->kscan_base = ioremap(kscandat->iores->start, | ||
250 | resource_size(kscandat->iores)); | ||
251 | if (!kscandat->kscan_base) { | ||
252 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | ||
253 | error = -EBUSY; | ||
254 | goto err_release_memregion; | ||
255 | } | ||
256 | |||
257 | /* Get the key scanner clock */ | ||
258 | kscandat->clk = clk_get(&pdev->dev, NULL); | ||
259 | if (IS_ERR(kscandat->clk)) { | ||
260 | dev_err(&pdev->dev, "failed to get clock\n"); | ||
261 | error = PTR_ERR(kscandat->clk); | ||
262 | goto err_unmap; | ||
263 | } | ||
264 | |||
265 | /* Configure the key scanner */ | ||
266 | error = clk_prepare_enable(kscandat->clk); | ||
267 | if (error) | ||
268 | goto err_clk_put; | ||
269 | |||
270 | writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base)); | ||
271 | writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base)); | ||
272 | writel(LPC32XX_KSCAN_FTST_USE32K_CLK, | ||
273 | LPC32XX_KS_FAST_TST(kscandat->kscan_base)); | ||
274 | writel(kscandat->matrix_sz, | ||
275 | LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base)); | ||
276 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
277 | clk_disable_unprepare(kscandat->clk); | ||
278 | |||
279 | error = request_irq(irq, lpc32xx_kscan_irq, 0, pdev->name, kscandat); | ||
280 | if (error) { | ||
281 | dev_err(&pdev->dev, "failed to request irq\n"); | ||
282 | goto err_clk_put; | ||
283 | } | ||
284 | |||
285 | error = input_register_device(kscandat->input); | ||
286 | if (error) { | ||
287 | dev_err(&pdev->dev, "failed to register input device\n"); | ||
288 | goto err_free_irq; | ||
289 | } | ||
290 | |||
291 | platform_set_drvdata(pdev, kscandat); | ||
292 | return 0; | ||
293 | |||
294 | err_free_irq: | ||
295 | free_irq(irq, kscandat); | ||
296 | err_clk_put: | ||
297 | clk_put(kscandat->clk); | ||
298 | err_unmap: | ||
299 | iounmap(kscandat->kscan_base); | ||
300 | err_release_memregion: | ||
301 | release_mem_region(kscandat->iores->start, | ||
302 | resource_size(kscandat->iores)); | ||
303 | err_free_input: | ||
304 | input_free_device(kscandat->input); | ||
305 | err_free_keymap: | ||
306 | kfree(kscandat->keymap); | ||
307 | err_free_mem: | ||
308 | kfree(kscandat); | ||
309 | |||
310 | return error; | ||
311 | } | ||
312 | |||
313 | static int __devexit lpc32xx_kscan_remove(struct platform_device *pdev) | ||
314 | { | ||
315 | struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); | ||
316 | |||
317 | free_irq(platform_get_irq(pdev, 0), kscandat); | ||
318 | clk_put(kscandat->clk); | ||
319 | iounmap(kscandat->kscan_base); | ||
320 | release_mem_region(kscandat->iores->start, | ||
321 | resource_size(kscandat->iores)); | ||
322 | input_unregister_device(kscandat->input); | ||
323 | kfree(kscandat->keymap); | ||
324 | kfree(kscandat); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | #ifdef CONFIG_PM_SLEEP | ||
330 | static int lpc32xx_kscan_suspend(struct device *dev) | ||
331 | { | ||
332 | struct platform_device *pdev = to_platform_device(dev); | ||
333 | struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); | ||
334 | struct input_dev *input = kscandat->input; | ||
335 | |||
336 | mutex_lock(&input->mutex); | ||
337 | |||
338 | if (input->users) { | ||
339 | /* Clear IRQ and disable clock */ | ||
340 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
341 | clk_disable_unprepare(kscandat->clk); | ||
342 | } | ||
343 | |||
344 | mutex_unlock(&input->mutex); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int lpc32xx_kscan_resume(struct device *dev) | ||
349 | { | ||
350 | struct platform_device *pdev = to_platform_device(dev); | ||
351 | struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); | ||
352 | struct input_dev *input = kscandat->input; | ||
353 | int retval = 0; | ||
354 | |||
355 | mutex_lock(&input->mutex); | ||
356 | |||
357 | if (input->users) { | ||
358 | /* Enable clock and clear IRQ */ | ||
359 | retval = clk_prepare_enable(kscandat->clk); | ||
360 | if (retval == 0) | ||
361 | writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); | ||
362 | } | ||
363 | |||
364 | mutex_unlock(&input->mutex); | ||
365 | return retval; | ||
366 | } | ||
367 | #endif | ||
368 | |||
369 | static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend, | ||
370 | lpc32xx_kscan_resume); | ||
371 | |||
372 | static const struct of_device_id lpc32xx_kscan_match[] = { | ||
373 | { .compatible = "nxp,lpc3220-key" }, | ||
374 | {}, | ||
375 | }; | ||
376 | MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match); | ||
377 | |||
378 | static struct platform_driver lpc32xx_kscan_driver = { | ||
379 | .probe = lpc32xx_kscan_probe, | ||
380 | .remove = __devexit_p(lpc32xx_kscan_remove), | ||
381 | .driver = { | ||
382 | .name = DRV_NAME, | ||
383 | .owner = THIS_MODULE, | ||
384 | .pm = &lpc32xx_kscan_pm_ops, | ||
385 | .of_match_table = of_match_ptr(lpc32xx_kscan_match), | ||
386 | } | ||
387 | }; | ||
388 | |||
389 | module_platform_driver(lpc32xx_kscan_driver); | ||
390 | |||
391 | MODULE_LICENSE("GPL"); | ||
392 | MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); | ||
393 | MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); | ||
394 | MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices"); | ||