diff options
-rw-r--r-- | arch/arm/mach-w90x900/include/mach/w90p910_keypad.h | 18 | ||||
-rw-r--r-- | drivers/input/keyboard/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/w90p910_keypad.c | 305 |
4 files changed, 334 insertions, 0 deletions
diff --git a/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h b/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h new file mode 100644 index 000000000000..79462faaa189 --- /dev/null +++ b/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef __ASM_ARCH_W90P910_KEYPAD_H | ||
2 | #define __ASM_ARCH_W90P910_KEYPAD_H | ||
3 | |||
4 | #include <linux/input/matrix_keypad.h> | ||
5 | |||
6 | extern void mfp_set_groupi(struct device *dev); | ||
7 | |||
8 | struct w90p910_keypad_platform_data { | ||
9 | |||
10 | unsigned int prescale; | ||
11 | unsigned int debounce; | ||
12 | unsigned int matrix_key_rows; | ||
13 | unsigned int matrix_key_cols; | ||
14 | unsigned int *matrix_key_map; | ||
15 | int matrix_key_map_size; | ||
16 | }; | ||
17 | |||
18 | #endif /* __ASM_ARCH_W90P910_KEYPAD_H */ | ||
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 5239e25e88ac..50e407de8a78 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -362,4 +362,14 @@ config KEYBOARD_XTKBD | |||
362 | To compile this driver as a module, choose M here: the | 362 | To compile this driver as a module, choose M here: the |
363 | module will be called xtkbd. | 363 | module will be called xtkbd. |
364 | 364 | ||
365 | config KEYBOARD_W90P910 | ||
366 | tristate "W90P910 Matrix Keypad support" | ||
367 | depends on ARCH_W90X900 | ||
368 | help | ||
369 | Say Y here to enable the matrix keypad on evaluation board | ||
370 | based on W90P910. | ||
371 | |||
372 | To compile this driver as a module, choose M here: the | ||
373 | module will be called w90p910_keypad. | ||
374 | |||
365 | endif | 375 | endif |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index b5b5eae9724f..152303029203 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -31,3 +31,4 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o | |||
31 | obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o | 31 | obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o |
32 | obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o | 32 | obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o |
33 | obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o | 33 | obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o |
34 | obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o | ||
diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c new file mode 100644 index 000000000000..472c70514af0 --- /dev/null +++ b/drivers/input/keyboard/w90p910_keypad.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008-2009 Nuvoton technology corporation. | ||
3 | * | ||
4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation;version 2 of the License. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/input.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <mach/w90p910_keypad.h> | ||
24 | |||
25 | /* Keypad Interface Control Registers */ | ||
26 | #define KPI_CONF 0x00 | ||
27 | #define KPI_3KCONF 0x04 | ||
28 | #define KPI_LPCONF 0x08 | ||
29 | #define KPI_STATUS 0x0C | ||
30 | |||
31 | #define IS1KEY (0x01 << 16) | ||
32 | #define INTTR (0x01 << 21) | ||
33 | #define KEY0R (0x0f << 3) | ||
34 | #define KEY0C 0x07 | ||
35 | #define DEBOUNCE_BIT 0x08 | ||
36 | #define KSIZE0 (0x01 << 16) | ||
37 | #define KSIZE1 (0x01 << 17) | ||
38 | #define KPSEL (0x01 << 19) | ||
39 | #define ENKP (0x01 << 18) | ||
40 | |||
41 | #define KGET_RAW(n) (((n) & KEY0R) >> 3) | ||
42 | #define KGET_COLUMN(n) ((n) & KEY0C) | ||
43 | |||
44 | #define MAX_MATRIX_KEY_NUM (8 * 8) | ||
45 | |||
46 | struct w90p910_keypad { | ||
47 | struct w90p910_keypad_platform_data *pdata; | ||
48 | struct clk *clk; | ||
49 | struct input_dev *input_dev; | ||
50 | void __iomem *mmio_base; | ||
51 | int irq; | ||
52 | unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM]; | ||
53 | }; | ||
54 | |||
55 | static void w90p910_keypad_build_keycode(struct w90p910_keypad *keypad) | ||
56 | { | ||
57 | struct w90p910_keypad_platform_data *pdata = keypad->pdata; | ||
58 | struct input_dev *input_dev = keypad->input_dev; | ||
59 | unsigned int *key; | ||
60 | int i; | ||
61 | |||
62 | key = &pdata->matrix_key_map[0]; | ||
63 | for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { | ||
64 | int row = KEY_ROW(*key); | ||
65 | int col = KEY_COL(*key); | ||
66 | int code = KEY_VAL(*key); | ||
67 | |||
68 | keypad->matrix_keycodes[(row << 3) + col] = code; | ||
69 | __set_bit(code, input_dev->keybit); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static inline unsigned int lookup_matrix_keycode( | ||
74 | struct w90p910_keypad *keypad, int row, int col) | ||
75 | { | ||
76 | return keypad->matrix_keycodes[(row << 3) + col]; | ||
77 | } | ||
78 | |||
79 | static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, | ||
80 | unsigned int status) | ||
81 | { | ||
82 | unsigned int row, col, val; | ||
83 | |||
84 | row = KGET_RAW(status); | ||
85 | col = KGET_COLUMN(status); | ||
86 | |||
87 | val = lookup_matrix_keycode(keypad, row, col); | ||
88 | |||
89 | input_report_key(keypad->input_dev, val, 1); | ||
90 | |||
91 | input_sync(keypad->input_dev); | ||
92 | |||
93 | input_report_key(keypad->input_dev, val, 0); | ||
94 | |||
95 | input_sync(keypad->input_dev); | ||
96 | } | ||
97 | |||
98 | static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) | ||
99 | { | ||
100 | struct w90p910_keypad *keypad = dev_id; | ||
101 | unsigned int kstatus, val; | ||
102 | |||
103 | kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); | ||
104 | |||
105 | val = INTTR | IS1KEY; | ||
106 | |||
107 | if (kstatus & val) | ||
108 | w90p910_keypad_scan_matrix(keypad, kstatus); | ||
109 | |||
110 | return IRQ_HANDLED; | ||
111 | } | ||
112 | |||
113 | static int w90p910_keypad_open(struct input_dev *dev) | ||
114 | { | ||
115 | struct w90p910_keypad *keypad = input_get_drvdata(dev); | ||
116 | struct w90p910_keypad_platform_data *pdata = keypad->pdata; | ||
117 | unsigned int val, config; | ||
118 | |||
119 | /* Enable unit clock */ | ||
120 | clk_enable(keypad->clk); | ||
121 | |||
122 | val = __raw_readl(keypad->mmio_base + KPI_CONF); | ||
123 | val |= (KPSEL | ENKP); | ||
124 | val &= ~(KSIZE0 | KSIZE1); | ||
125 | |||
126 | config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); | ||
127 | |||
128 | val |= config; | ||
129 | |||
130 | __raw_writel(val, keypad->mmio_base + KPI_CONF); | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static void w90p910_keypad_close(struct input_dev *dev) | ||
136 | { | ||
137 | struct w90p910_keypad *keypad = input_get_drvdata(dev); | ||
138 | |||
139 | /* Disable clock unit */ | ||
140 | clk_disable(keypad->clk); | ||
141 | } | ||
142 | |||
143 | static int __devinit w90p910_keypad_probe(struct platform_device *pdev) | ||
144 | { | ||
145 | struct w90p910_keypad *keypad; | ||
146 | struct input_dev *input_dev; | ||
147 | struct resource *res; | ||
148 | int irq, error; | ||
149 | |||
150 | keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); | ||
151 | if (keypad == NULL) { | ||
152 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | ||
153 | return -ENOMEM; | ||
154 | } | ||
155 | |||
156 | keypad->pdata = pdev->dev.platform_data; | ||
157 | if (keypad->pdata == NULL) { | ||
158 | dev_err(&pdev->dev, "no platform data defined\n"); | ||
159 | error = -EINVAL; | ||
160 | goto failed_free; | ||
161 | } | ||
162 | |||
163 | irq = platform_get_irq(pdev, 0); | ||
164 | if (irq < 0) { | ||
165 | dev_err(&pdev->dev, "failed to get keypad irq\n"); | ||
166 | error = -ENXIO; | ||
167 | goto failed_free; | ||
168 | } | ||
169 | |||
170 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
171 | if (res == NULL) { | ||
172 | dev_err(&pdev->dev, "failed to get I/O memory\n"); | ||
173 | error = -ENXIO; | ||
174 | goto failed_free; | ||
175 | } | ||
176 | |||
177 | res = request_mem_region(res->start, resource_size(res), pdev->name); | ||
178 | if (res == NULL) { | ||
179 | dev_err(&pdev->dev, "failed to request I/O memory\n"); | ||
180 | error = -EBUSY; | ||
181 | goto failed_free; | ||
182 | } | ||
183 | |||
184 | keypad->mmio_base = ioremap(res->start, resource_size(res)); | ||
185 | if (keypad->mmio_base == NULL) { | ||
186 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | ||
187 | error = -ENXIO; | ||
188 | goto failed_free_mem; | ||
189 | } | ||
190 | |||
191 | keypad->clk = clk_get(&pdev->dev, NULL); | ||
192 | if (IS_ERR(keypad->clk)) { | ||
193 | dev_err(&pdev->dev, "failed to get keypad clock\n"); | ||
194 | error = PTR_ERR(keypad->clk); | ||
195 | goto failed_free_io; | ||
196 | } | ||
197 | |||
198 | /* Create and register the input driver. */ | ||
199 | input_dev = input_allocate_device(); | ||
200 | if (!input_dev) { | ||
201 | dev_err(&pdev->dev, "failed to allocate input device\n"); | ||
202 | error = -ENOMEM; | ||
203 | goto failed_put_clk; | ||
204 | } | ||
205 | |||
206 | /* set multi-function pin for w90p910 kpi. */ | ||
207 | mfp_set_groupi(&pdev->dev); | ||
208 | |||
209 | input_dev->name = pdev->name; | ||
210 | input_dev->id.bustype = BUS_HOST; | ||
211 | input_dev->open = w90p910_keypad_open; | ||
212 | input_dev->close = w90p910_keypad_close; | ||
213 | input_dev->dev.parent = &pdev->dev; | ||
214 | input_dev->keycode = keypad->matrix_keycodes; | ||
215 | input_dev->keycodesize = sizeof(keypad->matrix_keycodes[0]); | ||
216 | input_dev->keycodemax = ARRAY_SIZE(keypad->matrix_keycodes); | ||
217 | |||
218 | keypad->input_dev = input_dev; | ||
219 | input_set_drvdata(input_dev, keypad); | ||
220 | |||
221 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); | ||
222 | w90p910_keypad_build_keycode(keypad); | ||
223 | platform_set_drvdata(pdev, keypad); | ||
224 | |||
225 | error = request_irq(irq, w90p910_keypad_irq_handler, IRQF_DISABLED, | ||
226 | pdev->name, keypad); | ||
227 | if (error) { | ||
228 | dev_err(&pdev->dev, "failed to request IRQ\n"); | ||
229 | goto failed_free_dev; | ||
230 | } | ||
231 | |||
232 | keypad->irq = irq; | ||
233 | |||
234 | /* Register the input device */ | ||
235 | error = input_register_device(input_dev); | ||
236 | if (error) { | ||
237 | dev_err(&pdev->dev, "failed to register input device\n"); | ||
238 | goto failed_free_irq; | ||
239 | } | ||
240 | |||
241 | return 0; | ||
242 | |||
243 | failed_free_irq: | ||
244 | free_irq(irq, pdev); | ||
245 | platform_set_drvdata(pdev, NULL); | ||
246 | failed_free_dev: | ||
247 | input_free_device(input_dev); | ||
248 | failed_put_clk: | ||
249 | clk_put(keypad->clk); | ||
250 | failed_free_io: | ||
251 | iounmap(keypad->mmio_base); | ||
252 | failed_free_mem: | ||
253 | release_mem_region(res->start, resource_size(res)); | ||
254 | failed_free: | ||
255 | kfree(keypad); | ||
256 | return error; | ||
257 | } | ||
258 | |||
259 | static int __devexit w90p910_keypad_remove(struct platform_device *pdev) | ||
260 | { | ||
261 | struct w90p910_keypad *keypad = platform_get_drvdata(pdev); | ||
262 | struct resource *res; | ||
263 | |||
264 | free_irq(keypad->irq, pdev); | ||
265 | |||
266 | clk_put(keypad->clk); | ||
267 | |||
268 | input_unregister_device(keypad->input_dev); | ||
269 | |||
270 | iounmap(keypad->mmio_base); | ||
271 | |||
272 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
273 | release_mem_region(res->start, resource_size(res)); | ||
274 | |||
275 | platform_set_drvdata(pdev, NULL); | ||
276 | kfree(keypad); | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static struct platform_driver w90p910_keypad_driver = { | ||
281 | .probe = w90p910_keypad_probe, | ||
282 | .remove = __devexit_p(w90p910_keypad_remove), | ||
283 | .driver = { | ||
284 | .name = "w90p910-keypad", | ||
285 | .owner = THIS_MODULE, | ||
286 | }, | ||
287 | }; | ||
288 | |||
289 | static int __init w90p910_keypad_init(void) | ||
290 | { | ||
291 | return platform_driver_register(&w90p910_keypad_driver); | ||
292 | } | ||
293 | |||
294 | static void __exit w90p910_keypad_exit(void) | ||
295 | { | ||
296 | platform_driver_unregister(&w90p910_keypad_driver); | ||
297 | } | ||
298 | |||
299 | module_init(w90p910_keypad_init); | ||
300 | module_exit(w90p910_keypad_exit); | ||
301 | |||
302 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | ||
303 | MODULE_DESCRIPTION("w90p910 keypad driver!"); | ||
304 | MODULE_LICENSE("GPL"); | ||
305 | MODULE_ALIAS("platform:w90p910-keypad"); | ||