diff options
author | Alexander Shiyan <shc_work@mail.ru> | 2014-03-28 12:37:14 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-03-30 16:24:55 -0400 |
commit | e70f18e1c7868bebad16b21b4821b2adb8c1abf8 (patch) | |
tree | 9d02b52fb41a729a4188f1fb9db904fbe083fa9c /drivers/input/keyboard | |
parent | fd335ab04b3f1b3309dfbfea71a1a79a7bacc4ad (diff) |
Input: add new driver for ARM CLPS711X keypad
This patch adds a new driver for keypad for Cirrus Logic CLPS711X CPUs.
Target CPU contain keyboard interface which can scan 8 column lines,
so we can read row GPIOs to read status and determine asserted state.
Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/clps711x-keypad.c | 207 |
3 files changed, 220 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 935dcaf16646..76842d7dc2e3 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -151,6 +151,18 @@ config KEYBOARD_BFIN | |||
151 | To compile this driver as a module, choose M here: the | 151 | To compile this driver as a module, choose M here: the |
152 | module will be called bf54x-keys. | 152 | module will be called bf54x-keys. |
153 | 153 | ||
154 | config KEYBOARD_CLPS711X | ||
155 | tristate "CLPS711X Keypad support" | ||
156 | depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST) | ||
157 | select INPUT_MATRIXKMAP | ||
158 | select INPUT_POLLDEV | ||
159 | help | ||
160 | Say Y here to enable the matrix keypad on the Cirrus Logic | ||
161 | CLPS711X CPUs. | ||
162 | |||
163 | To compile this driver as a module, choose M here: the | ||
164 | module will be called clps711x-keypad. | ||
165 | |||
154 | config KEYBOARD_LKKBD | 166 | config KEYBOARD_LKKBD |
155 | tristate "DECstation/VAXstation LK201/LK401 keyboard" | 167 | tristate "DECstation/VAXstation LK201/LK401 keyboard" |
156 | select SERIO | 168 | select SERIO |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 81014d94a0c1..11cff7b84b47 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o | |||
11 | obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o | 11 | obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o |
12 | obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o | 12 | obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o |
13 | obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o | 13 | obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o |
14 | obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o | ||
14 | obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o | 15 | obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o |
15 | obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o | 16 | obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o |
16 | obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o | 17 | obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o |
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c new file mode 100644 index 000000000000..3955aecee44b --- /dev/null +++ b/drivers/input/keyboard/clps711x-keypad.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Cirrus Logic CLPS711X Keypad driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> | ||
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; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/input.h> | ||
13 | #include <linux/input-polldev.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of_gpio.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/regmap.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/input/matrix_keypad.h> | ||
20 | #include <linux/mfd/syscon.h> | ||
21 | #include <linux/mfd/syscon/clps711x.h> | ||
22 | |||
23 | #define CLPS711X_KEYPAD_COL_COUNT 8 | ||
24 | |||
25 | struct clps711x_gpio_data { | ||
26 | struct gpio_desc *desc; | ||
27 | DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT); | ||
28 | }; | ||
29 | |||
30 | struct clps711x_keypad_data { | ||
31 | struct regmap *syscon; | ||
32 | int row_count; | ||
33 | unsigned int row_shift; | ||
34 | struct clps711x_gpio_data *gpio_data; | ||
35 | }; | ||
36 | |||
37 | static void clps711x_keypad_poll(struct input_polled_dev *dev) | ||
38 | { | ||
39 | const unsigned short *keycodes = dev->input->keycode; | ||
40 | struct clps711x_keypad_data *priv = dev->private; | ||
41 | bool sync = false; | ||
42 | int col, row; | ||
43 | |||
44 | for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) { | ||
45 | /* Assert column */ | ||
46 | regmap_update_bits(priv->syscon, SYSCON_OFFSET, | ||
47 | SYSCON1_KBDSCAN_MASK, | ||
48 | SYSCON1_KBDSCAN(8 + col)); | ||
49 | |||
50 | /* Scan rows */ | ||
51 | for (row = 0; row < priv->row_count; row++) { | ||
52 | struct clps711x_gpio_data *data = &priv->gpio_data[row]; | ||
53 | bool state, state1; | ||
54 | |||
55 | /* Read twice for protection against fluctuations */ | ||
56 | do { | ||
57 | state = gpiod_get_value_cansleep(data->desc); | ||
58 | cond_resched(); | ||
59 | state1 = gpiod_get_value_cansleep(data->desc); | ||
60 | } while (state != state1); | ||
61 | |||
62 | if (test_bit(col, data->last_state) != state) { | ||
63 | int code = MATRIX_SCAN_CODE(row, col, | ||
64 | priv->row_shift); | ||
65 | |||
66 | if (state) { | ||
67 | set_bit(col, data->last_state); | ||
68 | input_event(dev->input, EV_MSC, | ||
69 | MSC_SCAN, code); | ||
70 | } else { | ||
71 | clear_bit(col, data->last_state); | ||
72 | } | ||
73 | |||
74 | if (keycodes[code]) | ||
75 | input_report_key(dev->input, | ||
76 | keycodes[code], state); | ||
77 | sync = true; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /* Set all columns to low */ | ||
82 | regmap_update_bits(priv->syscon, SYSCON_OFFSET, | ||
83 | SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1)); | ||
84 | } | ||
85 | |||
86 | if (sync) | ||
87 | input_sync(dev->input); | ||
88 | } | ||
89 | |||
90 | static int clps711x_keypad_probe(struct platform_device *pdev) | ||
91 | { | ||
92 | struct clps711x_keypad_data *priv; | ||
93 | struct device *dev = &pdev->dev; | ||
94 | struct device_node *np = dev->of_node; | ||
95 | struct input_polled_dev *poll_dev; | ||
96 | u32 poll_interval; | ||
97 | int i, err; | ||
98 | |||
99 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
100 | if (!priv) | ||
101 | return -ENOMEM; | ||
102 | |||
103 | priv->syscon = | ||
104 | syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); | ||
105 | if (IS_ERR(priv->syscon)) | ||
106 | return PTR_ERR(priv->syscon); | ||
107 | |||
108 | priv->row_count = of_gpio_named_count(np, "row-gpios"); | ||
109 | if (priv->row_count < 1) | ||
110 | return -EINVAL; | ||
111 | |||
112 | priv->gpio_data = devm_kzalloc(dev, | ||
113 | sizeof(*priv->gpio_data) * priv->row_count, | ||
114 | GFP_KERNEL); | ||
115 | if (!priv->gpio_data) | ||
116 | return -ENOMEM; | ||
117 | |||
118 | priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT); | ||
119 | |||
120 | for (i = 0; i < priv->row_count; i++) { | ||
121 | struct clps711x_gpio_data *data = &priv->gpio_data[i]; | ||
122 | |||
123 | data->desc = devm_gpiod_get_index(dev, "row", i); | ||
124 | if (!data->desc) | ||
125 | return -EINVAL; | ||
126 | |||
127 | if (IS_ERR(data->desc)) | ||
128 | return PTR_ERR(data->desc); | ||
129 | |||
130 | gpiod_direction_input(data->desc); | ||
131 | } | ||
132 | |||
133 | err = of_property_read_u32(np, "poll-interval", &poll_interval); | ||
134 | if (err) | ||
135 | return err; | ||
136 | |||
137 | poll_dev = input_allocate_polled_device(); | ||
138 | if (!poll_dev) | ||
139 | return -ENOMEM; | ||
140 | |||
141 | poll_dev->private = priv; | ||
142 | poll_dev->poll = clps711x_keypad_poll; | ||
143 | poll_dev->poll_interval = poll_interval; | ||
144 | poll_dev->input->name = pdev->name; | ||
145 | poll_dev->input->dev.parent = dev; | ||
146 | poll_dev->input->id.bustype = BUS_HOST; | ||
147 | poll_dev->input->id.vendor = 0x0001; | ||
148 | poll_dev->input->id.product = 0x0001; | ||
149 | poll_dev->input->id.version = 0x0100; | ||
150 | |||
151 | err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count, | ||
152 | CLPS711X_KEYPAD_COL_COUNT, | ||
153 | NULL, poll_dev->input); | ||
154 | if (err) | ||
155 | goto out_err; | ||
156 | |||
157 | input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN); | ||
158 | if (of_property_read_bool(np, "autorepeat")) | ||
159 | __set_bit(EV_REP, poll_dev->input->evbit); | ||
160 | |||
161 | platform_set_drvdata(pdev, poll_dev); | ||
162 | |||
163 | /* Set all columns to low */ | ||
164 | regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK, | ||
165 | SYSCON1_KBDSCAN(1)); | ||
166 | |||
167 | err = input_register_polled_device(poll_dev); | ||
168 | if (err) | ||
169 | goto out_err; | ||
170 | |||
171 | return 0; | ||
172 | |||
173 | out_err: | ||
174 | input_free_polled_device(poll_dev); | ||
175 | return err; | ||
176 | } | ||
177 | |||
178 | static int clps711x_keypad_remove(struct platform_device *pdev) | ||
179 | { | ||
180 | struct input_polled_dev *poll_dev = platform_get_drvdata(pdev); | ||
181 | |||
182 | input_unregister_polled_device(poll_dev); | ||
183 | input_free_polled_device(poll_dev); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static struct of_device_id clps711x_keypad_of_match[] = { | ||
189 | { .compatible = "cirrus,clps711x-keypad", }, | ||
190 | { } | ||
191 | }; | ||
192 | MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); | ||
193 | |||
194 | static struct platform_driver clps711x_keypad_driver = { | ||
195 | .driver = { | ||
196 | .name = "clps711x-keypad", | ||
197 | .owner = THIS_MODULE, | ||
198 | .of_match_table = clps711x_keypad_of_match, | ||
199 | }, | ||
200 | .probe = clps711x_keypad_probe, | ||
201 | .remove = clps711x_keypad_remove, | ||
202 | }; | ||
203 | module_platform_driver(clps711x_keypad_driver); | ||
204 | |||
205 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); | ||
206 | MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver"); | ||
207 | MODULE_LICENSE("GPL"); | ||