Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Daniel Tang and committed by
Dmitry Torokhov
fc4f3146 9b5f953d

+356
+60
Documentation/devicetree/bindings/input/ti,nspire-keypad.txt
··· 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 + };
+10
drivers/input/keyboard/Kconfig
··· 418 418 To compile this driver as a module, choose M here: the 419 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 431 config KEYBOARD_TEGRA 422 432 tristate "NVIDIA Tegra internal matrix keyboard controller support" 423 433 depends on ARCH_TEGRA && OF
+1
drivers/input/keyboard/Makefile
··· 35 35 obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o 36 36 obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o 37 37 obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o 38 + obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o 38 39 obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o 39 40 obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o 40 41 obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
+285
drivers/input/keyboard/nspire-keypad.c
··· 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");