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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.34-rc4 431 lines 11 kB view raw
1/* 2 * Keyboard driver for Sharp Tosa models (SL-6000x) 3 * 4 * Copyright (c) 2005 Dirk Opfer 5 * Copyright (c) 2007 Dmitry Baryshkov 6 * 7 * Based on xtkbd.c/locomkbd.c/corgikbd.c 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 */ 14 15#include <linux/kernel.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/input.h> 19#include <linux/delay.h> 20#include <linux/interrupt.h> 21#include <linux/slab.h> 22 23#include <mach/gpio.h> 24#include <mach/tosa.h> 25 26#define KB_ROWMASK(r) (1 << (r)) 27#define SCANCODE(r, c) (((r)<<4) + (c) + 1) 28#define NR_SCANCODES SCANCODE(TOSA_KEY_SENSE_NUM - 1, TOSA_KEY_STROBE_NUM - 1) + 1 29 30#define SCAN_INTERVAL (HZ/10) 31 32#define KB_DISCHARGE_DELAY 10 33#define KB_ACTIVATE_DELAY 10 34 35static unsigned short tosakbd_keycode[NR_SCANCODES] = { 360, 370, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 380, 0, 0, 0, 0, 0, 0, 0, 39KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 400, 0, 0, 0, 0, 0, 0, 0, 41KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 420, 0, 0, 0, 0, 0, 0, 0, 43KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESSBOOK, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, 44KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, 0, 45KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDAR, TOSA_KEY_HOMEPAGE, KEY_LEFTCTRL, TOSA_KEY_LIGHT, 460, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, 47KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 480, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, 49KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 500, 0, 0, 51}; 52 53struct tosakbd { 54 unsigned short keycode[ARRAY_SIZE(tosakbd_keycode)]; 55 struct input_dev *input; 56 bool suspended; 57 spinlock_t lock; /* protect kbd scanning */ 58 struct timer_list timer; 59}; 60 61 62/* Helper functions for reading the keyboard matrix 63 * Note: We should really be using the generic gpio functions to alter 64 * GPDR but it requires a function call per GPIO bit which is 65 * excessive when we need to access 12 bits at once, multiple times. 66 * These functions must be called within local_irq_save()/local_irq_restore() 67 * or similar. 68 */ 69#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) 70 71static inline void tosakbd_discharge_all(void) 72{ 73 /* STROBE All HiZ */ 74 GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; 75 GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; 76 GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; 77 GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; 78} 79 80static inline void tosakbd_activate_all(void) 81{ 82 /* STROBE ALL -> High */ 83 GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; 84 GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; 85 GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; 86 GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; 87 88 udelay(KB_DISCHARGE_DELAY); 89 90 /* STATE CLEAR */ 91 GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; 92} 93 94static inline void tosakbd_activate_col(int col) 95{ 96 if (col <= 5) { 97 /* STROBE col -> High, not col -> HiZ */ 98 GPSR1 = TOSA_GPIO_STROBE_BIT(col); 99 GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); 100 } else { 101 /* STROBE col -> High, not col -> HiZ */ 102 GPSR2 = TOSA_GPIO_STROBE_BIT(col); 103 GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); 104 } 105} 106 107static inline void tosakbd_reset_col(int col) 108{ 109 if (col <= 5) { 110 /* STROBE col -> Low */ 111 GPCR1 = TOSA_GPIO_STROBE_BIT(col); 112 /* STROBE col -> out, not col -> HiZ */ 113 GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); 114 } else { 115 /* STROBE col -> Low */ 116 GPCR2 = TOSA_GPIO_STROBE_BIT(col); 117 /* STROBE col -> out, not col -> HiZ */ 118 GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); 119 } 120} 121/* 122 * The tosa keyboard only generates interrupts when a key is pressed. 123 * So when a key is pressed, we enable a timer. This timer scans the 124 * keyboard, and this is how we detect when the key is released. 125 */ 126 127/* Scan the hardware keyboard and push any changes up through the input layer */ 128static void tosakbd_scankeyboard(struct platform_device *dev) 129{ 130 struct tosakbd *tosakbd = platform_get_drvdata(dev); 131 unsigned int row, col, rowd; 132 unsigned long flags; 133 unsigned int num_pressed = 0; 134 135 spin_lock_irqsave(&tosakbd->lock, flags); 136 137 if (tosakbd->suspended) 138 goto out; 139 140 for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) { 141 /* 142 * Discharge the output driver capacitatance 143 * in the keyboard matrix. (Yes it is significant..) 144 */ 145 tosakbd_discharge_all(); 146 udelay(KB_DISCHARGE_DELAY); 147 148 tosakbd_activate_col(col); 149 udelay(KB_ACTIVATE_DELAY); 150 151 rowd = GET_ROWS_STATUS(col); 152 153 for (row = 0; row < TOSA_KEY_SENSE_NUM; row++) { 154 unsigned int scancode, pressed; 155 scancode = SCANCODE(row, col); 156 pressed = rowd & KB_ROWMASK(row); 157 158 if (pressed && !tosakbd->keycode[scancode]) 159 dev_warn(&dev->dev, 160 "unhandled scancode: 0x%02x\n", 161 scancode); 162 163 input_report_key(tosakbd->input, 164 tosakbd->keycode[scancode], 165 pressed); 166 if (pressed) 167 num_pressed++; 168 } 169 170 tosakbd_reset_col(col); 171 } 172 173 tosakbd_activate_all(); 174 175 input_sync(tosakbd->input); 176 177 /* if any keys are pressed, enable the timer */ 178 if (num_pressed) 179 mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL); 180 181 out: 182 spin_unlock_irqrestore(&tosakbd->lock, flags); 183} 184 185/* 186 * tosa keyboard interrupt handler. 187 */ 188static irqreturn_t tosakbd_interrupt(int irq, void *__dev) 189{ 190 struct platform_device *dev = __dev; 191 struct tosakbd *tosakbd = platform_get_drvdata(dev); 192 193 if (!timer_pending(&tosakbd->timer)) { 194 /** wait chattering delay **/ 195 udelay(20); 196 tosakbd_scankeyboard(dev); 197 } 198 199 return IRQ_HANDLED; 200} 201 202/* 203 * tosa timer checking for released keys 204 */ 205static void tosakbd_timer_callback(unsigned long __dev) 206{ 207 struct platform_device *dev = (struct platform_device *)__dev; 208 209 tosakbd_scankeyboard(dev); 210} 211 212#ifdef CONFIG_PM 213static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) 214{ 215 struct tosakbd *tosakbd = platform_get_drvdata(dev); 216 unsigned long flags; 217 218 spin_lock_irqsave(&tosakbd->lock, flags); 219 tosakbd->suspended = true; 220 spin_unlock_irqrestore(&tosakbd->lock, flags); 221 222 del_timer_sync(&tosakbd->timer); 223 224 return 0; 225} 226 227static int tosakbd_resume(struct platform_device *dev) 228{ 229 struct tosakbd *tosakbd = platform_get_drvdata(dev); 230 231 tosakbd->suspended = false; 232 tosakbd_scankeyboard(dev); 233 234 return 0; 235} 236#else 237#define tosakbd_suspend NULL 238#define tosakbd_resume NULL 239#endif 240 241static int __devinit tosakbd_probe(struct platform_device *pdev) { 242 243 int i; 244 struct tosakbd *tosakbd; 245 struct input_dev *input_dev; 246 int error; 247 248 tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); 249 if (!tosakbd) 250 return -ENOMEM; 251 252 input_dev = input_allocate_device(); 253 if (!input_dev) { 254 kfree(tosakbd); 255 return -ENOMEM; 256 } 257 258 platform_set_drvdata(pdev, tosakbd); 259 260 spin_lock_init(&tosakbd->lock); 261 262 /* Init Keyboard rescan timer */ 263 init_timer(&tosakbd->timer); 264 tosakbd->timer.function = tosakbd_timer_callback; 265 tosakbd->timer.data = (unsigned long) pdev; 266 267 tosakbd->input = input_dev; 268 269 input_set_drvdata(input_dev, tosakbd); 270 input_dev->name = "Tosa Keyboard"; 271 input_dev->phys = "tosakbd/input0"; 272 input_dev->dev.parent = &pdev->dev; 273 274 input_dev->id.bustype = BUS_HOST; 275 input_dev->id.vendor = 0x0001; 276 input_dev->id.product = 0x0001; 277 input_dev->id.version = 0x0100; 278 279 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); 280 input_dev->keycode = tosakbd->keycode; 281 input_dev->keycodesize = sizeof(tosakbd->keycode[0]); 282 input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); 283 284 memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode)); 285 286 for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) 287 __set_bit(tosakbd->keycode[i], input_dev->keybit); 288 __clear_bit(KEY_RESERVED, input_dev->keybit); 289 290 /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ 291 for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { 292 int gpio = TOSA_GPIO_KEY_SENSE(i); 293 int irq; 294 error = gpio_request(gpio, "tosakbd"); 295 if (error < 0) { 296 printk(KERN_ERR "tosakbd: failed to request GPIO %d, " 297 " error %d\n", gpio, error); 298 goto fail; 299 } 300 301 error = gpio_direction_input(TOSA_GPIO_KEY_SENSE(i)); 302 if (error < 0) { 303 printk(KERN_ERR "tosakbd: failed to configure input" 304 " direction for GPIO %d, error %d\n", 305 gpio, error); 306 gpio_free(gpio); 307 goto fail; 308 } 309 310 irq = gpio_to_irq(gpio); 311 if (irq < 0) { 312 error = irq; 313 printk(KERN_ERR "gpio-keys: Unable to get irq number" 314 " for GPIO %d, error %d\n", 315 gpio, error); 316 gpio_free(gpio); 317 goto fail; 318 } 319 320 error = request_irq(irq, tosakbd_interrupt, 321 IRQF_DISABLED | IRQF_TRIGGER_RISING, 322 "tosakbd", pdev); 323 324 if (error) { 325 printk("tosakbd: Can't get IRQ: %d: error %d!\n", 326 irq, error); 327 gpio_free(gpio); 328 goto fail; 329 } 330 } 331 332 /* Set Strobe lines as outputs - set high */ 333 for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { 334 int gpio = TOSA_GPIO_KEY_STROBE(i); 335 error = gpio_request(gpio, "tosakbd"); 336 if (error < 0) { 337 printk(KERN_ERR "tosakbd: failed to request GPIO %d, " 338 " error %d\n", gpio, error); 339 goto fail2; 340 } 341 342 error = gpio_direction_output(gpio, 1); 343 if (error < 0) { 344 printk(KERN_ERR "tosakbd: failed to configure input" 345 " direction for GPIO %d, error %d\n", 346 gpio, error); 347 gpio_free(gpio); 348 goto fail2; 349 } 350 351 } 352 353 error = input_register_device(input_dev); 354 if (error) { 355 printk(KERN_ERR "tosakbd: Unable to register input device, " 356 "error: %d\n", error); 357 goto fail2; 358 } 359 360 printk(KERN_INFO "input: Tosa Keyboard Registered\n"); 361 362 return 0; 363 364fail2: 365 while (--i >= 0) 366 gpio_free(TOSA_GPIO_KEY_STROBE(i)); 367 368 i = TOSA_KEY_SENSE_NUM; 369fail: 370 while (--i >= 0) { 371 free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), pdev); 372 gpio_free(TOSA_GPIO_KEY_SENSE(i)); 373 } 374 375 platform_set_drvdata(pdev, NULL); 376 input_free_device(input_dev); 377 kfree(tosakbd); 378 379 return error; 380} 381 382static int __devexit tosakbd_remove(struct platform_device *dev) 383{ 384 int i; 385 struct tosakbd *tosakbd = platform_get_drvdata(dev); 386 387 for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) 388 gpio_free(TOSA_GPIO_KEY_STROBE(i)); 389 390 for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { 391 free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), dev); 392 gpio_free(TOSA_GPIO_KEY_SENSE(i)); 393 } 394 395 del_timer_sync(&tosakbd->timer); 396 397 input_unregister_device(tosakbd->input); 398 399 kfree(tosakbd); 400 401 return 0; 402} 403 404static struct platform_driver tosakbd_driver = { 405 .probe = tosakbd_probe, 406 .remove = __devexit_p(tosakbd_remove), 407 .suspend = tosakbd_suspend, 408 .resume = tosakbd_resume, 409 .driver = { 410 .name = "tosa-keyboard", 411 .owner = THIS_MODULE, 412 }, 413}; 414 415static int __devinit tosakbd_init(void) 416{ 417 return platform_driver_register(&tosakbd_driver); 418} 419 420static void __exit tosakbd_exit(void) 421{ 422 platform_driver_unregister(&tosakbd_driver); 423} 424 425module_init(tosakbd_init); 426module_exit(tosakbd_exit); 427 428MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); 429MODULE_DESCRIPTION("Tosa Keyboard Driver"); 430MODULE_LICENSE("GPL v2"); 431MODULE_ALIAS("platform:tosa-keyboard");