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

Input: add support for key scan interface of the LPC32xx SoC

This is a driver for the key scan interface of the LPC32xx SoC

Signed-off-by: Roland Stigge <stigge@antcom.de>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Roland Stigge and committed by
Dmitry Torokhov
69690bec 13987435

+433
+28
Documentation/devicetree/bindings/input/lpc32xx-key.txt
··· 1 + NXP LPC32xx Key Scan Interface 2 + 3 + Required Properties: 4 + - compatible: Should be "nxp,lpc3220-key" 5 + - reg: Physical base address of the controller and length of memory mapped 6 + region. 7 + - interrupts: The interrupt number to the cpu. 8 + - keypad,num-rows: Number of rows and columns, e.g. 1: 1x1, 6: 6x6 9 + - keypad,num-columns: Must be equal to keypad,num-rows since LPC32xx only 10 + supports square matrices 11 + - nxp,debounce-delay-ms: Debounce delay in ms 12 + - nxp,scan-delay-ms: Repeated scan period in ms 13 + - linux,keymap: the key-code to be reported when the key is pressed 14 + and released, see also 15 + Documentation/devicetree/bindings/input/matrix-keymap.txt 16 + 17 + Example: 18 + 19 + key@40050000 { 20 + compatible = "nxp,lpc3220-key"; 21 + reg = <0x40050000 0x1000>; 22 + interrupts = <54 0>; 23 + keypad,num-rows = <1>; 24 + keypad,num-columns = <1>; 25 + nxp,debounce-delay-ms = <3>; 26 + nxp,scan-delay-ms = <34>; 27 + linux,keymap = <0x00000002>; 28 + };
+10
drivers/input/keyboard/Kconfig
··· 332 332 To compile this driver as a module, choose M here: the 333 333 module will be called locomokbd. 334 334 335 + config KEYBOARD_LPC32XX 336 + tristate "LPC32XX matrix key scanner support" 337 + depends on ARCH_LPC32XX && OF 338 + help 339 + Say Y here if you want to use NXP LPC32XX SoC key scanner interface, 340 + connected to a key matrix. 341 + 342 + To compile this driver as a module, choose M here: the 343 + module will be called lpc32xx-keys. 344 + 335 345 config KEYBOARD_MAPLE 336 346 tristate "Maple bus keyboard" 337 347 depends on SH_DREAMCAST && MAPLE
+1
drivers/input/keyboard/Makefile
··· 26 26 obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o 27 27 obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o 28 28 obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o 29 + obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o 29 30 obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o 30 31 obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o 31 32 obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
+394
drivers/input/keyboard/lpc32xx-keys.c
··· 1 + /* 2 + * NXP LPC32xx SoC Key Scan Interface 3 + * 4 + * Authors: 5 + * Kevin Wells <kevin.wells@nxp.com> 6 + * Roland Stigge <stigge@antcom.de> 7 + * 8 + * Copyright (C) 2010 NXP Semiconductors 9 + * Copyright (C) 2012 Roland Stigge 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of the GNU General Public License as published by 13 + * the Free Software Foundation; either version 2 of the License, or 14 + * (at your option) any later version. 15 + * 16 + * This program is distributed in the hope that it will be useful, 17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 + * GNU General Public License for more details. 20 + * 21 + * 22 + * This controller supports square key matrices from 1x1 up to 8x8 23 + */ 24 + 25 + #include <linux/module.h> 26 + #include <linux/interrupt.h> 27 + #include <linux/slab.h> 28 + #include <linux/irq.h> 29 + #include <linux/pm.h> 30 + #include <linux/platform_device.h> 31 + #include <linux/input.h> 32 + #include <linux/clk.h> 33 + #include <linux/io.h> 34 + #include <linux/of.h> 35 + #include <linux/input/matrix_keypad.h> 36 + 37 + #define DRV_NAME "lpc32xx_keys" 38 + 39 + /* 40 + * Key scanner register offsets 41 + */ 42 + #define LPC32XX_KS_DEB(x) ((x) + 0x00) 43 + #define LPC32XX_KS_STATE_COND(x) ((x) + 0x04) 44 + #define LPC32XX_KS_IRQ(x) ((x) + 0x08) 45 + #define LPC32XX_KS_SCAN_CTL(x) ((x) + 0x0C) 46 + #define LPC32XX_KS_FAST_TST(x) ((x) + 0x10) 47 + #define LPC32XX_KS_MATRIX_DIM(x) ((x) + 0x14) /* 1..8 */ 48 + #define LPC32XX_KS_DATA(x, y) ((x) + 0x40 + ((y) << 2)) 49 + 50 + #define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n) ((n) & 0xFF) 51 + 52 + #define LPC32XX_KSCAN_SCOND_IN_IDLE 0x0 53 + #define LPC32XX_KSCAN_SCOND_IN_SCANONCE 0x1 54 + #define LPC32XX_KSCAN_SCOND_IN_IRQGEN 0x2 55 + #define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX 0x3 56 + 57 + #define LPC32XX_KSCAN_IRQ_PENDING_CLR 0x1 58 + 59 + #define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n) ((n) & 0xFF) 60 + 61 + #define LPC32XX_KSCAN_FTST_FORCESCANONCE 0x1 62 + #define LPC32XX_KSCAN_FTST_USE32K_CLK 0x2 63 + 64 + #define LPC32XX_KSCAN_MSEL_SELECT(n) ((n) & 0xF) 65 + 66 + struct lpc32xx_kscan_drv { 67 + struct input_dev *input; 68 + struct clk *clk; 69 + struct resource *iores; 70 + void __iomem *kscan_base; 71 + unsigned int irq; 72 + 73 + u32 matrix_sz; /* Size of matrix in XxY, ie. 3 = 3x3 */ 74 + u32 deb_clks; /* Debounce clocks (based on 32KHz clock) */ 75 + u32 scan_delay; /* Scan delay (based on 32KHz clock) */ 76 + 77 + unsigned short *keymap; /* Pointer to key map for the scan matrix */ 78 + unsigned int row_shift; 79 + 80 + u8 lastkeystates[8]; 81 + }; 82 + 83 + static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col) 84 + { 85 + struct input_dev *input = kscandat->input; 86 + unsigned row, changed, scancode, keycode; 87 + u8 key; 88 + 89 + key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col)); 90 + changed = key ^ kscandat->lastkeystates[col]; 91 + kscandat->lastkeystates[col] = key; 92 + 93 + for (row = 0; changed; row++, changed >>= 1) { 94 + if (changed & 1) { 95 + /* Key state changed, signal an event */ 96 + scancode = MATRIX_SCAN_CODE(row, col, 97 + kscandat->row_shift); 98 + keycode = kscandat->keymap[scancode]; 99 + input_event(input, EV_MSC, MSC_SCAN, scancode); 100 + input_report_key(input, keycode, key & (1 << row)); 101 + } 102 + } 103 + } 104 + 105 + static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id) 106 + { 107 + struct lpc32xx_kscan_drv *kscandat = dev_id; 108 + int i; 109 + 110 + for (i = 0; i < kscandat->matrix_sz; i++) 111 + lpc32xx_mod_states(kscandat, i); 112 + 113 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 114 + 115 + input_sync(kscandat->input); 116 + 117 + return IRQ_HANDLED; 118 + } 119 + 120 + static int lpc32xx_kscan_open(struct input_dev *dev) 121 + { 122 + struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev); 123 + int error; 124 + 125 + error = clk_prepare_enable(kscandat->clk); 126 + if (error) 127 + return error; 128 + 129 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 130 + 131 + return 0; 132 + } 133 + 134 + static void lpc32xx_kscan_close(struct input_dev *dev) 135 + { 136 + struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev); 137 + 138 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 139 + clk_disable_unprepare(kscandat->clk); 140 + } 141 + 142 + static int __devinit lpc32xx_parse_dt(struct device *dev, 143 + struct lpc32xx_kscan_drv *kscandat) 144 + { 145 + struct device_node *np = dev->of_node; 146 + u32 rows = 0, columns = 0; 147 + 148 + of_property_read_u32(np, "keypad,num-rows", &rows); 149 + of_property_read_u32(np, "keypad,num-columns", &columns); 150 + if (!rows || rows != columns) { 151 + dev_err(dev, 152 + "rows and columns must be specified and be equal!\n"); 153 + return -EINVAL; 154 + } 155 + 156 + kscandat->matrix_sz = rows; 157 + kscandat->row_shift = get_count_order(columns); 158 + 159 + of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks); 160 + of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay); 161 + if (!kscandat->deb_clks || !kscandat->scan_delay) { 162 + dev_err(dev, "debounce or scan delay not specified\n"); 163 + return -EINVAL; 164 + } 165 + 166 + return 0; 167 + } 168 + 169 + static int __devinit lpc32xx_kscan_probe(struct platform_device *pdev) 170 + { 171 + struct lpc32xx_kscan_drv *kscandat; 172 + struct input_dev *input; 173 + struct resource *res; 174 + size_t keymap_size; 175 + int error; 176 + int irq; 177 + 178 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 179 + if (!res) { 180 + dev_err(&pdev->dev, "failed to get platform I/O memory\n"); 181 + return -EINVAL; 182 + } 183 + 184 + irq = platform_get_irq(pdev, 0); 185 + if (irq < 0 || irq >= NR_IRQS) { 186 + dev_err(&pdev->dev, "failed to get platform irq\n"); 187 + return -EINVAL; 188 + } 189 + 190 + kscandat = kzalloc(sizeof(struct lpc32xx_kscan_drv), GFP_KERNEL); 191 + if (!kscandat) { 192 + dev_err(&pdev->dev, "failed to allocate memory\n"); 193 + return -ENOMEM; 194 + } 195 + 196 + error = lpc32xx_parse_dt(&pdev->dev, kscandat); 197 + if (error) { 198 + dev_err(&pdev->dev, "failed to parse device tree\n"); 199 + goto err_free_mem; 200 + } 201 + 202 + keymap_size = sizeof(kscandat->keymap[0]) * 203 + (kscandat->matrix_sz << kscandat->row_shift); 204 + kscandat->keymap = kzalloc(keymap_size, GFP_KERNEL); 205 + if (!kscandat->keymap) { 206 + dev_err(&pdev->dev, "could not allocate memory for keymap\n"); 207 + error = -ENOMEM; 208 + goto err_free_mem; 209 + } 210 + 211 + kscandat->input = input = input_allocate_device(); 212 + if (!input) { 213 + dev_err(&pdev->dev, "failed to allocate input device\n"); 214 + error = -ENOMEM; 215 + goto err_free_keymap; 216 + } 217 + 218 + /* Setup key input */ 219 + input->name = pdev->name; 220 + input->phys = "lpc32xx/input0"; 221 + input->id.vendor = 0x0001; 222 + input->id.product = 0x0001; 223 + input->id.version = 0x0100; 224 + input->open = lpc32xx_kscan_open; 225 + input->close = lpc32xx_kscan_close; 226 + input->dev.parent = &pdev->dev; 227 + 228 + input_set_capability(input, EV_MSC, MSC_SCAN); 229 + 230 + error = matrix_keypad_build_keymap(NULL, NULL, 231 + kscandat->matrix_sz, 232 + kscandat->matrix_sz, 233 + kscandat->keymap, kscandat->input); 234 + if (error) { 235 + dev_err(&pdev->dev, "failed to build keymap\n"); 236 + goto err_free_input; 237 + } 238 + 239 + input_set_drvdata(kscandat->input, kscandat); 240 + 241 + kscandat->iores = request_mem_region(res->start, resource_size(res), 242 + pdev->name); 243 + if (!kscandat->iores) { 244 + dev_err(&pdev->dev, "failed to request I/O memory\n"); 245 + error = -EBUSY; 246 + goto err_free_input; 247 + } 248 + 249 + kscandat->kscan_base = ioremap(kscandat->iores->start, 250 + resource_size(kscandat->iores)); 251 + if (!kscandat->kscan_base) { 252 + dev_err(&pdev->dev, "failed to remap I/O memory\n"); 253 + error = -EBUSY; 254 + goto err_release_memregion; 255 + } 256 + 257 + /* Get the key scanner clock */ 258 + kscandat->clk = clk_get(&pdev->dev, NULL); 259 + if (IS_ERR(kscandat->clk)) { 260 + dev_err(&pdev->dev, "failed to get clock\n"); 261 + error = PTR_ERR(kscandat->clk); 262 + goto err_unmap; 263 + } 264 + 265 + /* Configure the key scanner */ 266 + error = clk_prepare_enable(kscandat->clk); 267 + if (error) 268 + goto err_clk_put; 269 + 270 + writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base)); 271 + writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base)); 272 + writel(LPC32XX_KSCAN_FTST_USE32K_CLK, 273 + LPC32XX_KS_FAST_TST(kscandat->kscan_base)); 274 + writel(kscandat->matrix_sz, 275 + LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base)); 276 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 277 + clk_disable_unprepare(kscandat->clk); 278 + 279 + error = request_irq(irq, lpc32xx_kscan_irq, 0, pdev->name, kscandat); 280 + if (error) { 281 + dev_err(&pdev->dev, "failed to request irq\n"); 282 + goto err_clk_put; 283 + } 284 + 285 + error = input_register_device(kscandat->input); 286 + if (error) { 287 + dev_err(&pdev->dev, "failed to register input device\n"); 288 + goto err_free_irq; 289 + } 290 + 291 + platform_set_drvdata(pdev, kscandat); 292 + return 0; 293 + 294 + err_free_irq: 295 + free_irq(irq, kscandat); 296 + err_clk_put: 297 + clk_put(kscandat->clk); 298 + err_unmap: 299 + iounmap(kscandat->kscan_base); 300 + err_release_memregion: 301 + release_mem_region(kscandat->iores->start, 302 + resource_size(kscandat->iores)); 303 + err_free_input: 304 + input_free_device(kscandat->input); 305 + err_free_keymap: 306 + kfree(kscandat->keymap); 307 + err_free_mem: 308 + kfree(kscandat); 309 + 310 + return error; 311 + } 312 + 313 + static int __devexit lpc32xx_kscan_remove(struct platform_device *pdev) 314 + { 315 + struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); 316 + 317 + free_irq(platform_get_irq(pdev, 0), kscandat); 318 + clk_put(kscandat->clk); 319 + iounmap(kscandat->kscan_base); 320 + release_mem_region(kscandat->iores->start, 321 + resource_size(kscandat->iores)); 322 + input_unregister_device(kscandat->input); 323 + kfree(kscandat->keymap); 324 + kfree(kscandat); 325 + 326 + return 0; 327 + } 328 + 329 + #ifdef CONFIG_PM_SLEEP 330 + static int lpc32xx_kscan_suspend(struct device *dev) 331 + { 332 + struct platform_device *pdev = to_platform_device(dev); 333 + struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); 334 + struct input_dev *input = kscandat->input; 335 + 336 + mutex_lock(&input->mutex); 337 + 338 + if (input->users) { 339 + /* Clear IRQ and disable clock */ 340 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 341 + clk_disable_unprepare(kscandat->clk); 342 + } 343 + 344 + mutex_unlock(&input->mutex); 345 + return 0; 346 + } 347 + 348 + static int lpc32xx_kscan_resume(struct device *dev) 349 + { 350 + struct platform_device *pdev = to_platform_device(dev); 351 + struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev); 352 + struct input_dev *input = kscandat->input; 353 + int retval = 0; 354 + 355 + mutex_lock(&input->mutex); 356 + 357 + if (input->users) { 358 + /* Enable clock and clear IRQ */ 359 + retval = clk_prepare_enable(kscandat->clk); 360 + if (retval == 0) 361 + writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base)); 362 + } 363 + 364 + mutex_unlock(&input->mutex); 365 + return retval; 366 + } 367 + #endif 368 + 369 + static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend, 370 + lpc32xx_kscan_resume); 371 + 372 + static const struct of_device_id lpc32xx_kscan_match[] = { 373 + { .compatible = "nxp,lpc3220-key" }, 374 + {}, 375 + }; 376 + MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match); 377 + 378 + static struct platform_driver lpc32xx_kscan_driver = { 379 + .probe = lpc32xx_kscan_probe, 380 + .remove = __devexit_p(lpc32xx_kscan_remove), 381 + .driver = { 382 + .name = DRV_NAME, 383 + .owner = THIS_MODULE, 384 + .pm = &lpc32xx_kscan_pm_ops, 385 + .of_match_table = of_match_ptr(lpc32xx_kscan_match), 386 + } 387 + }; 388 + 389 + module_platform_driver(lpc32xx_kscan_driver); 390 + 391 + MODULE_LICENSE("GPL"); 392 + MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 393 + MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 394 + MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");