Commit | Line | Data |
---|---|---|
bc95df78 RK |
1 | /* |
2 | * SPEAr Keyboard Driver | |
3 | * Based on omap-keypad driver | |
4 | * | |
5 | * Copyright (C) 2010 ST Microelectronics | |
6 | * Rajeev Kumar<rajeev-dlh.kumar@st.com> | |
7 | * | |
8 | * This file is licensed under the terms of the GNU General Public | |
9 | * License version 2. This program is licensed "as is" without any | |
10 | * warranty of any kind, whether express or implied. | |
11 | */ | |
12 | ||
13 | #include <linux/clk.h> | |
14 | #include <linux/errno.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/input.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/irq.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
829c4f96 | 22 | #include <linux/of.h> |
bc95df78 RK |
23 | #include <linux/platform_device.h> |
24 | #include <linux/pm_wakeup.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/types.h> | |
27 | #include <plat/keyboard.h> | |
28 | ||
29 | /* Keyboard Registers */ | |
30 | #define MODE_REG 0x00 /* 16 bit reg */ | |
31 | #define STATUS_REG 0x0C /* 2 bit reg */ | |
32 | #define DATA_REG 0x10 /* 8 bit reg */ | |
33 | #define INTR_MASK 0x54 | |
34 | ||
35 | /* Register Values */ | |
36 | /* | |
37 | * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode | |
38 | * control register as 1010010(82MHZ) | |
39 | */ | |
40 | #define PCLK_FREQ_MSK 0xA400 /* 82 MHz */ | |
41 | #define START_SCAN 0x0100 | |
42 | #define SCAN_RATE_10 0x0000 | |
43 | #define SCAN_RATE_20 0x0004 | |
44 | #define SCAN_RATE_40 0x0008 | |
45 | #define SCAN_RATE_80 0x000C | |
46 | #define MODE_KEYBOARD 0x0002 | |
47 | #define DATA_AVAIL 0x2 | |
48 | ||
49 | #define KEY_MASK 0xFF000000 | |
50 | #define KEY_VALUE 0x00FFFFFF | |
51 | #define ROW_MASK 0xF0 | |
52 | #define COLUMN_MASK 0x0F | |
1932811f DT |
53 | #define NUM_ROWS 16 |
54 | #define NUM_COLS 16 | |
55 | ||
f8354c60 | 56 | #define KEY_MATRIX_SHIFT 6 |
bc95df78 RK |
57 | |
58 | struct spear_kbd { | |
59 | struct input_dev *input; | |
60 | struct resource *res; | |
61 | void __iomem *io_base; | |
62 | struct clk *clk; | |
63 | unsigned int irq; | |
f8354c60 | 64 | unsigned int mode; |
bc95df78 | 65 | unsigned short last_key; |
1932811f | 66 | unsigned short keycodes[NUM_ROWS * NUM_COLS]; |
829c4f96 | 67 | bool rep; |
bc95df78 RK |
68 | }; |
69 | ||
70 | static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) | |
71 | { | |
72 | struct spear_kbd *kbd = dev_id; | |
73 | struct input_dev *input = kbd->input; | |
74 | unsigned int key; | |
75 | u8 sts, val; | |
76 | ||
77 | sts = readb(kbd->io_base + STATUS_REG); | |
799a2a21 | 78 | if (!(sts & DATA_AVAIL)) |
bc95df78 RK |
79 | return IRQ_NONE; |
80 | ||
81 | if (kbd->last_key != KEY_RESERVED) { | |
82 | input_report_key(input, kbd->last_key, 0); | |
83 | kbd->last_key = KEY_RESERVED; | |
84 | } | |
85 | ||
86 | /* following reads active (row, col) pair */ | |
87 | val = readb(kbd->io_base + DATA_REG); | |
88 | key = kbd->keycodes[val]; | |
89 | ||
90 | input_event(input, EV_MSC, MSC_SCAN, val); | |
91 | input_report_key(input, key, 1); | |
92 | input_sync(input); | |
93 | ||
94 | kbd->last_key = key; | |
95 | ||
96 | /* clear interrupt */ | |
97 | writeb(0, kbd->io_base + STATUS_REG); | |
98 | ||
99 | return IRQ_HANDLED; | |
100 | } | |
101 | ||
102 | static int spear_kbd_open(struct input_dev *dev) | |
103 | { | |
104 | struct spear_kbd *kbd = input_get_drvdata(dev); | |
105 | int error; | |
106 | u16 val; | |
107 | ||
108 | kbd->last_key = KEY_RESERVED; | |
109 | ||
110 | error = clk_enable(kbd->clk); | |
111 | if (error) | |
112 | return error; | |
113 | ||
114 | /* program keyboard */ | |
f8354c60 RK |
115 | val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK | |
116 | (kbd->mode << KEY_MATRIX_SHIFT); | |
bc95df78 RK |
117 | writew(val, kbd->io_base + MODE_REG); |
118 | writeb(1, kbd->io_base + STATUS_REG); | |
119 | ||
120 | /* start key scan */ | |
121 | val = readw(kbd->io_base + MODE_REG); | |
122 | val |= START_SCAN; | |
123 | writew(val, kbd->io_base + MODE_REG); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static void spear_kbd_close(struct input_dev *dev) | |
129 | { | |
130 | struct spear_kbd *kbd = input_get_drvdata(dev); | |
131 | u16 val; | |
132 | ||
133 | /* stop key scan */ | |
134 | val = readw(kbd->io_base + MODE_REG); | |
135 | val &= ~START_SCAN; | |
136 | writew(val, kbd->io_base + MODE_REG); | |
137 | ||
138 | clk_disable(kbd->clk); | |
139 | ||
140 | kbd->last_key = KEY_RESERVED; | |
141 | } | |
142 | ||
829c4f96 VK |
143 | #ifdef CONFIG_OF |
144 | static int __devinit spear_kbd_parse_dt(struct platform_device *pdev, | |
145 | struct spear_kbd *kbd) | |
bc95df78 | 146 | { |
829c4f96 | 147 | struct device_node *np = pdev->dev.of_node; |
bc95df78 | 148 | int error; |
829c4f96 | 149 | u32 val; |
bc95df78 | 150 | |
829c4f96 VK |
151 | if (!np) { |
152 | dev_err(&pdev->dev, "Missing DT data\n"); | |
bc95df78 RK |
153 | return -EINVAL; |
154 | } | |
155 | ||
829c4f96 VK |
156 | if (of_property_read_bool(np, "autorepeat")) |
157 | kbd->rep = true; | |
158 | ||
159 | error = of_property_read_u32(np, "st,mode", &val); | |
160 | if (error) { | |
161 | dev_err(&pdev->dev, "DT: Invalid or missing mode\n"); | |
162 | return error; | |
bc95df78 RK |
163 | } |
164 | ||
829c4f96 VK |
165 | kbd->mode = val; |
166 | return 0; | |
167 | } | |
168 | #else | |
169 | static inline int spear_kbd_parse_dt(struct platform_device *pdev, | |
170 | struct spear_kbd *kbd) | |
171 | { | |
172 | return -ENOSYS; | |
173 | } | |
174 | #endif | |
175 | ||
176 | static int __devinit spear_kbd_probe(struct platform_device *pdev) | |
177 | { | |
178 | struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
179 | const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL; | |
180 | struct spear_kbd *kbd; | |
181 | struct input_dev *input_dev; | |
182 | struct resource *res; | |
183 | int irq; | |
184 | int error; | |
185 | ||
bc95df78 RK |
186 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
187 | if (!res) { | |
188 | dev_err(&pdev->dev, "no keyboard resource defined\n"); | |
189 | return -EBUSY; | |
190 | } | |
191 | ||
192 | irq = platform_get_irq(pdev, 0); | |
193 | if (irq < 0) { | |
194 | dev_err(&pdev->dev, "not able to get irq for the device\n"); | |
195 | return irq; | |
196 | } | |
197 | ||
198 | kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); | |
199 | input_dev = input_allocate_device(); | |
200 | if (!kbd || !input_dev) { | |
201 | dev_err(&pdev->dev, "out of memory\n"); | |
202 | error = -ENOMEM; | |
203 | goto err_free_mem; | |
204 | } | |
205 | ||
206 | kbd->input = input_dev; | |
207 | kbd->irq = irq; | |
829c4f96 VK |
208 | |
209 | if (!pdata) { | |
210 | error = spear_kbd_parse_dt(pdev, kbd); | |
211 | if (error) | |
212 | goto err_free_mem; | |
213 | } else { | |
214 | kbd->mode = pdata->mode; | |
215 | kbd->rep = pdata->rep; | |
216 | } | |
f8354c60 | 217 | |
bc95df78 RK |
218 | kbd->res = request_mem_region(res->start, resource_size(res), |
219 | pdev->name); | |
220 | if (!kbd->res) { | |
221 | dev_err(&pdev->dev, "keyboard region already claimed\n"); | |
222 | error = -EBUSY; | |
223 | goto err_free_mem; | |
224 | } | |
225 | ||
226 | kbd->io_base = ioremap(res->start, resource_size(res)); | |
227 | if (!kbd->io_base) { | |
228 | dev_err(&pdev->dev, "ioremap failed for kbd_region\n"); | |
229 | error = -ENOMEM; | |
230 | goto err_release_mem_region; | |
231 | } | |
232 | ||
233 | kbd->clk = clk_get(&pdev->dev, NULL); | |
234 | if (IS_ERR(kbd->clk)) { | |
235 | error = PTR_ERR(kbd->clk); | |
236 | goto err_iounmap; | |
237 | } | |
238 | ||
239 | input_dev->name = "Spear Keyboard"; | |
240 | input_dev->phys = "keyboard/input0"; | |
241 | input_dev->dev.parent = &pdev->dev; | |
242 | input_dev->id.bustype = BUS_HOST; | |
243 | input_dev->id.vendor = 0x0001; | |
244 | input_dev->id.product = 0x0001; | |
245 | input_dev->id.version = 0x0100; | |
246 | input_dev->open = spear_kbd_open; | |
247 | input_dev->close = spear_kbd_close; | |
248 | ||
1932811f DT |
249 | error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, |
250 | kbd->keycodes, input_dev); | |
251 | if (error) { | |
252 | dev_err(&pdev->dev, "Failed to build keymap\n"); | |
253 | goto err_put_clk; | |
254 | } | |
255 | ||
829c4f96 | 256 | if (kbd->rep) |
bc95df78 RK |
257 | __set_bit(EV_REP, input_dev->evbit); |
258 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); | |
259 | ||
bc95df78 RK |
260 | input_set_drvdata(input_dev, kbd); |
261 | ||
262 | error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); | |
263 | if (error) { | |
264 | dev_err(&pdev->dev, "request_irq fail\n"); | |
265 | goto err_put_clk; | |
266 | } | |
267 | ||
268 | error = input_register_device(input_dev); | |
269 | if (error) { | |
270 | dev_err(&pdev->dev, "Unable to register keyboard device\n"); | |
271 | goto err_free_irq; | |
272 | } | |
273 | ||
274 | device_init_wakeup(&pdev->dev, 1); | |
275 | platform_set_drvdata(pdev, kbd); | |
276 | ||
277 | return 0; | |
278 | ||
279 | err_free_irq: | |
280 | free_irq(kbd->irq, kbd); | |
281 | err_put_clk: | |
282 | clk_put(kbd->clk); | |
283 | err_iounmap: | |
284 | iounmap(kbd->io_base); | |
285 | err_release_mem_region: | |
286 | release_mem_region(res->start, resource_size(res)); | |
287 | err_free_mem: | |
288 | input_free_device(input_dev); | |
289 | kfree(kbd); | |
290 | ||
291 | return error; | |
292 | } | |
293 | ||
294 | static int __devexit spear_kbd_remove(struct platform_device *pdev) | |
295 | { | |
296 | struct spear_kbd *kbd = platform_get_drvdata(pdev); | |
297 | ||
298 | free_irq(kbd->irq, kbd); | |
299 | input_unregister_device(kbd->input); | |
300 | clk_put(kbd->clk); | |
301 | iounmap(kbd->io_base); | |
302 | release_mem_region(kbd->res->start, resource_size(kbd->res)); | |
303 | kfree(kbd); | |
304 | ||
305 | device_init_wakeup(&pdev->dev, 1); | |
306 | platform_set_drvdata(pdev, NULL); | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | #ifdef CONFIG_PM | |
312 | static int spear_kbd_suspend(struct device *dev) | |
313 | { | |
314 | struct platform_device *pdev = to_platform_device(dev); | |
315 | struct spear_kbd *kbd = platform_get_drvdata(pdev); | |
316 | struct input_dev *input_dev = kbd->input; | |
317 | ||
318 | mutex_lock(&input_dev->mutex); | |
319 | ||
320 | if (input_dev->users) | |
321 | clk_enable(kbd->clk); | |
322 | ||
323 | if (device_may_wakeup(&pdev->dev)) | |
324 | enable_irq_wake(kbd->irq); | |
325 | ||
326 | mutex_unlock(&input_dev->mutex); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static int spear_kbd_resume(struct device *dev) | |
332 | { | |
333 | struct platform_device *pdev = to_platform_device(dev); | |
334 | struct spear_kbd *kbd = platform_get_drvdata(pdev); | |
335 | struct input_dev *input_dev = kbd->input; | |
336 | ||
337 | mutex_lock(&input_dev->mutex); | |
338 | ||
339 | if (device_may_wakeup(&pdev->dev)) | |
340 | disable_irq_wake(kbd->irq); | |
341 | ||
342 | if (input_dev->users) | |
343 | clk_enable(kbd->clk); | |
344 | ||
345 | mutex_unlock(&input_dev->mutex); | |
346 | ||
347 | return 0; | |
348 | } | |
bc95df78 RK |
349 | #endif |
350 | ||
f79e30a8 VK |
351 | static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); |
352 | ||
829c4f96 VK |
353 | #ifdef CONFIG_OF |
354 | static const struct of_device_id spear_kbd_id_table[] = { | |
355 | { .compatible = "st,spear300-kbd" }, | |
356 | {} | |
357 | }; | |
358 | MODULE_DEVICE_TABLE(of, spear_kbd_id_table); | |
359 | #endif | |
360 | ||
bc95df78 RK |
361 | static struct platform_driver spear_kbd_driver = { |
362 | .probe = spear_kbd_probe, | |
363 | .remove = __devexit_p(spear_kbd_remove), | |
364 | .driver = { | |
365 | .name = "keyboard", | |
366 | .owner = THIS_MODULE, | |
bc95df78 | 367 | .pm = &spear_kbd_pm_ops, |
829c4f96 | 368 | .of_match_table = of_match_ptr(spear_kbd_id_table), |
bc95df78 RK |
369 | }, |
370 | }; | |
5146c84f | 371 | module_platform_driver(spear_kbd_driver); |
bc95df78 RK |
372 | |
373 | MODULE_AUTHOR("Rajeev Kumar"); | |
374 | MODULE_DESCRIPTION("SPEAr Keyboard Driver"); | |
375 | MODULE_LICENSE("GPL"); |