aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/input/ti,nspire-keypad.txt60
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/nspire-keypad.c285
4 files changed, 356 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/ti,nspire-keypad.txt b/Documentation/devicetree/bindings/input/ti,nspire-keypad.txt
new file mode 100644
index 000000000000..513d94d6e899
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ti,nspire-keypad.txt
@@ -0,0 +1,60 @@
1TI-NSPIRE Keypad
2
3Required properties:
4- compatible: Compatible property value should be "ti,nspire-keypad".
5
6- reg: Physical base address of the peripheral and length of memory mapped
7 region.
8
9- interrupts: The interrupt number for the peripheral.
10
11- scan-interval: How often to scan in us. Based on a APB speed of 33MHz, the
12 maximum and minimum delay time is ~2000us and ~500us respectively
13
14- row-delay: How long to wait before scanning each row.
15
16- clocks: The clock this peripheral is attached to.
17
18- linux,keymap: The keymap to use
19 (see Documentation/devicetree/bindings/input/matrix-keymap.txt)
20
21Optional properties:
22- active-low: Specify that the keypad is active low (i.e. logical low signifies
23 a key press).
24
25Example:
26
27input {
28 compatible = "ti,nspire-keypad";
29 reg = <0x900E0000 0x1000>;
30 interrupts = <16>;
31
32 scan-interval = <1000>;
33 row-delay = <200>;
34
35 clocks = <&apb_pclk>;
36
37 linux,keymap = <
38 0x0000001c 0x0001001c 0x00040039
39 0x0005002c 0x00060015 0x0007000b
40 0x0008000f 0x0100002d 0x01010011
41 0x0102002f 0x01030004 0x01040016
42 0x01050014 0x0106001f 0x01070002
43 0x010a006a 0x02000013 0x02010010
44 0x02020019 0x02030007 0x02040018
45 0x02050031 0x02060032 0x02070005
46 0x02080028 0x0209006c 0x03000026
47 0x03010025 0x03020024 0x0303000a
48 0x03040017 0x03050023 0x03060022
49 0x03070008 0x03080035 0x03090069
50 0x04000021 0x04010012 0x04020020
51 0x0404002e 0x04050030 0x0406001e
52 0x0407000d 0x04080037 0x04090067
53 0x05010038 0x0502000c 0x0503001b
54 0x05040034 0x0505001a 0x05060006
55 0x05080027 0x0509000e 0x050a006f
56 0x0600002b 0x0602004e 0x06030068
57 0x06040003 0x0605006d 0x06060009
58 0x06070001 0x0609000f 0x0708002a
59 0x0709001d 0x070a0033 >;
60};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index ac0500667000..37c366623fc0 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -418,6 +418,16 @@ config KEYBOARD_NOMADIK
418 To compile this driver as a module, choose M here: the 418 To compile this driver as a module, choose M here: the
419 module will be called nmk-ske-keypad. 419 module will be called nmk-ske-keypad.
420 420
421config KEYBOARD_NSPIRE
422 tristate "TI-NSPIRE built-in keyboard"
423 depends on ARCH_NSPIRE && OF
424 select INPUT_MATRIXKMAP
425 help
426 Say Y here if you want to use the built-in keypad on TI-NSPIRE.
427
428 To compile this driver as a module, choose M here: the
429 module will be called nspire-keypad.
430
421config KEYBOARD_TEGRA 431config KEYBOARD_TEGRA
422 tristate "NVIDIA Tegra internal matrix keyboard controller support" 432 tristate "NVIDIA Tegra internal matrix keyboard controller support"
423 depends on ARCH_TEGRA && OF 433 depends on ARCH_TEGRA && OF
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 49b16453d00e..89d997b05452 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
35obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o 35obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
36obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o 36obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
37obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o 37obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
38obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
38obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o 39obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
39obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o 40obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
40obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o 41obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c
new file mode 100644
index 000000000000..1b0d04c68d45
--- /dev/null
+++ b/drivers/input/keyboard/nspire-keypad.c
@@ -0,0 +1,285 @@
1/*
2 * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2, as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/input/matrix_keypad.h>
10#include <linux/platform_device.h>
11#include <linux/interrupt.h>
12#include <linux/io.h>
13#include <linux/delay.h>
14#include <linux/input.h>
15#include <linux/slab.h>
16#include <linux/clk.h>
17#include <linux/module.h>
18#include <linux/of.h>
19
20#define KEYPAD_SCAN_MODE 0x00
21#define KEYPAD_CNTL 0x04
22#define KEYPAD_INT 0x08
23#define KEYPAD_INTMSK 0x0C
24
25#define KEYPAD_DATA 0x10
26#define KEYPAD_GPIO 0x30
27
28#define KEYPAD_UNKNOWN_INT 0x40
29#define KEYPAD_UNKNOWN_INT_STS 0x44
30
31#define KEYPAD_BITMASK_COLS 11
32#define KEYPAD_BITMASK_ROWS 8
33
34struct nspire_keypad {
35 void __iomem *reg_base;
36 u32 int_mask;
37
38 struct input_dev *input;
39 struct clk *clk;
40
41 struct matrix_keymap_data *keymap;
42 int row_shift;
43
44 /* Maximum delay estimated assuming 33MHz APB */
45 u32 scan_interval; /* In microseconds (~2000us max) */
46 u32 row_delay; /* In microseconds (~500us max) */
47
48 u16 state[KEYPAD_BITMASK_ROWS];
49
50 bool active_low;
51};
52
53static irqreturn_t nspire_keypad_irq(int irq, void *dev_id)
54{
55 struct nspire_keypad *keypad = dev_id;
56 struct input_dev *input = keypad->input;
57 unsigned short *keymap = input->keycode;
58 unsigned int code;
59 int row, col;
60 u32 int_sts;
61 u16 state[8];
62 u16 bits, changed;
63
64 int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask;
65 if (!int_sts)
66 return IRQ_NONE;
67
68 memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state));
69
70 for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) {
71 bits = state[row];
72 if (keypad->active_low)
73 bits = ~bits;
74
75 changed = bits ^ keypad->state[row];
76 if (!changed)
77 continue;
78
79 keypad->state[row] = bits;
80
81 for (col = 0; col < KEYPAD_BITMASK_COLS; col++) {
82 if (!(changed & (1U << col)))
83 continue;
84
85 code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
86 input_event(input, EV_MSC, MSC_SCAN, code);
87 input_report_key(input, keymap[code],
88 bits & (1U << col));
89 }
90 }
91
92 input_sync(input);
93
94 writel(0x3, keypad->reg_base + KEYPAD_INT);
95
96 return IRQ_HANDLED;
97}
98
99static int nspire_keypad_chip_init(struct nspire_keypad *keypad)
100{
101 unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles;
102
103 cycles_per_us = (clk_get_rate(keypad->clk) / 1000000);
104 if (cycles_per_us == 0)
105 cycles_per_us = 1;
106
107 delay_cycles = cycles_per_us * keypad->scan_interval;
108 WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */
109 delay_cycles &= 0xffff;
110
111 row_delay_cycles = cycles_per_us * keypad->row_delay;
112 WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */
113 row_delay_cycles &= 0x3fff;
114
115 val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */
116 val |= row_delay_cycles << 2; /* Delay between scanning each row */
117 val |= delay_cycles << 16; /* Delay between scans */
118 writel(val, keypad->reg_base + KEYPAD_SCAN_MODE);
119
120 val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8;
121 writel(val, keypad->reg_base + KEYPAD_CNTL);
122
123 /* Enable interrupts */
124 keypad->int_mask = 1 << 1;
125 writel(keypad->int_mask, keypad->reg_base + 0xc);
126
127 /* Disable GPIO interrupts to prevent hanging on touchpad */
128 /* Possibly used to detect touchpad events */
129 writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT);
130 /* Acknowledge existing interrupts */
131 writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS);
132
133 return 0;
134}
135
136static int nspire_keypad_open(struct input_dev *input)
137{
138 struct nspire_keypad *keypad = input_get_drvdata(input);
139 int error;
140
141 error = clk_prepare_enable(keypad->clk);
142 if (error)
143 return error;
144
145 error = nspire_keypad_chip_init(keypad);
146 if (error)
147 return error;
148
149 return 0;
150}
151
152static void nspire_keypad_close(struct input_dev *input)
153{
154 struct nspire_keypad *keypad = input_get_drvdata(input);
155
156 clk_disable_unprepare(keypad->clk);
157}
158
159static int nspire_keypad_probe(struct platform_device *pdev)
160{
161 const struct device_node *of_node = pdev->dev.of_node;
162 struct nspire_keypad *keypad;
163 struct input_dev *input;
164 struct resource *res;
165 int irq;
166 int error;
167
168 irq = platform_get_irq(pdev, 0);
169 if (irq < 0) {
170 dev_err(&pdev->dev, "failed to get keypad irq\n");
171 return -EINVAL;
172 }
173
174 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
175 if (!res) {
176 dev_err(&pdev->dev, "missing platform resources\n");
177 return -EINVAL;
178 }
179
180 keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad),
181 GFP_KERNEL);
182 if (!keypad) {
183 dev_err(&pdev->dev, "failed to allocate keypad memory\n");
184 return -ENOMEM;
185 }
186
187 keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS);
188
189 error = of_property_read_u32(of_node, "scan-interval",
190 &keypad->scan_interval);
191 if (error) {
192 dev_err(&pdev->dev, "failed to get scan-interval\n");
193 return error;
194 }
195
196 error = of_property_read_u32(of_node, "row-delay",
197 &keypad->row_delay);
198 if (error) {
199 dev_err(&pdev->dev, "failed to get row-delay\n");
200 return error;
201 }
202
203 keypad->active_low = of_property_read_bool(of_node, "active-low");
204
205 keypad->clk = devm_clk_get(&pdev->dev, NULL);
206 if (IS_ERR(keypad->clk)) {
207 dev_err(&pdev->dev, "unable to get clock\n");
208 return PTR_ERR(keypad->clk);
209 }
210
211 keypad->reg_base = devm_ioremap_resource(&pdev->dev, res);
212 if (IS_ERR(keypad->reg_base)) {
213 dev_err(&pdev->dev, "failed to remap I/O memory\n");
214 return PTR_ERR(keypad->reg_base);
215 }
216
217 keypad->input = input = devm_input_allocate_device(&pdev->dev);
218 if (!input) {
219 dev_err(&pdev->dev, "failed to allocate input device\n");
220 return -ENOMEM;
221 }
222
223 input_set_drvdata(input, keypad);
224
225 input->id.bustype = BUS_HOST;
226 input->name = "nspire-keypad";
227 input->open = nspire_keypad_open;
228 input->close = nspire_keypad_close;
229
230 __set_bit(EV_KEY, input->evbit);
231 __set_bit(EV_REP, input->evbit);
232 input_set_capability(input, EV_MSC, MSC_SCAN);
233
234 error = matrix_keypad_build_keymap(NULL, NULL,
235 KEYPAD_BITMASK_ROWS,
236 KEYPAD_BITMASK_COLS,
237 NULL, input);
238 if (error) {
239 dev_err(&pdev->dev, "building keymap failed\n");
240 return error;
241 }
242
243 error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0,
244 "nspire_keypad", keypad);
245 if (error) {
246 dev_err(&pdev->dev, "allocate irq %d failed\n", irq);
247 return error;
248 }
249
250 error = input_register_device(input);
251 if (error) {
252 dev_err(&pdev->dev,
253 "unable to register input device: %d\n", error);
254 return error;
255 }
256
257 platform_set_drvdata(pdev, keypad);
258
259 dev_dbg(&pdev->dev,
260 "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n",
261 res, keypad->row_delay, keypad->scan_interval,
262 keypad->active_low ? ", active_low" : "");
263
264 return 0;
265}
266
267static const struct of_device_id nspire_keypad_dt_match[] = {
268 { .compatible = "ti,nspire-keypad" },
269 { },
270};
271MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match);
272
273static struct platform_driver nspire_keypad_driver = {
274 .driver = {
275 .name = "nspire-keypad",
276 .owner = THIS_MODULE,
277 .of_match_table = of_match_ptr(nspire_keypad_dt_match),
278 },
279 .probe = nspire_keypad_probe,
280};
281
282module_platform_driver(nspire_keypad_driver);
283
284MODULE_LICENSE("GPL");
285MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver");