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.15-rc2 402 lines 12 kB view raw
1/* 2 * Keyboard driver for Sharp Corgi models (SL-C7xx) 3 * 4 * Copyright (c) 2004-2005 Richard Purdie 5 * 6 * Based on xtkbd.c/locomkbd.c 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14#include <linux/delay.h> 15#include <linux/platform_device.h> 16#include <linux/init.h> 17#include <linux/input.h> 18#include <linux/interrupt.h> 19#include <linux/jiffies.h> 20#include <linux/module.h> 21#include <linux/slab.h> 22#include <asm/irq.h> 23 24#include <asm/arch/corgi.h> 25#include <asm/arch/hardware.h> 26#include <asm/arch/pxa-regs.h> 27#include <asm/hardware/scoop.h> 28 29#define KB_ROWS 8 30#define KB_COLS 12 31#define KB_ROWMASK(r) (1 << (r)) 32#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) 33/* zero code, 124 scancodes + 3 hinge combinations */ 34#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 ) 35#define SCAN_INTERVAL (HZ/10) 36 37#define HINGE_SCAN_INTERVAL (HZ/4) 38 39#define CORGI_KEY_CALENDER KEY_F1 40#define CORGI_KEY_ADDRESS KEY_F2 41#define CORGI_KEY_FN KEY_F3 42#define CORGI_KEY_CANCEL KEY_F4 43#define CORGI_KEY_OFF KEY_SUSPEND 44#define CORGI_KEY_EXOK KEY_F5 45#define CORGI_KEY_EXCANCEL KEY_F6 46#define CORGI_KEY_EXJOGDOWN KEY_F7 47#define CORGI_KEY_EXJOGUP KEY_F8 48#define CORGI_KEY_JAP1 KEY_LEFTCTRL 49#define CORGI_KEY_JAP2 KEY_LEFTALT 50#define CORGI_KEY_MAIL KEY_F10 51#define CORGI_KEY_OK KEY_F11 52#define CORGI_KEY_MENU KEY_F12 53#define CORGI_HINGE_0 KEY_KP0 54#define CORGI_HINGE_1 KEY_KP1 55#define CORGI_HINGE_2 KEY_KP2 56 57static unsigned char corgikbd_keycode[NR_SCANCODES] = { 58 0, /* 0 */ 59 0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */ 60 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */ 61 KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ 62 CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ 63 CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */ 64 CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ 65 KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ 66 CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */ 67 CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */ 68}; 69 70 71struct corgikbd { 72 unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)]; 73 struct input_dev *input; 74 75 spinlock_t lock; 76 struct timer_list timer; 77 struct timer_list htimer; 78 79 unsigned int suspended; 80 unsigned long suspend_jiffies; 81}; 82 83#define KB_DISCHARGE_DELAY 10 84#define KB_ACTIVATE_DELAY 10 85 86/* Helper functions for reading the keyboard matrix 87 * Note: We should really be using pxa_gpio_mode to alter GPDR but it 88 * requires a function call per GPIO bit which is excessive 89 * when we need to access 12 bits at once multiple times. 90 * These functions must be called within local_irq_save()/local_irq_restore() 91 * or similar. 92 */ 93static inline void corgikbd_discharge_all(void) 94{ 95 /* STROBE All HiZ */ 96 GPCR2 = CORGI_GPIO_ALL_STROBE_BIT; 97 GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT; 98} 99 100static inline void corgikbd_activate_all(void) 101{ 102 /* STROBE ALL -> High */ 103 GPSR2 = CORGI_GPIO_ALL_STROBE_BIT; 104 GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT; 105 106 udelay(KB_DISCHARGE_DELAY); 107 108 /* Clear any interrupts we may have triggered when altering the GPIO lines */ 109 GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT; 110 GEDR2 = CORGI_GPIO_LOW_SENSE_BIT; 111} 112 113static inline void corgikbd_activate_col(int col) 114{ 115 /* STROBE col -> High, not col -> HiZ */ 116 GPSR2 = CORGI_GPIO_STROBE_BIT(col); 117 GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col); 118} 119 120static inline void corgikbd_reset_col(int col) 121{ 122 /* STROBE col -> Low */ 123 GPCR2 = CORGI_GPIO_STROBE_BIT(col); 124 /* STROBE col -> out, not col -> HiZ */ 125 GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col); 126} 127 128#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT)) 129 130/* 131 * The corgi keyboard only generates interrupts when a key is pressed. 132 * When a key is pressed, we enable a timer which then scans the 133 * keyboard to detect when the key is released. 134 */ 135 136/* Scan the hardware keyboard and push any changes up through the input layer */ 137static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs *regs) 138{ 139 unsigned int row, col, rowd; 140 unsigned long flags; 141 unsigned int num_pressed; 142 143 if (corgikbd_data->suspended) 144 return; 145 146 spin_lock_irqsave(&corgikbd_data->lock, flags); 147 148 if (regs) 149 input_regs(corgikbd_data->input, regs); 150 151 num_pressed = 0; 152 for (col = 0; col < KB_COLS; col++) { 153 /* 154 * Discharge the output driver capacitatance 155 * in the keyboard matrix. (Yes it is significant..) 156 */ 157 158 corgikbd_discharge_all(); 159 udelay(KB_DISCHARGE_DELAY); 160 161 corgikbd_activate_col(col); 162 udelay(KB_ACTIVATE_DELAY); 163 164 rowd = GET_ROWS_STATUS(col); 165 for (row = 0; row < KB_ROWS; row++) { 166 unsigned int scancode, pressed; 167 168 scancode = SCANCODE(row, col); 169 pressed = rowd & KB_ROWMASK(row); 170 171 input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed); 172 173 if (pressed) 174 num_pressed++; 175 176 if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF) 177 && time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) { 178 input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1); 179 corgikbd_data->suspend_jiffies=jiffies; 180 } 181 } 182 corgikbd_reset_col(col); 183 } 184 185 corgikbd_activate_all(); 186 187 input_sync(corgikbd_data->input); 188 189 /* if any keys are pressed, enable the timer */ 190 if (num_pressed) 191 mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL); 192 193 spin_unlock_irqrestore(&corgikbd_data->lock, flags); 194} 195 196/* 197 * corgi keyboard interrupt handler. 198 */ 199static irqreturn_t corgikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) 200{ 201 struct corgikbd *corgikbd_data = dev_id; 202 203 if (!timer_pending(&corgikbd_data->timer)) { 204 /** wait chattering delay **/ 205 udelay(20); 206 corgikbd_scankeyboard(corgikbd_data, regs); 207 } 208 209 return IRQ_HANDLED; 210} 211 212/* 213 * corgi timer checking for released keys 214 */ 215static void corgikbd_timer_callback(unsigned long data) 216{ 217 struct corgikbd *corgikbd_data = (struct corgikbd *) data; 218 corgikbd_scankeyboard(corgikbd_data, NULL); 219} 220 221/* 222 * The hinge switches generate no interrupt so they need to be 223 * monitored by a timer. 224 * 225 * We debounce the switches and pass them to the input system. 226 * 227 * gprr == 0x00 - Keyboard with Landscape Screen 228 * 0x08 - No Keyboard with Portrait Screen 229 * 0x0c - Keyboard and Screen Closed 230 */ 231 232#define HINGE_STABLE_COUNT 2 233static int sharpsl_hinge_state; 234static int hinge_count; 235 236static void corgikbd_hinge_timer(unsigned long data) 237{ 238 struct corgikbd *corgikbd_data = (struct corgikbd *) data; 239 unsigned long gprr; 240 unsigned long flags; 241 242 gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB); 243 if (gprr != sharpsl_hinge_state) { 244 hinge_count = 0; 245 sharpsl_hinge_state = gprr; 246 } else if (hinge_count < HINGE_STABLE_COUNT) { 247 hinge_count++; 248 if (hinge_count >= HINGE_STABLE_COUNT) { 249 spin_lock_irqsave(&corgikbd_data->lock, flags); 250 251 input_report_switch(corgikbd_data->input, SW_0, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0)); 252 input_report_switch(corgikbd_data->input, SW_1, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0)); 253 input_sync(corgikbd_data->input); 254 255 spin_unlock_irqrestore(&corgikbd_data->lock, flags); 256 } 257 } 258 mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL); 259} 260 261#ifdef CONFIG_PM 262static int corgikbd_suspend(struct platform_device *dev, pm_message_t state) 263{ 264 struct corgikbd *corgikbd = platform_get_drvdata(dev); 265 corgikbd->suspended = 1; 266 267 return 0; 268} 269 270static int corgikbd_resume(struct platform_device *dev) 271{ 272 struct corgikbd *corgikbd = platform_get_drvdata(dev); 273 274 /* Upon resume, ignore the suspend key for a short while */ 275 corgikbd->suspend_jiffies=jiffies; 276 corgikbd->suspended = 0; 277 278 return 0; 279} 280#else 281#define corgikbd_suspend NULL 282#define corgikbd_resume NULL 283#endif 284 285static int __init corgikbd_probe(struct platform_device *pdev) 286{ 287 struct corgikbd *corgikbd; 288 struct input_dev *input_dev; 289 int i; 290 291 corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL); 292 input_dev = input_allocate_device(); 293 if (!corgikbd || !input_dev) { 294 kfree(corgikbd); 295 input_free_device(input_dev); 296 return -ENOMEM; 297 } 298 299 platform_set_drvdata(pdev, corgikbd); 300 301 corgikbd->input = input_dev; 302 spin_lock_init(&corgikbd->lock); 303 304 /* Init Keyboard rescan timer */ 305 init_timer(&corgikbd->timer); 306 corgikbd->timer.function = corgikbd_timer_callback; 307 corgikbd->timer.data = (unsigned long) corgikbd; 308 309 /* Init Hinge Timer */ 310 init_timer(&corgikbd->htimer); 311 corgikbd->htimer.function = corgikbd_hinge_timer; 312 corgikbd->htimer.data = (unsigned long) corgikbd; 313 314 corgikbd->suspend_jiffies=jiffies; 315 316 memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode)); 317 318 input_dev->name = "Corgi Keyboard"; 319 input_dev->phys = "corgikbd/input0"; 320 input_dev->id.bustype = BUS_HOST; 321 input_dev->id.vendor = 0x0001; 322 input_dev->id.product = 0x0001; 323 input_dev->id.version = 0x0100; 324 input_dev->cdev.dev = &pdev->dev; 325 input_dev->private = corgikbd; 326 327 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); 328 input_dev->keycode = corgikbd->keycode; 329 input_dev->keycodesize = sizeof(unsigned char); 330 input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode); 331 332 for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++) 333 set_bit(corgikbd->keycode[i], input_dev->keybit); 334 clear_bit(0, input_dev->keybit); 335 set_bit(SW_0, input_dev->swbit); 336 set_bit(SW_1, input_dev->swbit); 337 338 input_register_device(corgikbd->input); 339 340 mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL); 341 342 /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ 343 for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) { 344 pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN); 345 if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt, 346 SA_INTERRUPT, "corgikbd", corgikbd)) 347 printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i); 348 else 349 set_irq_type(CORGI_IRQ_GPIO_KEY_SENSE(i),IRQT_RISING); 350 } 351 352 /* Set Strobe lines as outputs - set high */ 353 for (i = 0; i < CORGI_KEY_STROBE_NUM; i++) 354 pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); 355 356 return 0; 357} 358 359static int corgikbd_remove(struct platform_device *pdev) 360{ 361 int i; 362 struct corgikbd *corgikbd = platform_get_drvdata(pdev); 363 364 for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) 365 free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd); 366 367 del_timer_sync(&corgikbd->htimer); 368 del_timer_sync(&corgikbd->timer); 369 370 input_unregister_device(corgikbd->input); 371 372 kfree(corgikbd); 373 374 return 0; 375} 376 377static struct platform_driver corgikbd_driver = { 378 .probe = corgikbd_probe, 379 .remove = corgikbd_remove, 380 .suspend = corgikbd_suspend, 381 .resume = corgikbd_resume, 382 .driver = { 383 .name = "corgi-keyboard", 384 }, 385}; 386 387static int __devinit corgikbd_init(void) 388{ 389 return platform_driver_register(&corgikbd_driver); 390} 391 392static void __exit corgikbd_exit(void) 393{ 394 platform_driver_unregister(&corgikbd_driver); 395} 396 397module_init(corgikbd_init); 398module_exit(corgikbd_exit); 399 400MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); 401MODULE_DESCRIPTION("Corgi Keyboard Driver"); 402MODULE_LICENSE("GPLv2");