diff options
author | Thomas Abraham <thomas.abraham@linaro.org> | 2011-11-02 06:37:22 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2011-12-22 20:07:10 -0500 |
commit | b3d6ac3e5f937440a362c0fa187257fa1197f5b9 (patch) | |
tree | f9d535987acc864a3948174e893077b5f6543138 /drivers/input/keyboard | |
parent | 8742e0441d6530d40d85d65a6bcc5a6c0c4eab13 (diff) |
input: samsung-keypad: Add device tree support
Add device tree based discovery support for Samsung's keypad controller.
Cc: Joonyoung Shim <jy0922.shim@samsung.com>
Cc: Donghwa Lee <dh09.lee@samsung.com>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r-- | drivers/input/keyboard/samsung-keypad.c | 174 |
1 files changed, 162 insertions, 12 deletions
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index f689f49e3109..8a0060cd3982 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/of.h> | ||
25 | #include <linux/of_gpio.h> | ||
24 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
25 | #include <plat/keypad.h> | 27 | #include <plat/keypad.h> |
26 | 28 | ||
@@ -68,31 +70,26 @@ struct samsung_keypad { | |||
68 | wait_queue_head_t wait; | 70 | wait_queue_head_t wait; |
69 | bool stopped; | 71 | bool stopped; |
70 | int irq; | 72 | int irq; |
73 | enum samsung_keypad_type type; | ||
71 | unsigned int row_shift; | 74 | unsigned int row_shift; |
72 | unsigned int rows; | 75 | unsigned int rows; |
73 | unsigned int cols; | 76 | unsigned int cols; |
74 | unsigned int row_state[SAMSUNG_MAX_COLS]; | 77 | unsigned int row_state[SAMSUNG_MAX_COLS]; |
78 | #ifdef CONFIG_OF | ||
79 | int row_gpios[SAMSUNG_MAX_ROWS]; | ||
80 | int col_gpios[SAMSUNG_MAX_COLS]; | ||
81 | #endif | ||
75 | unsigned short keycodes[]; | 82 | unsigned short keycodes[]; |
76 | }; | 83 | }; |
77 | 84 | ||
78 | static int samsung_keypad_is_s5pv210(struct device *dev) | ||
79 | { | ||
80 | struct platform_device *pdev = to_platform_device(dev); | ||
81 | enum samsung_keypad_type type = | ||
82 | platform_get_device_id(pdev)->driver_data; | ||
83 | |||
84 | return type == KEYPAD_TYPE_S5PV210; | ||
85 | } | ||
86 | |||
87 | static void samsung_keypad_scan(struct samsung_keypad *keypad, | 85 | static void samsung_keypad_scan(struct samsung_keypad *keypad, |
88 | unsigned int *row_state) | 86 | unsigned int *row_state) |
89 | { | 87 | { |
90 | struct device *dev = keypad->input_dev->dev.parent; | ||
91 | unsigned int col; | 88 | unsigned int col; |
92 | unsigned int val; | 89 | unsigned int val; |
93 | 90 | ||
94 | for (col = 0; col < keypad->cols; col++) { | 91 | for (col = 0; col < keypad->cols; col++) { |
95 | if (samsung_keypad_is_s5pv210(dev)) { | 92 | if (keypad->type == KEYPAD_TYPE_S5PV210) { |
96 | val = S5PV210_KEYIFCOLEN_MASK; | 93 | val = S5PV210_KEYIFCOLEN_MASK; |
97 | val &= ~(1 << col) << 8; | 94 | val &= ~(1 << col) << 8; |
98 | } else { | 95 | } else { |
@@ -235,6 +232,126 @@ static void samsung_keypad_close(struct input_dev *input_dev) | |||
235 | samsung_keypad_stop(keypad); | 232 | samsung_keypad_stop(keypad); |
236 | } | 233 | } |
237 | 234 | ||
235 | #ifdef CONFIG_OF | ||
236 | static struct samsung_keypad_platdata *samsung_keypad_parse_dt( | ||
237 | struct device *dev) | ||
238 | { | ||
239 | struct samsung_keypad_platdata *pdata; | ||
240 | struct matrix_keymap_data *keymap_data; | ||
241 | uint32_t *keymap, num_rows = 0, num_cols = 0; | ||
242 | struct device_node *np = dev->of_node, *key_np; | ||
243 | unsigned int key_count = 0; | ||
244 | |||
245 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
246 | if (!pdata) { | ||
247 | dev_err(dev, "could not allocate memory for platform data\n"); | ||
248 | return NULL; | ||
249 | } | ||
250 | |||
251 | of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows); | ||
252 | of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols); | ||
253 | if (!num_rows || !num_cols) { | ||
254 | dev_err(dev, "number of keypad rows/columns not specified\n"); | ||
255 | return NULL; | ||
256 | } | ||
257 | pdata->rows = num_rows; | ||
258 | pdata->cols = num_cols; | ||
259 | |||
260 | keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL); | ||
261 | if (!keymap_data) { | ||
262 | dev_err(dev, "could not allocate memory for keymap data\n"); | ||
263 | return NULL; | ||
264 | } | ||
265 | pdata->keymap_data = keymap_data; | ||
266 | |||
267 | for_each_child_of_node(np, key_np) | ||
268 | key_count++; | ||
269 | |||
270 | keymap_data->keymap_size = key_count; | ||
271 | keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL); | ||
272 | if (!keymap) { | ||
273 | dev_err(dev, "could not allocate memory for keymap\n"); | ||
274 | return NULL; | ||
275 | } | ||
276 | keymap_data->keymap = keymap; | ||
277 | |||
278 | for_each_child_of_node(np, key_np) { | ||
279 | u32 row, col, key_code; | ||
280 | of_property_read_u32(key_np, "keypad,row", &row); | ||
281 | of_property_read_u32(key_np, "keypad,column", &col); | ||
282 | of_property_read_u32(key_np, "linux,code", &key_code); | ||
283 | *keymap++ = KEY(row, col, key_code); | ||
284 | } | ||
285 | |||
286 | if (of_get_property(np, "linux,input-no-autorepeat", NULL)) | ||
287 | pdata->no_autorepeat = true; | ||
288 | if (of_get_property(np, "linux,input-wakeup", NULL)) | ||
289 | pdata->wakeup = true; | ||
290 | |||
291 | return pdata; | ||
292 | } | ||
293 | |||
294 | static void samsung_keypad_parse_dt_gpio(struct device *dev, | ||
295 | struct samsung_keypad *keypad) | ||
296 | { | ||
297 | struct device_node *np = dev->of_node; | ||
298 | int gpio, ret, row, col; | ||
299 | |||
300 | for (row = 0; row < keypad->rows; row++) { | ||
301 | gpio = of_get_named_gpio(np, "row-gpios", row); | ||
302 | keypad->row_gpios[row] = gpio; | ||
303 | if (!gpio_is_valid(gpio)) { | ||
304 | dev_err(dev, "keypad row[%d]: invalid gpio %d\n", | ||
305 | row, gpio); | ||
306 | continue; | ||
307 | } | ||
308 | |||
309 | ret = gpio_request(gpio, "keypad-row"); | ||
310 | if (ret) | ||
311 | dev_err(dev, "keypad row[%d] gpio request failed\n", | ||
312 | row); | ||
313 | } | ||
314 | |||
315 | for (col = 0; col < keypad->cols; col++) { | ||
316 | gpio = of_get_named_gpio(np, "col-gpios", col); | ||
317 | keypad->col_gpios[col] = gpio; | ||
318 | if (!gpio_is_valid(gpio)) { | ||
319 | dev_err(dev, "keypad column[%d]: invalid gpio %d\n", | ||
320 | col, gpio); | ||
321 | continue; | ||
322 | } | ||
323 | |||
324 | ret = gpio_request(gpio, "keypad-col"); | ||
325 | if (ret) | ||
326 | dev_err(dev, "keypad column[%d] gpio request failed\n", | ||
327 | col); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) | ||
332 | { | ||
333 | int cnt; | ||
334 | |||
335 | for (cnt = 0; cnt < keypad->rows; cnt++) | ||
336 | if (gpio_is_valid(keypad->row_gpios[cnt])) | ||
337 | gpio_free(keypad->row_gpios[cnt]); | ||
338 | |||
339 | for (cnt = 0; cnt < keypad->cols; cnt++) | ||
340 | if (gpio_is_valid(keypad->col_gpios[cnt])) | ||
341 | gpio_free(keypad->col_gpios[cnt]); | ||
342 | } | ||
343 | #else | ||
344 | static | ||
345 | struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev) | ||
346 | { | ||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) | ||
351 | { | ||
352 | } | ||
353 | #endif | ||
354 | |||
238 | static int __devinit samsung_keypad_probe(struct platform_device *pdev) | 355 | static int __devinit samsung_keypad_probe(struct platform_device *pdev) |
239 | { | 356 | { |
240 | const struct samsung_keypad_platdata *pdata; | 357 | const struct samsung_keypad_platdata *pdata; |
@@ -246,7 +363,10 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
246 | unsigned int keymap_size; | 363 | unsigned int keymap_size; |
247 | int error; | 364 | int error; |
248 | 365 | ||
249 | pdata = pdev->dev.platform_data; | 366 | if (pdev->dev.of_node) |
367 | pdata = samsung_keypad_parse_dt(&pdev->dev); | ||
368 | else | ||
369 | pdata = pdev->dev.platform_data; | ||
250 | if (!pdata) { | 370 | if (!pdata) { |
251 | dev_err(&pdev->dev, "no platform data defined\n"); | 371 | dev_err(&pdev->dev, "no platform data defined\n"); |
252 | return -EINVAL; | 372 | return -EINVAL; |
@@ -303,6 +423,16 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
303 | keypad->cols = pdata->cols; | 423 | keypad->cols = pdata->cols; |
304 | init_waitqueue_head(&keypad->wait); | 424 | init_waitqueue_head(&keypad->wait); |
305 | 425 | ||
426 | if (pdev->dev.of_node) { | ||
427 | #ifdef CONFIG_OF | ||
428 | samsung_keypad_parse_dt_gpio(&pdev->dev, keypad); | ||
429 | keypad->type = of_device_is_compatible(pdev->dev.of_node, | ||
430 | "samsung,s5pv210-keypad"); | ||
431 | #endif | ||
432 | } else { | ||
433 | keypad->type = platform_get_device_id(pdev)->driver_data; | ||
434 | } | ||
435 | |||
306 | input_dev->name = pdev->name; | 436 | input_dev->name = pdev->name; |
307 | input_dev->id.bustype = BUS_HOST; | 437 | input_dev->id.bustype = BUS_HOST; |
308 | input_dev->dev.parent = &pdev->dev; | 438 | input_dev->dev.parent = &pdev->dev; |
@@ -343,12 +473,19 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
343 | 473 | ||
344 | device_init_wakeup(&pdev->dev, pdata->wakeup); | 474 | device_init_wakeup(&pdev->dev, pdata->wakeup); |
345 | platform_set_drvdata(pdev, keypad); | 475 | platform_set_drvdata(pdev, keypad); |
476 | |||
477 | if (pdev->dev.of_node) { | ||
478 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); | ||
479 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data); | ||
480 | devm_kfree(&pdev->dev, (void *)pdata); | ||
481 | } | ||
346 | return 0; | 482 | return 0; |
347 | 483 | ||
348 | err_free_irq: | 484 | err_free_irq: |
349 | free_irq(keypad->irq, keypad); | 485 | free_irq(keypad->irq, keypad); |
350 | err_put_clk: | 486 | err_put_clk: |
351 | clk_put(keypad->clk); | 487 | clk_put(keypad->clk); |
488 | samsung_keypad_dt_gpio_free(keypad); | ||
352 | err_unmap_base: | 489 | err_unmap_base: |
353 | iounmap(keypad->base); | 490 | iounmap(keypad->base); |
354 | err_free_mem: | 491 | err_free_mem: |
@@ -374,6 +511,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
374 | free_irq(keypad->irq, keypad); | 511 | free_irq(keypad->irq, keypad); |
375 | 512 | ||
376 | clk_put(keypad->clk); | 513 | clk_put(keypad->clk); |
514 | samsung_keypad_dt_gpio_free(keypad); | ||
377 | 515 | ||
378 | iounmap(keypad->base); | 516 | iounmap(keypad->base); |
379 | kfree(keypad); | 517 | kfree(keypad); |
@@ -447,6 +585,17 @@ static const struct dev_pm_ops samsung_keypad_pm_ops = { | |||
447 | }; | 585 | }; |
448 | #endif | 586 | #endif |
449 | 587 | ||
588 | #ifdef CONFIG_OF | ||
589 | static const struct of_device_id samsung_keypad_dt_match[] = { | ||
590 | { .compatible = "samsung,s3c6410-keypad" }, | ||
591 | { .compatible = "samsung,s5pv210-keypad" }, | ||
592 | {}, | ||
593 | }; | ||
594 | MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); | ||
595 | #else | ||
596 | #define samsung_keypad_dt_match NULL | ||
597 | #endif | ||
598 | |||
450 | static struct platform_device_id samsung_keypad_driver_ids[] = { | 599 | static struct platform_device_id samsung_keypad_driver_ids[] = { |
451 | { | 600 | { |
452 | .name = "samsung-keypad", | 601 | .name = "samsung-keypad", |
@@ -465,6 +614,7 @@ static struct platform_driver samsung_keypad_driver = { | |||
465 | .driver = { | 614 | .driver = { |
466 | .name = "samsung-keypad", | 615 | .name = "samsung-keypad", |
467 | .owner = THIS_MODULE, | 616 | .owner = THIS_MODULE, |
617 | .of_match_table = samsung_keypad_dt_match, | ||
468 | #ifdef CONFIG_PM | 618 | #ifdef CONFIG_PM |
469 | .pm = &samsung_keypad_pm_ops, | 619 | .pm = &samsung_keypad_pm_ops, |
470 | #endif | 620 | #endif |