X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Finput%2Fserio%2Fq40kbd.c;h=46093c507988e002fa8ee8abf198cd397721d396;hb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;hp=021763a3e6bed621ac10815985350b400924dcf4;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 021763a3e..46093c507 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -4,7 +4,7 @@ * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: - * Richard Zidlicky + * Richard Zidlicky */ /* @@ -35,8 +35,9 @@ #include #include #include +#include +#include -#include #include #include #include @@ -47,54 +48,106 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +DEFINE_SPINLOCK(q40kbd_lock); +static struct serio *q40kbd_port; +static struct platform_device *q40kbd_device; -static int q40kbd_open(struct serio *port) +static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - return 0; + unsigned long flags; + + spin_lock_irqsave(&q40kbd_lock, flags); + + if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) + serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0, regs); + + master_outb(-1, KEYBOARD_UNLOCK_REG); + + spin_unlock_irqrestore(&q40kbd_lock, flags); + + return IRQ_HANDLED; } -static void q40kbd_close(struct serio *port) + +/* + * q40kbd_flush() flushes all data that may be in the keyboard buffers + */ + +static void q40kbd_flush(void) { + int maxread = 100; + unsigned long flags; + + spin_lock_irqsave(&q40kbd_lock, flags); + + while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) + master_inb(KEYCODE_REG); + + spin_unlock_irqrestore(&q40kbd_lock, flags); } -static struct serio q40kbd_port = -{ - .type = SERIO_8042, - .name = "Q40 kbd port", - .phys = "Q40", - .write = NULL, - .open = q40kbd_open, - .close = q40kbd_close, -}; - -static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, - struct pt_regs *regs) +/* + * q40kbd_open() is called when a port is open by the higher layer. + * It allocates the interrupt and enables in in the chip. + */ + +static int q40kbd_open(struct serio *port) { - if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(&q40kbd_port, master_inb(KEYCODE_REG), 0, regs); + q40kbd_flush(); + + if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { + printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + return -1; + } + /* off we go */ + master_outb(-1, KEYBOARD_UNLOCK_REG); + master_outb(1, KEY_IRQ_ENABLE_REG); + + return 0; +} + +static void q40kbd_close(struct serio *port) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); master_outb(-1, KEYBOARD_UNLOCK_REG); - return IRQ_HANDLED; + free_irq(Q40_IRQ_KEYBOARD, NULL); + + q40kbd_flush(); } -static int __init q40kbd_init(void) +static struct serio * __init q40kbd_allocate_port(void) { - int maxread = 100; + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->id.type = SERIO_8042; + serio->open = q40kbd_open; + serio->close = q40kbd_close; + serio->dev.parent = &q40kbd_device->dev; + strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name)); + strlcpy(serio->phys, "Q40", sizeof(serio->phys)); + } + + return serio; +} +static int __init q40kbd_init(void) +{ if (!MACH_IS_Q40) return -EIO; - /* allocate the IRQ */ - request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL); - - /* flush any pending input */ - while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) - master_inb(KEYCODE_REG); + q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0); + if (IS_ERR(q40kbd_device)) + return PTR_ERR(q40kbd_device); - /* off we go */ - master_outb(-1,KEYBOARD_UNLOCK_REG); - master_outb(1,KEY_IRQ_ENABLE_REG); + if (!(q40kbd_port = q40kbd_allocate_port())) { + platform_device_unregister(q40kbd_device); + return -ENOMEM; + } - serio_register_port(&q40kbd_port); + serio_register_port(q40kbd_port); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; @@ -102,11 +155,8 @@ static int __init q40kbd_init(void) static void __exit q40kbd_exit(void) { - master_outb(0,KEY_IRQ_ENABLE_REG); - master_outb(-1,KEYBOARD_UNLOCK_REG); - - serio_unregister_port(&q40kbd_port); - free_irq(Q40_IRQ_KEYBOARD, NULL); + serio_unregister_port(q40kbd_port); + platform_device_unregister(q40kbd_device); } module_init(q40kbd_init);