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

Input: add support for PXA27x keyboard controller

Signed-off-by: Rodolfo Giometti <giometti@enneenne.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Rodolfo Giometti and committed by
Dmitry Torokhov
5a90e5bc 867d2682

+281
+9
drivers/input/keyboard/Kconfig
··· 203 203 To compile this driver as a module, choose M here: the 204 204 module will be called omap-keypad. 205 205 206 + config KEYBOARD_PXA27x 207 + tristate "PXA27x keyboard support" 208 + depends on PXA27x 209 + help 210 + Enable support for PXA27x matrix keyboard controller 211 + 212 + To compile this driver as a module, choose M here: the 213 + module will be called pxa27x_keyboard. 214 + 206 215 config KEYBOARD_AAED2000 207 216 tristate "AAED-2000 keyboard" 208 217 depends on MACH_AAED2000
+1
drivers/input/keyboard/Makefile
··· 17 17 obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o 18 18 obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o 19 19 obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o 20 + obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o 20 21 obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o 21 22 obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o 22 23
+258
drivers/input/keyboard/pxa27x_keyboard.c
··· 1 + /* 2 + * linux/drivers/input/keyboard/pxa27x_keyboard.c 3 + * 4 + * Driver for the pxa27x matrix keyboard controller. 5 + * 6 + * Created: Feb 22, 2007 7 + * Author: Rodolfo Giometti <giometti@linux.it> 8 + * 9 + * Based on a previous implementations by Kevin O'Connor 10 + * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and 11 + * on some suggestions by Nicolas Pitre <nico@cam.org>. 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License version 2 as 15 + * published by the Free Software Foundation. 16 + */ 17 + 18 + 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/init.h> 22 + #include <linux/interrupt.h> 23 + #include <linux/input.h> 24 + #include <linux/device.h> 25 + #include <linux/platform_device.h> 26 + 27 + #include <asm/mach-types.h> 28 + #include <asm/mach/arch.h> 29 + #include <asm/mach/map.h> 30 + 31 + #include <asm/arch/hardware.h> 32 + #include <asm/arch/pxa-regs.h> 33 + #include <asm/arch/irqs.h> 34 + #include <asm/arch/pxa27x_keyboard.h> 35 + 36 + #define DRIVER_NAME "pxa27x-keyboard" 37 + 38 + #define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \ 39 + col/2 == 1 ? KPASMKP1 : \ 40 + col/2 == 2 ? KPASMKP2 : KPASMKP3) 41 + #define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) 42 + 43 + static irqreturn_t pxakbd_irq_handler(int irq, void *dev_id) 44 + { 45 + struct platform_device *pdev = dev_id; 46 + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; 47 + struct input_dev *input_dev = platform_get_drvdata(pdev); 48 + unsigned long kpc = KPC; 49 + int p, row, col, rel; 50 + 51 + if (kpc & KPC_DI) { 52 + unsigned long kpdk = KPDK; 53 + 54 + if (!(kpdk & KPDK_DKP)) { 55 + /* better luck next time */ 56 + } else if (kpc & KPC_REE0) { 57 + unsigned long kprec = KPREC; 58 + KPREC = 0x7f; 59 + 60 + if (kprec & KPREC_OF0) 61 + rel = (kprec & 0xff) + 0x7f; 62 + else if (kprec & KPREC_UF0) 63 + rel = (kprec & 0xff) - 0x7f - 0xff; 64 + else 65 + rel = (kprec & 0xff) - 0x7f; 66 + 67 + if (rel) { 68 + input_report_rel(input_dev, REL_WHEEL, rel); 69 + input_sync(input_dev); 70 + } 71 + } 72 + } 73 + 74 + if (kpc & KPC_MI) { 75 + /* report the status of every button */ 76 + for (row = 0; row < pdata->nr_rows; row++) { 77 + for (col = 0; col < pdata->nr_cols; col++) { 78 + p = KPASMKP(col) & KPASMKPx_MKC(row, col) ? 79 + 1 : 0; 80 + pr_debug("keycode %x - pressed %x\n", 81 + pdata->keycodes[row][col], p); 82 + input_report_key(input_dev, 83 + pdata->keycodes[row][col], p); 84 + } 85 + } 86 + input_sync(input_dev); 87 + } 88 + 89 + return IRQ_HANDLED; 90 + } 91 + 92 + static int pxakbd_open(struct input_dev *dev) 93 + { 94 + /* Set keypad control register */ 95 + KPC |= (KPC_ASACT | 96 + KPC_MS_ALL | 97 + (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL | 98 + KPC_ME | KPC_MIE | KPC_DE | KPC_DIE); 99 + 100 + KPC &= ~KPC_AS; /* disable automatic scan */ 101 + KPC &= ~KPC_IMKP; /* do not ignore multiple keypresses */ 102 + 103 + /* Set rotary count to mid-point value */ 104 + KPREC = 0x7F; 105 + 106 + /* Enable unit clock */ 107 + pxa_set_cken(CKEN19_KEYPAD, 1); 108 + 109 + return 0; 110 + } 111 + 112 + static void pxakbd_close(struct input_dev *dev) 113 + { 114 + /* Disable clock unit */ 115 + pxa_set_cken(CKEN19_KEYPAD, 0); 116 + } 117 + 118 + #ifdef CONFIG_PM 119 + static int pxakbd_suspend(struct platform_device *pdev, pm_message_t state) 120 + { 121 + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; 122 + 123 + /* Save controller status */ 124 + pdata->reg_kpc = KPC; 125 + pdata->reg_kprec = KPREC; 126 + 127 + return 0; 128 + } 129 + 130 + static int pxakbd_resume(struct platform_device *pdev) 131 + { 132 + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; 133 + struct input_dev *input_dev = platform_get_drvdata(pdev); 134 + 135 + mutex_lock(&input_dev->mutex); 136 + 137 + if (input_dev->users) { 138 + /* Restore controller status */ 139 + KPC = pdata->reg_kpc; 140 + KPREC = pdata->reg_kprec; 141 + 142 + /* Enable unit clock */ 143 + pxa_set_cken(CKEN19_KEYPAD, 1); 144 + } 145 + 146 + mutex_unlock(&input_dev->mutex); 147 + 148 + return 0; 149 + } 150 + #else 151 + #define pxakbd_suspend NULL 152 + #define pxakbd_resume NULL 153 + #endif 154 + 155 + static int __devinit pxakbd_probe(struct platform_device *pdev) 156 + { 157 + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; 158 + struct input_dev *input_dev; 159 + int i, row, col, error; 160 + 161 + /* Create and register the input driver. */ 162 + input_dev = input_allocate_device(); 163 + if (!input_dev) { 164 + printk(KERN_ERR "Cannot request keypad device\n"); 165 + return -ENOMEM; 166 + } 167 + 168 + input_dev->name = DRIVER_NAME; 169 + input_dev->id.bustype = BUS_HOST; 170 + input_dev->open = pxakbd_open; 171 + input_dev->close = pxakbd_close; 172 + input_dev->cdev.dev = &pdev->dev; 173 + 174 + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); 175 + input_dev->relbit[LONG(REL_WHEEL)] = BIT(REL_WHEEL); 176 + for (row = 0; row < pdata->nr_rows; row++) { 177 + for (col = 0; col < pdata->nr_cols; col++) { 178 + int code = pdata->keycodes[row][col]; 179 + if (code > 0) 180 + set_bit(code, input_dev->keybit); 181 + } 182 + } 183 + 184 + error = request_irq(IRQ_KEYPAD, pxakbd_irq_handler, SA_INTERRUPT, 185 + DRIVER_NAME, pdev); 186 + if (error) { 187 + printk(KERN_ERR "Cannot request keypad IRQ\n"); 188 + pxa_set_cken(CKEN19_KEYPAD, 0); 189 + goto err_free_dev; 190 + } 191 + 192 + platform_set_drvdata(pdev, input_dev); 193 + 194 + /* Register the input device */ 195 + error = input_register_device(input_dev); 196 + if (error) 197 + goto err_free_irq; 198 + 199 + /* Setup GPIOs. */ 200 + for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++) 201 + pxa_gpio_mode(pdata->gpio_modes[i]); 202 + 203 + /* 204 + * Store rows/cols info into keyboard registers. 205 + */ 206 + 207 + KPC |= (pdata->nr_rows - 1) << 26; 208 + KPC |= (pdata->nr_cols - 1) << 23; 209 + 210 + for (col = 0; col < pdata->nr_cols; col++) 211 + KPC |= KPC_MS0 << col; 212 + 213 + return 0; 214 + 215 + err_free_irq: 216 + platform_set_drvdata(pdev, NULL); 217 + free_irq(IRQ_KEYPAD, pdev); 218 + err_free_dev: 219 + input_free_device(input_dev); 220 + return error; 221 + } 222 + 223 + static int __devexit pxakbd_remove(struct platform_device *pdev) 224 + { 225 + struct input_dev *input_dev = platform_get_drvdata(pdev); 226 + 227 + input_unregister_device(input_dev); 228 + free_irq(IRQ_KEYPAD, pdev); 229 + platform_set_drvdata(pdev, NULL); 230 + 231 + return 0; 232 + } 233 + 234 + static struct platform_driver pxakbd_driver = { 235 + .probe = pxakbd_probe, 236 + .remove = __devexit_p(pxakbd_remove), 237 + .suspend = pxakbd_suspend, 238 + .resume = pxakbd_resume, 239 + .driver = { 240 + .name = DRIVER_NAME, 241 + }, 242 + }; 243 + 244 + static int __init pxakbd_init(void) 245 + { 246 + return platform_driver_register(&pxakbd_driver); 247 + } 248 + 249 + static void __exit pxakbd_exit(void) 250 + { 251 + platform_driver_unregister(&pxakbd_driver); 252 + } 253 + 254 + module_init(pxakbd_init); 255 + module_exit(pxakbd_exit); 256 + 257 + MODULE_DESCRIPTION("PXA27x Matrix Keyboard Driver"); 258 + MODULE_LICENSE("GPL");
+13
include/asm-arm/arch-pxa/pxa27x_keyboard.h
··· 1 + #define PXAKBD_MAXROW 8 2 + #define PXAKBD_MAXCOL 8 3 + 4 + struct pxa27x_keyboard_platform_data { 5 + int nr_rows, nr_cols; 6 + int keycodes[PXAKBD_MAXROW][PXAKBD_MAXCOL]; 7 + int gpio_modes[PXAKBD_MAXROW + PXAKBD_MAXCOL]; 8 + 9 + #ifdef CONFIG_PM 10 + u32 reg_kpc; 11 + u32 reg_kprec; 12 + #endif 13 + };