diff options
| author | Daniel Tang <dt.tangr@gmail.com> | 2013-05-30 16:16:10 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-06-04 12:17:25 -0400 |
| commit | fc4f314618923c2bef708a535f8483fa7f7dbad2 (patch) | |
| tree | afc0f99990ef6fa758bfad84d83ff1ff6e1ef693 | |
| parent | 9b5f953ddc3247dd0c3d0cafa4247be10d5c05db (diff) | |
Input: add TI-Nspire keypad support
This is a driver for the keypads found on the TI-Nspire series calculators.
Signed-off-by: Daniel Tang <dt.tangr@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
| -rw-r--r-- | Documentation/devicetree/bindings/input/ti,nspire-keypad.txt | 60 | ||||
| -rw-r--r-- | drivers/input/keyboard/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
| -rw-r--r-- | drivers/input/keyboard/nspire-keypad.c | 285 |
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 @@ | |||
| 1 | TI-NSPIRE Keypad | ||
| 2 | |||
| 3 | Required 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 | |||
| 21 | Optional properties: | ||
| 22 | - active-low: Specify that the keypad is active low (i.e. logical low signifies | ||
| 23 | a key press). | ||
| 24 | |||
| 25 | Example: | ||
| 26 | |||
| 27 | input { | ||
| 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 | ||
| 421 | config 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 | |||
| 421 | config KEYBOARD_TEGRA | 431 | config 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 | |||
| 35 | obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o | 35 | obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o |
| 36 | obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o | 36 | obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o |
| 37 | obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o | 37 | obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o |
| 38 | obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o | ||
| 38 | obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o | 39 | obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o |
| 39 | obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o | 40 | obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o |
| 40 | obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o | 41 | obj-$(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 | |||
| 34 | struct 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 | |||
| 53 | static 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 | |||
| 99 | static 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 | |||
| 136 | static 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 | |||
| 152 | static 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 | |||
| 159 | static 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 | |||
| 267 | static const struct of_device_id nspire_keypad_dt_match[] = { | ||
| 268 | { .compatible = "ti,nspire-keypad" }, | ||
| 269 | { }, | ||
| 270 | }; | ||
| 271 | MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match); | ||
| 272 | |||
| 273 | static 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 | |||
| 282 | module_platform_driver(nspire_keypad_driver); | ||
| 283 | |||
| 284 | MODULE_LICENSE("GPL"); | ||
| 285 | MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver"); | ||
