Input: q40kbd - convert driver to the split model
[linux-3.10.git] / drivers / input / serio / q40kbd.c
index 5eb84b3..0c0df7f 100644 (file)
 #include <asm/irq.h>
 #include <asm/q40ints.h>
 
+#define DRV_NAME       "q40kbd"
+
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
 
-static DEFINE_SPINLOCK(q40kbd_lock);
-static struct serio *q40kbd_port;
-static struct platform_device *q40kbd_device;
+struct q40kbd {
+       struct serio *port;
+       spinlock_t lock;
+};
 
 static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
 {
+       struct q40kbd *q40kbd = dev_id;
        unsigned long flags;
 
-       spin_lock_irqsave(&q40kbd_lock, 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);
+               serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0);
 
        master_outb(-1, KEYBOARD_UNLOCK_REG);
 
-       spin_unlock_irqrestore(&q40kbd_lock, flags);
+       spin_unlock_irqrestore(&q40kbd->lock, flags);
 
        return IRQ_HANDLED;
 }
@@ -72,17 +77,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
  * q40kbd_flush() flushes all data that may be in the keyboard buffers
  */
 
-static void q40kbd_flush(void)
+static void q40kbd_flush(struct q40kbd *q40kbd)
 {
        int maxread = 100;
        unsigned long flags;
 
-       spin_lock_irqsave(&q40kbd_lock, 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);
+       spin_unlock_irqrestore(&q40kbd->lock, flags);
+}
+
+static void q40kbd_stop(void)
+{
+       master_outb(0, KEY_IRQ_ENABLE_REG);
+       master_outb(-1, KEYBOARD_UNLOCK_REG);
 }
 
 /*
@@ -92,12 +103,9 @@ static void q40kbd_flush(void)
 
 static int q40kbd_open(struct serio *port)
 {
-       q40kbd_flush();
+       struct q40kbd *q40kbd = port->port_data;
 
-       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 -EBUSY;
-       }
+       q40kbd_flush(q40kbd);
 
        /* off we go */
        master_outb(-1, KEYBOARD_UNLOCK_REG);
@@ -108,36 +116,72 @@ static int q40kbd_open(struct serio *port)
 
 static void q40kbd_close(struct serio *port)
 {
-       master_outb(0, KEY_IRQ_ENABLE_REG);
-       master_outb(-1, KEYBOARD_UNLOCK_REG);
-       free_irq(Q40_IRQ_KEYBOARD, NULL);
+       struct q40kbd *q40kbd = port->port_data;
 
-       q40kbd_flush();
+       q40kbd_stop();
+       q40kbd_flush(q40kbd);
 }
 
-static int __devinit q40kbd_probe(struct platform_device *dev)
+static int __devinit q40kbd_probe(struct platform_device *pdev)
 {
-       q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
-       if (!q40kbd_port)
-               return -ENOMEM;
-
-       q40kbd_port->id.type    = SERIO_8042;
-       q40kbd_port->open       = q40kbd_open;
-       q40kbd_port->close      = q40kbd_close;
-       q40kbd_port->dev.parent = &dev->dev;
-       strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name));
-       strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys));
-
-       serio_register_port(q40kbd_port);
+       struct q40kbd *q40kbd;
+       struct serio *port;
+       int error;
+
+       q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL);
+       port = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!q40kbd || !port) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       q40kbd->port = port;
+       spin_lock_init(&q40kbd->lock);
+
+       port->id.type = SERIO_8042;
+       port->open = q40kbd_open;
+       port->close = q40kbd_close;
+       port->port_data = q40kbd;
+       port->dev.parent = &pdev->dev;
+       strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name));
+       strlcpy(port->phys, "Q40", sizeof(port->phys));
+
+       q40kbd_stop();
+
+       error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0,
+                           DRV_NAME, q40kbd);
+       if (error) {
+               dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
+               goto err_free_mem;
+       }
+
+       serio_register_port(q40kbd->port);
+
+       platform_set_drvdata(pdev, q40kbd);
        printk(KERN_INFO "serio: Q40 kbd registered\n");
 
        return 0;
+
+err_free_mem:
+       kfree(port);
+       kfree(q40kbd);
+       return error;
 }
 
-static int __devexit q40kbd_remove(struct platform_device *dev)
+static int __devexit q40kbd_remove(struct platform_device *pdev)
 {
-       serio_unregister_port(q40kbd_port);
-
+       struct q40kbd *q40kbd = platform_get_drvdata(pdev);
+
+       /*
+        * q40kbd_close() will be called as part of unregistering
+        * and will ensure that IRQ is turned off, so it is safe
+        * to unregister port first and free IRQ later.
+        */
+       serio_unregister_port(q40kbd->port);
+       free_irq(Q40_IRQ_KEYBOARD, q40kbd);
+       kfree(q40kbd);
+
+       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
@@ -146,41 +190,16 @@ static struct platform_driver q40kbd_driver = {
                .name   = "q40kbd",
                .owner  = THIS_MODULE,
        },
-       .probe          = q40kbd_probe,
        .remove         = __devexit_p(q40kbd_remove),
 };
 
 static int __init q40kbd_init(void)
 {
-       int error;
-
-       if (!MACH_IS_Q40)
-               return -ENODEV;
-
-       error = platform_driver_register(&q40kbd_driver);
-       if (error)
-               return error;
-
-       q40kbd_device = platform_device_alloc("q40kbd", -1);
-       if (!q40kbd_device)
-               goto err_unregister_driver;
-
-       error = platform_device_add(q40kbd_device);
-       if (error)
-               goto err_free_device;
-
-       return 0;
-
- err_free_device:
-       platform_device_put(q40kbd_device);
- err_unregister_driver:
-       platform_driver_unregister(&q40kbd_driver);
-       return error;
+       return platform_driver_probe(&q40kbd_driver, q40kbd_probe);
 }
 
 static void __exit q40kbd_exit(void)
 {
-       platform_device_unregister(q40kbd_device);
        platform_driver_unregister(&q40kbd_driver);
 }