Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
[safe/jmp/linux-2.6] / drivers / char / sonypi.c
index fd04206..73f66d0 100644 (file)
@@ -1,11 +1,13 @@
 /*
  * Sony Programmable I/O Control Device driver for VAIO
  *
+ * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+ *
  * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
  *
  * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
  *
- * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
  *
  * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
  *
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/input.h>
 #include <linux/pci.h>
-#include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/miscdevice.h>
@@ -48,6 +49,8 @@
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/kfifo.h>
+#include <linux/platform_device.h>
+#include <linux/gfp.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -96,14 +99,20 @@ module_param(useinput, int, 0444);
 MODULE_PARM_DESC(useinput,
                 "set this if you would like sonypi to feed events to the input subsystem");
 
+static int check_ioport = 1;
+module_param(check_ioport, int, 0444);
+MODULE_PARM_DESC(check_ioport,
+                "set this to 0 if you think the automatic ioport check for sony-laptop is wrong");
+
 #define SONYPI_DEVICE_MODEL_TYPE1      1
 #define SONYPI_DEVICE_MODEL_TYPE2      2
+#define SONYPI_DEVICE_MODEL_TYPE3      3
 
 /* type1 models use those */
 #define SONYPI_IRQ_PORT                        0x8034
 #define SONYPI_IRQ_SHIFT               22
-#define SONYPI_BASE                    0x50
-#define SONYPI_G10A                    (SONYPI_BASE+0x14)
+#define SONYPI_TYPE1_BASE              0x50
+#define SONYPI_G10A                    (SONYPI_TYPE1_BASE+0x14)
 #define SONYPI_TYPE1_REGION_SIZE       0x08
 #define SONYPI_TYPE1_EVTYPE_OFFSET     0x04
 
@@ -114,6 +123,13 @@ MODULE_PARM_DESC(useinput,
 #define SONYPI_TYPE2_REGION_SIZE       0x20
 #define SONYPI_TYPE2_EVTYPE_OFFSET     0x12
 
+/* type3 series specifics */
+#define SONYPI_TYPE3_BASE              0x40
+#define SONYPI_TYPE3_GID2              (SONYPI_TYPE3_BASE+0x48) /* 16 bits */
+#define SONYPI_TYPE3_MISC              (SONYPI_TYPE3_BASE+0x6d) /* 8 bits  */
+#define SONYPI_TYPE3_REGION_SIZE       0x20
+#define SONYPI_TYPE3_EVTYPE_OFFSET     0x12
+
 /* battery / brightness addresses */
 #define SONYPI_BAT_FLAGS       0x81
 #define SONYPI_LCD_LIGHT       0x96
@@ -159,6 +175,10 @@ static struct sonypi_ioport_list sonypi_type2_ioport_list[] = {
        { 0x0, 0x0 }
 };
 
+/* same as in type 2 models */
+static struct sonypi_ioport_list *sonypi_type3_ioport_list =
+       sonypi_type2_ioport_list;
+
 /* The set of possible interrupts */
 struct sonypi_irq_list {
        u16     irq;
@@ -180,6 +200,9 @@ static struct sonypi_irq_list sonypi_type2_irq_list[] = {
        {  0, 0x00 }    /* no IRQ, 0x00 in SIRQ in AML */
 };
 
+/* same as in type2 models */
+static struct sonypi_irq_list *sonypi_type3_irq_list = sonypi_type2_irq_list;
+
 #define SONYPI_CAMERA_BRIGHTNESS               0
 #define SONYPI_CAMERA_CONTRAST                 1
 #define SONYPI_CAMERA_HUE                      2
@@ -223,6 +246,7 @@ static struct sonypi_irq_list sonypi_type2_irq_list[] = {
 #define SONYPI_MEYE_MASK                       0x00000400
 #define SONYPI_MEMORYSTICK_MASK                        0x00000800
 #define SONYPI_BATTERY_MASK                    0x00001000
+#define SONYPI_WIRELESS_MASK                   0x00002000
 
 struct sonypi_event {
        u8      data;
@@ -305,6 +329,13 @@ static struct sonypi_event sonypi_blueev[] = {
        { 0, 0 }
 };
 
+/* The set of possible wireless events */
+static struct sonypi_event sonypi_wlessev[] = {
+       { 0x59, SONYPI_EVENT_WIRELESS_ON },
+       { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
+       { 0, 0 }
+};
+
 /* The set of possible back button events */
 static struct sonypi_event sonypi_backev[] = {
        { 0x20, SONYPI_EVENT_BACK_PRESSED },
@@ -383,7 +414,6 @@ static struct sonypi_eventtypes {
        { SONYPI_DEVICE_MODEL_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
-       { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_HELP_MASK, sonypi_helpev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
@@ -391,15 +421,17 @@ static struct sonypi_eventtypes {
        { SONYPI_DEVICE_MODEL_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
 
+       { SONYPI_DEVICE_MODEL_TYPE3, 0, 0xffffffff, sonypi_releaseev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
        { 0 }
 };
 
 #define SONYPI_BUF_SIZE        128
 
-/* The name of the devices for the input device drivers */
-#define SONYPI_JOG_INPUTNAME   "Sony Vaio Jogdial"
-#define SONYPI_KEY_INPUTNAME   "Sony Vaio Keys"
-
 /* Correspondance table between sonypi events and input layer events */
 static struct {
        int sonypiev;
@@ -439,9 +471,13 @@ static struct {
        { 0, 0 },
 };
 
+struct sonypi_keypress {
+       struct input_dev *dev;
+       int key;
+};
+
 static struct sonypi_device {
        struct pci_dev *dev;
-       struct platform_device *pdev;
        u16 irq;
        u16 bits;
        u16 ioport1;
@@ -450,17 +486,17 @@ static struct sonypi_device {
        u16 evtype_offset;
        int camera_power;
        int bluetooth_power;
-       struct semaphore lock;
-       struct kfifo *fifo;
+       struct mutex lock;
+       struct kfifo fifo;
        spinlock_t fifo_lock;
        wait_queue_head_t fifo_proc_list;
        struct fasync_struct *fifo_async;
        int open_count;
        int model;
-       struct input_dev input_jog_dev;
-       struct input_dev input_key_dev;
+       struct input_dev *input_jog_dev;
+       struct input_dev *input_key_dev;
        struct work_struct input_work;
-       struct kfifo *input_fifo;
+       struct kfifo input_fifo;
        spinlock_t input_fifo_lock;
 } sonypi_device;
 
@@ -472,7 +508,7 @@ static struct sonypi_device {
        while (--n && (command)) \
                udelay(1); \
        if (!n && (verbose || !quiet)) \
-               printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __FUNCTION__, __LINE__); \
+               printk(KERN_WARNING "sonypi command failed at %s : %s (line %d)\n", __FILE__, __func__, __LINE__); \
 }
 
 #ifdef CONFIG_ACPI
@@ -481,9 +517,14 @@ static struct sonypi_device {
 #define SONYPI_ACPI_ACTIVE 0
 #endif                         /* CONFIG_ACPI */
 
+#ifdef CONFIG_ACPI
+static struct acpi_device *sonypi_acpi_device;
+static int acpi_driver_registered;
+#endif
+
 static int sonypi_ec_write(u8 addr, u8 value)
 {
-#ifdef CONFIG_ACPI_EC
+#ifdef CONFIG_ACPI
        if (SONYPI_ACPI_ACTIVE)
                return ec_write(addr, value);
 #endif
@@ -499,7 +540,7 @@ static int sonypi_ec_write(u8 addr, u8 value)
 
 static int sonypi_ec_read(u8 addr, u8 *value)
 {
-#ifdef CONFIG_ACPI_EC
+#ifdef CONFIG_ACPI
        if (SONYPI_ACPI_ACTIVE)
                return ec_read(addr, value);
 #endif
@@ -558,6 +599,23 @@ static void sonypi_type2_srs(void)
        udelay(10);
 }
 
+static void sonypi_type3_srs(void)
+{
+       u16 v16;
+       u8  v8;
+
+       /* This model type uses the same initialiazation of
+        * the embedded controller as the type2 models. */
+       sonypi_type2_srs();
+
+       /* Initialization of PCI config space of the LPC interface bridge. */
+       v16 = (sonypi_device.ioport1 & 0xFFF0) | 0x01;
+       pci_write_config_word(sonypi_device.dev, SONYPI_TYPE3_GID2, v16);
+       pci_read_config_byte(sonypi_device.dev, SONYPI_TYPE3_MISC, &v8);
+       v8 = (v8 & 0xCF) | 0x10;
+       pci_write_config_byte(sonypi_device.dev, SONYPI_TYPE3_MISC, v8);
+}
+
 /* Disables the device - this comes from the AML code in the ACPI bios */
 static void sonypi_type1_dis(void)
 {
@@ -582,6 +640,13 @@ static void sonypi_type2_dis(void)
                printk(KERN_WARNING "ec_write failed\n");
 }
 
+static void sonypi_type3_dis(void)
+{
+       sonypi_type2_dis();
+       udelay(10);
+       pci_write_config_word(sonypi_device.dev, SONYPI_TYPE3_GID2, 0);
+}
+
 static u8 sonypi_call1(u8 dev)
 {
        u8 v1, v2;
@@ -708,29 +773,70 @@ static void sonypi_setbluetoothpower(u8 state)
        sonypi_device.bluetooth_power = state;
 }
 
-static void input_keyrelease(void *data)
+static void input_keyrelease(struct work_struct *work)
 {
-       struct input_dev *input_dev;
-       int key;
-
-       while (1) {
-               if (kfifo_get(sonypi_device.input_fifo,
-                             (unsigned char *)&input_dev,
-                             sizeof(input_dev)) != sizeof(input_dev))
-                       return;
-               if (kfifo_get(sonypi_device.input_fifo,
-                             (unsigned char *)&key,
-                             sizeof(key)) != sizeof(key))
-                       return;
+       struct sonypi_keypress kp;
 
+       while (kfifo_out_locked(&sonypi_device.input_fifo, (unsigned char *)&kp,
+                        sizeof(kp), &sonypi_device.input_fifo_lock)
+                       == sizeof(kp)) {
                msleep(10);
-               input_report_key(input_dev, key, 0);
-               input_sync(input_dev);
+               input_report_key(kp.dev, kp.key, 0);
+               input_sync(kp.dev);
+       }
+}
+
+static void sonypi_report_input_event(u8 event)
+{
+       struct input_dev *jog_dev = sonypi_device.input_jog_dev;
+       struct input_dev *key_dev = sonypi_device.input_key_dev;
+       struct sonypi_keypress kp = { NULL };
+       int i;
+
+       switch (event) {
+       case SONYPI_EVENT_JOGDIAL_UP:
+       case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, 1);
+               input_sync(jog_dev);
+               break;
+
+       case SONYPI_EVENT_JOGDIAL_DOWN:
+       case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, -1);
+               input_sync(jog_dev);
+               break;
+
+       case SONYPI_EVENT_JOGDIAL_PRESSED:
+               kp.key = BTN_MIDDLE;
+               kp.dev = jog_dev;
+               break;
+
+       case SONYPI_EVENT_FNKEY_RELEASED:
+               /* Nothing, not all VAIOs generate this event */
+               break;
+
+       default:
+               for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
+                       if (event == sonypi_inputkeys[i].sonypiev) {
+                               kp.dev = key_dev;
+                               kp.key = sonypi_inputkeys[i].inputev;
+                               break;
+                       }
+               break;
+       }
+
+       if (kp.dev) {
+               input_report_key(kp.dev, kp.key, 1);
+               input_sync(kp.dev);
+               kfifo_in_locked(&sonypi_device.input_fifo,
+                       (unsigned char *)&kp, sizeof(kp),
+                       &sonypi_device.input_fifo_lock);
+               schedule_work(&sonypi_device.input_work);
        }
 }
 
 /* Interrupt handler: some event is available */
-static irqreturn_t sonypi_irq(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sonypi_irq(int irq, void *dev_id)
 {
        u8 v1, v2, event = 0;
        int i, j;
@@ -768,133 +874,44 @@ found:
                printk(KERN_INFO
                       "sonypi: event port1=0x%02x,port2=0x%02x\n", v1, v2);
 
-       if (useinput) {
-               struct input_dev *input_jog_dev = &sonypi_device.input_jog_dev;
-               struct input_dev *input_key_dev = &sonypi_device.input_key_dev;
-               switch (event) {
-               case SONYPI_EVENT_JOGDIAL_UP:
-               case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
-                       input_report_rel(input_jog_dev, REL_WHEEL, 1);
-                       break;
-               case SONYPI_EVENT_JOGDIAL_DOWN:
-               case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
-                       input_report_rel(input_jog_dev, REL_WHEEL, -1);
-                       break;
-               case SONYPI_EVENT_JOGDIAL_PRESSED: {
-                       int key = BTN_MIDDLE;
-                       input_report_key(input_jog_dev, key, 1);
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&input_jog_dev,
-                                 sizeof(input_jog_dev));
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&key, sizeof(key));
-                       break;
-               }
-               case SONYPI_EVENT_FNKEY_RELEASED:
-                       /* Nothing, not all VAIOs generate this event */
-                       break;
-               }
-               input_sync(input_jog_dev);
-
-               for (i = 0; sonypi_inputkeys[i].sonypiev; i++) {
-                       int key;
+       if (useinput)
+               sonypi_report_input_event(event);
 
-                       if (event != sonypi_inputkeys[i].sonypiev)
-                               continue;
-
-                       key = sonypi_inputkeys[i].inputev;
-                       input_report_key(input_key_dev, key, 1);
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&input_key_dev,
-                                 sizeof(input_key_dev));
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&key, sizeof(key));
-               }
-               input_sync(input_key_dev);
-               schedule_work(&sonypi_device.input_work);
-       }
+#ifdef CONFIG_ACPI
+       if (sonypi_acpi_device)
+               acpi_bus_generate_proc_event(sonypi_acpi_device, 1, event);
+#endif
 
-       kfifo_put(sonypi_device.fifo, (unsigned char *)&event, sizeof(event));
+       kfifo_in_locked(&sonypi_device.fifo, (unsigned char *)&event,
+                       sizeof(event), &sonypi_device.fifo_lock);
        kill_fasync(&sonypi_device.fifo_async, SIGIO, POLL_IN);
        wake_up_interruptible(&sonypi_device.fifo_proc_list);
 
        return IRQ_HANDLED;
 }
 
-/* External camera command (exported to the motion eye v4l driver) */
-int sonypi_camera_command(int command, u8 value)
-{
-       if (!camera)
-               return -EIO;
-
-       down(&sonypi_device.lock);
-
-       switch (command) {
-       case SONYPI_COMMAND_SETCAMERA:
-               if (value)
-                       sonypi_camera_on();
-               else
-                       sonypi_camera_off();
-               break;
-       case SONYPI_COMMAND_SETCAMERABRIGHTNESS:
-               sonypi_set(SONYPI_CAMERA_BRIGHTNESS, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERACONTRAST:
-               sonypi_set(SONYPI_CAMERA_CONTRAST, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERAHUE:
-               sonypi_set(SONYPI_CAMERA_HUE, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERACOLOR:
-               sonypi_set(SONYPI_CAMERA_COLOR, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERASHARPNESS:
-               sonypi_set(SONYPI_CAMERA_SHARPNESS, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERAPICTURE:
-               sonypi_set(SONYPI_CAMERA_PICTURE, value);
-               break;
-       case SONYPI_COMMAND_SETCAMERAAGC:
-               sonypi_set(SONYPI_CAMERA_AGC, value);
-               break;
-       default:
-               printk(KERN_ERR "sonypi: sonypi_camera_command invalid: %d\n",
-                      command);
-               break;
-       }
-       up(&sonypi_device.lock);
-       return 0;
-}
-
-EXPORT_SYMBOL(sonypi_camera_command);
-
 static int sonypi_misc_fasync(int fd, struct file *filp, int on)
 {
-       int retval;
-
-       retval = fasync_helper(fd, filp, on, &sonypi_device.fifo_async);
-       if (retval < 0)
-               return retval;
-       return 0;
+       return fasync_helper(fd, filp, on, &sonypi_device.fifo_async);
 }
 
 static int sonypi_misc_release(struct inode *inode, struct file *file)
 {
-       sonypi_misc_fasync(-1, file, 0);
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        sonypi_device.open_count--;
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return 0;
 }
 
 static int sonypi_misc_open(struct inode *inode, struct file *file)
 {
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        /* Flush input queue on first open */
        if (!sonypi_device.open_count)
-               kfifo_reset(sonypi_device.fifo);
+               kfifo_reset(&sonypi_device.fifo);
        sonypi_device.open_count++;
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
+
        return 0;
 }
 
@@ -904,24 +921,25 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
        ssize_t ret;
        unsigned char c;
 
-       if ((kfifo_len(sonypi_device.fifo) == 0) &&
+       if ((kfifo_len(&sonypi_device.fifo) == 0) &&
            (file->f_flags & O_NONBLOCK))
                return -EAGAIN;
 
        ret = wait_event_interruptible(sonypi_device.fifo_proc_list,
-                                      kfifo_len(sonypi_device.fifo) != 0);
+                                      kfifo_len(&sonypi_device.fifo) != 0);
        if (ret)
                return ret;
 
        while (ret < count &&
-              (kfifo_get(sonypi_device.fifo, &c, sizeof(c)) == sizeof(c))) {
+              (kfifo_out_locked(&sonypi_device.fifo, &c, sizeof(c),
+                                &sonypi_device.fifo_lock) == sizeof(c))) {
                if (put_user(c, buf++))
                        return -EFAULT;
                ret++;
        }
 
        if (ret > 0) {
-               struct inode *inode = file->f_dentry->d_inode;
+               struct inode *inode = file->f_path.dentry->d_inode;
                inode->i_atime = current_fs_time(inode->i_sb);
        }
 
@@ -931,20 +949,20 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
 static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
 {
        poll_wait(file, &sonypi_device.fifo_proc_list, wait);
-       if (kfifo_len(sonypi_device.fifo))
+       if (kfifo_len(&sonypi_device.fifo))
                return POLLIN | POLLRDNORM;
        return 0;
 }
 
-static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
+static long sonypi_misc_ioctl(struct file *fp,
                             unsigned int cmd, unsigned long arg)
 {
-       int ret = 0;
+       long ret = 0;
        void __user *argp = (void __user *)arg;
        u8 val8;
        u16 val16;
 
-       down(&sonypi_device.lock);
+       mutex_lock(&sonypi_device.lock);
        switch (cmd) {
        case SONYPI_IOCGBRT:
                if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) {
@@ -1044,18 +1062,19 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
        default:
                ret = -EINVAL;
        }
-       up(&sonypi_device.lock);
+       mutex_unlock(&sonypi_device.lock);
        return ret;
 }
 
-static struct file_operations sonypi_misc_fops = {
+static const struct file_operations sonypi_misc_fops = {
        .owner          = THIS_MODULE,
        .read           = sonypi_misc_read,
        .poll           = sonypi_misc_poll,
        .open           = sonypi_misc_open,
        .release        = sonypi_misc_release,
        .fasync         = sonypi_misc_fasync,
-       .ioctl          = sonypi_misc_ioctl,
+       .unlocked_ioctl = sonypi_misc_ioctl,
+       .llseek         = no_llseek,
 };
 
 static struct miscdevice sonypi_misc_device = {
@@ -1066,10 +1085,17 @@ static struct miscdevice sonypi_misc_device = {
 
 static void sonypi_enable(unsigned int camera_on)
 {
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2)
-               sonypi_type2_srs();
-       else
+       switch (sonypi_device.model) {
+       case SONYPI_DEVICE_MODEL_TYPE1:
                sonypi_type1_srs();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE2:
+               sonypi_type2_srs();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE3:
+               sonypi_type3_srs();
+               break;
+       }
 
        sonypi_call1(0x82);
        sonypi_call2(0x81, 0xff);
@@ -1093,270 +1119,384 @@ static int sonypi_disable(void)
        if (!SONYPI_ACPI_ACTIVE && fnkeyinit)
                outb(0xf1, 0xb2);
 
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2)
-               sonypi_type2_dis();
-       else
+       switch (sonypi_device.model) {
+       case SONYPI_DEVICE_MODEL_TYPE1:
                sonypi_type1_dis();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE2:
+               sonypi_type2_dis();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE3:
+               sonypi_type3_dis();
+               break;
+       }
+
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int old_camera_power;
-
-static int sonypi_suspend(struct device *dev, pm_message_t state, u32 level)
+#ifdef CONFIG_ACPI
+static int sonypi_acpi_add(struct acpi_device *device)
 {
-       if (level == SUSPEND_DISABLE) {
-               old_camera_power = sonypi_device.camera_power;
-               sonypi_disable();
-       }
+       sonypi_acpi_device = device;
+       strcpy(acpi_device_name(device), "Sony laptop hotkeys");
+       strcpy(acpi_device_class(device), "sony/hotkey");
        return 0;
 }
 
-static int sonypi_resume(struct device *dev, u32 level)
+static int sonypi_acpi_remove(struct acpi_device *device, int type)
 {
-       if (level == RESUME_ENABLE)
-               sonypi_enable(old_camera_power);
+       sonypi_acpi_device = NULL;
        return 0;
 }
+
+static const struct acpi_device_id sonypi_device_ids[] = {
+       {"SNY6001", 0},
+       {"", 0},
+};
+
+static struct acpi_driver sonypi_acpi_driver = {
+       .name           = "sonypi",
+       .class          = "hkey",
+       .ids            = sonypi_device_ids,
+       .ops            = {
+                          .add = sonypi_acpi_add,
+                          .remove = sonypi_acpi_remove,
+       },
+};
 #endif
 
-static void sonypi_shutdown(struct device *dev)
+static int __devinit sonypi_create_input_devices(struct platform_device *pdev)
 {
-       sonypi_disable();
+       struct input_dev *jog_dev;
+       struct input_dev *key_dev;
+       int i;
+       int error;
+
+       sonypi_device.input_jog_dev = jog_dev = input_allocate_device();
+       if (!jog_dev)
+               return -ENOMEM;
+
+       jog_dev->name = "Sony Vaio Jogdial";
+       jog_dev->id.bustype = BUS_ISA;
+       jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+       jog_dev->dev.parent = &pdev->dev;
+
+       jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+       jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
+       jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
+
+       sonypi_device.input_key_dev = key_dev = input_allocate_device();
+       if (!key_dev) {
+               error = -ENOMEM;
+               goto err_free_jogdev;
+       }
+
+       key_dev->name = "Sony Vaio Keys";
+       key_dev->id.bustype = BUS_ISA;
+       key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+       key_dev->dev.parent = &pdev->dev;
+
+       /* Initialize the Input Drivers: special keys */
+       key_dev->evbit[0] = BIT_MASK(EV_KEY);
+       for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
+               if (sonypi_inputkeys[i].inputev)
+                       set_bit(sonypi_inputkeys[i].inputev, key_dev->keybit);
+
+       error = input_register_device(jog_dev);
+       if (error)
+               goto err_free_keydev;
+
+       error = input_register_device(key_dev);
+       if (error)
+               goto err_unregister_jogdev;
+
+       return 0;
+
+ err_unregister_jogdev:
+       input_unregister_device(jog_dev);
+       /* Set to NULL so we don't free it again below */
+       jog_dev = NULL;
+ err_free_keydev:
+       input_free_device(key_dev);
+       sonypi_device.input_key_dev = NULL;
+ err_free_jogdev:
+       input_free_device(jog_dev);
+       sonypi_device.input_jog_dev = NULL;
+
+       return error;
 }
 
-static struct device_driver sonypi_driver = {
-       .name           = "sonypi",
-       .bus            = &platform_bus_type,
-#ifdef CONFIG_PM
-       .suspend        = sonypi_suspend,
-       .resume         = sonypi_resume,
-#endif
-       .shutdown       = sonypi_shutdown,
-};
+static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
+                               const struct sonypi_ioport_list *ioport_list)
+{
+       /* try to detect if sony-laptop is being used and thus
+        * has already requested one of the known ioports.
+        * As in the deprecated check_region this is racy has we have
+        * multiple ioports available and one of them can be requested
+        * between this check and the subsequent request. Anyway, as an
+        * attempt to be some more user-friendly as we currently are,
+        * this is enough.
+        */
+       const struct sonypi_ioport_list *check = ioport_list;
+       while (check_ioport && check->port1) {
+               if (!request_region(check->port1,
+                                  sonypi_device.region_size,
+                                  "Sony Programable I/O Device Check")) {
+                       printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? "
+                                       "if not use check_ioport=0\n",
+                                       check->port1);
+                       return -EBUSY;
+               }
+               release_region(check->port1, sonypi_device.region_size);
+               check++;
+       }
+
+       while (ioport_list->port1) {
+
+               if (request_region(ioport_list->port1,
+                                  sonypi_device.region_size,
+                                  "Sony Programable I/O Device")) {
+                       dev->ioport1 = ioport_list->port1;
+                       dev->ioport2 = ioport_list->port2;
+                       return 0;
+               }
+               ioport_list++;
+       }
+
+       return -EBUSY;
+}
 
-static int __devinit sonypi_probe(void)
+static int __devinit sonypi_setup_irq(struct sonypi_device *dev,
+                                     const struct sonypi_irq_list *irq_list)
 {
-       int i, ret;
-       struct sonypi_ioport_list *ioport_list;
-       struct sonypi_irq_list *irq_list;
-       struct pci_dev *pcidev;
+       while (irq_list->irq) {
 
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                               PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+               if (!request_irq(irq_list->irq, sonypi_irq,
+                                IRQF_SHARED, "sonypi", sonypi_irq)) {
+                       dev->irq = irq_list->irq;
+                       dev->bits = irq_list->bits;
+                       return 0;
+               }
+               irq_list++;
+       }
 
-       sonypi_device.dev = pcidev;
-       sonypi_device.model = pcidev ?
-               SONYPI_DEVICE_MODEL_TYPE1 : SONYPI_DEVICE_MODEL_TYPE2;
+       return -EBUSY;
+}
+
+static void __devinit sonypi_display_info(void)
+{
+       printk(KERN_INFO "sonypi: detected type%d model, "
+              "verbose = %d, fnkeyinit = %s, camera = %s, "
+              "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n",
+              sonypi_device.model,
+              verbose,
+              fnkeyinit ? "on" : "off",
+              camera ? "on" : "off",
+              compat ? "on" : "off",
+              mask,
+              useinput ? "on" : "off",
+              SONYPI_ACPI_ACTIVE ? "on" : "off");
+       printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
+              sonypi_device.irq,
+              sonypi_device.ioport1, sonypi_device.ioport2);
+
+       if (minor == -1)
+               printk(KERN_INFO "sonypi: device allocated minor is %d\n",
+                      sonypi_misc_device.minor);
+}
+
+static int __devinit sonypi_probe(struct platform_device *dev)
+{
+       const struct sonypi_ioport_list *ioport_list;
+       const struct sonypi_irq_list *irq_list;
+       struct pci_dev *pcidev;
+       int error;
+
+       printk(KERN_WARNING "sonypi: please try the sony-laptop module instead "
+                       "and report failures, see also "
+                       "http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n");
 
        spin_lock_init(&sonypi_device.fifo_lock);
-       sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
-                                        &sonypi_device.fifo_lock);
-       if (IS_ERR(sonypi_device.fifo)) {
+       error = kfifo_alloc(&sonypi_device.fifo, SONYPI_BUF_SIZE, GFP_KERNEL);
+       if (error) {
                printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
-               ret = PTR_ERR(sonypi_device.fifo);
-               goto out_fifo;
+               return error;
        }
 
        init_waitqueue_head(&sonypi_device.fifo_proc_list);
-       init_MUTEX(&sonypi_device.lock);
+       mutex_init(&sonypi_device.lock);
        sonypi_device.bluetooth_power = -1;
 
+       if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                    PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE1;
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE3;
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE3;
+       else
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE2;
+
        if (pcidev && pci_enable_device(pcidev)) {
                printk(KERN_ERR "sonypi: pci_enable_device failed\n");
-               ret = -EIO;
-               goto out_pcienable;
+               error = -EIO;
+               goto err_put_pcidev;
        }
 
-       if (minor != -1)
-               sonypi_misc_device.minor = minor;
-       if ((ret = misc_register(&sonypi_misc_device))) {
-               printk(KERN_ERR "sonypi: misc_register failed\n");
-               goto out_miscreg;
-       }
+       sonypi_device.dev = pcidev;
 
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) {
+               ioport_list = sonypi_type1_ioport_list;
+               sonypi_device.region_size = SONYPI_TYPE1_REGION_SIZE;
+               sonypi_device.evtype_offset = SONYPI_TYPE1_EVTYPE_OFFSET;
+               irq_list = sonypi_type1_irq_list;
+       } else if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
                ioport_list = sonypi_type2_ioport_list;
                sonypi_device.region_size = SONYPI_TYPE2_REGION_SIZE;
                sonypi_device.evtype_offset = SONYPI_TYPE2_EVTYPE_OFFSET;
                irq_list = sonypi_type2_irq_list;
        } else {
-               ioport_list = sonypi_type1_ioport_list;
-               sonypi_device.region_size = SONYPI_TYPE1_REGION_SIZE;
-               sonypi_device.evtype_offset = SONYPI_TYPE1_EVTYPE_OFFSET;
-               irq_list = sonypi_type1_irq_list;
+               ioport_list = sonypi_type3_ioport_list;
+               sonypi_device.region_size = SONYPI_TYPE3_REGION_SIZE;
+               sonypi_device.evtype_offset = SONYPI_TYPE3_EVTYPE_OFFSET;
+               irq_list = sonypi_type3_irq_list;
        }
 
-       for (i = 0; ioport_list[i].port1; i++) {
-               if (request_region(ioport_list[i].port1,
-                                  sonypi_device.region_size,
-                                  "Sony Programable I/O Device")) {
-                       /* get the ioport */
-                       sonypi_device.ioport1 = ioport_list[i].port1;
-                       sonypi_device.ioport2 = ioport_list[i].port2;
-                       break;
-               }
-       }
-       if (!sonypi_device.ioport1) {
-               printk(KERN_ERR "sonypi: request_region failed\n");
-               ret = -ENODEV;
-               goto out_reqreg;
+       error = sonypi_setup_ioports(&sonypi_device, ioport_list);
+       if (error) {
+               printk(KERN_ERR "sonypi: failed to request ioports\n");
+               goto err_disable_pcidev;
        }
 
-       for (i = 0; irq_list[i].irq; i++) {
-
-               sonypi_device.irq = irq_list[i].irq;
-               sonypi_device.bits = irq_list[i].bits;
-
-               if (!request_irq(sonypi_device.irq, sonypi_irq,
-                                SA_SHIRQ, "sonypi", sonypi_irq))
-                       break;
+       error = sonypi_setup_irq(&sonypi_device, irq_list);
+       if (error) {
+               printk(KERN_ERR "sonypi: request_irq failed\n");
+               goto err_free_ioports;
        }
 
-       if (!irq_list[i].irq) {
-               printk(KERN_ERR "sonypi: request_irq failed\n");
-               ret = -ENODEV;
-               goto out_reqirq;
+       if (minor != -1)
+               sonypi_misc_device.minor = minor;
+       error = misc_register(&sonypi_misc_device);
+       if (error) {
+               printk(KERN_ERR "sonypi: misc_register failed\n");
+               goto err_free_irq;
        }
 
-       if (useinput) {
-               /* Initialize the Input Drivers: jogdial */
-               int i;
-               sonypi_device.input_jog_dev.evbit[0] =
-                       BIT(EV_KEY) | BIT(EV_REL);
-               sonypi_device.input_jog_dev.keybit[LONG(BTN_MOUSE)] =
-                       BIT(BTN_MIDDLE);
-               sonypi_device.input_jog_dev.relbit[0] = BIT(REL_WHEEL);
-               sonypi_device.input_jog_dev.name =
-                       kmalloc(sizeof(SONYPI_JOG_INPUTNAME), GFP_KERNEL);
-               if (!sonypi_device.input_jog_dev.name) {
-                       printk(KERN_ERR "sonypi: kmalloc failed\n");
-                       ret = -ENOMEM;
-                       goto out_inkmallocinput1;
-               }
-               sprintf(sonypi_device.input_jog_dev.name, SONYPI_JOG_INPUTNAME);
-               sonypi_device.input_jog_dev.id.bustype = BUS_ISA;
-               sonypi_device.input_jog_dev.id.vendor = PCI_VENDOR_ID_SONY;
+       sonypi_display_info();
 
-               input_register_device(&sonypi_device.input_jog_dev);
-               printk(KERN_INFO "%s input method installed.\n",
-                      sonypi_device.input_jog_dev.name);
+       if (useinput) {
 
-               /* Initialize the Input Drivers: special keys */
-               sonypi_device.input_key_dev.evbit[0] = BIT(EV_KEY);
-               for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
-                       if (sonypi_inputkeys[i].inputev)
-                               set_bit(sonypi_inputkeys[i].inputev,
-                                       sonypi_device.input_key_dev.keybit);
-               sonypi_device.input_key_dev.name =
-                       kmalloc(sizeof(SONYPI_KEY_INPUTNAME), GFP_KERNEL);
-               if (!sonypi_device.input_key_dev.name) {
-                       printk(KERN_ERR "sonypi: kmalloc failed\n");
-                       ret = -ENOMEM;
-                       goto out_inkmallocinput2;
+               error = sonypi_create_input_devices(dev);
+               if (error) {
+                       printk(KERN_ERR
+                               "sonypi: failed to create input devices\n");
+                       goto err_miscdev_unregister;
                }
-               sprintf(sonypi_device.input_key_dev.name, SONYPI_KEY_INPUTNAME);
-               sonypi_device.input_key_dev.id.bustype = BUS_ISA;
-               sonypi_device.input_key_dev.id.vendor = PCI_VENDOR_ID_SONY;
-
-               input_register_device(&sonypi_device.input_key_dev);
-               printk(KERN_INFO "%s input method installed.\n",
-                      sonypi_device.input_key_dev.name);
 
                spin_lock_init(&sonypi_device.input_fifo_lock);
-               sonypi_device.input_fifo =
-                       kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
-                                   &sonypi_device.input_fifo_lock);
-               if (IS_ERR(sonypi_device.input_fifo)) {
+               error = kfifo_alloc(&sonypi_device.input_fifo, SONYPI_BUF_SIZE,
+                               GFP_KERNEL);
+               if (error) {
                        printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
-                       ret = PTR_ERR(sonypi_device.input_fifo);
-                       goto out_infifo;
+                       goto err_inpdev_unregister;
                }
 
-               INIT_WORK(&sonypi_device.input_work, input_keyrelease, NULL);
-       }
-
-       sonypi_device.pdev = platform_device_register_simple("sonypi", -1,
-                                                            NULL, 0);
-       if (IS_ERR(sonypi_device.pdev)) {
-               ret = PTR_ERR(sonypi_device.pdev);
-               goto out_platformdev;
+               INIT_WORK(&sonypi_device.input_work, input_keyrelease);
        }
 
        sonypi_enable(0);
 
-       printk(KERN_INFO "sonypi: Sony Programmable I/O Controller Driver"
-              "v%s.\n", SONYPI_DRIVER_VERSION);
-       printk(KERN_INFO "sonypi: detected %s model, "
-              "verbose = %d, fnkeyinit = %s, camera = %s, "
-              "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n",
-              (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) ?
-                       "type1" : "type2",
-              verbose,
-              fnkeyinit ? "on" : "off",
-              camera ? "on" : "off",
-              compat ? "on" : "off",
-              mask,
-              useinput ? "on" : "off",
-              SONYPI_ACPI_ACTIVE ? "on" : "off");
-       printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
-              sonypi_device.irq,
-              sonypi_device.ioport1, sonypi_device.ioport2);
-
-       if (minor == -1)
-               printk(KERN_INFO "sonypi: device allocated minor is %d\n",
-                      sonypi_misc_device.minor);
-
        return 0;
 
-out_platformdev:
-       kfifo_free(sonypi_device.input_fifo);
-out_infifo:
-       input_unregister_device(&sonypi_device.input_key_dev);
-       kfree(sonypi_device.input_key_dev.name);
-out_inkmallocinput2:
-       input_unregister_device(&sonypi_device.input_jog_dev);
-       kfree(sonypi_device.input_jog_dev.name);
-out_inkmallocinput1:
+ err_inpdev_unregister:
+       input_unregister_device(sonypi_device.input_key_dev);
+       input_unregister_device(sonypi_device.input_jog_dev);
+ err_miscdev_unregister:
+       misc_deregister(&sonypi_misc_device);
+ err_free_irq:
        free_irq(sonypi_device.irq, sonypi_irq);
-out_reqirq:
+ err_free_ioports:
        release_region(sonypi_device.ioport1, sonypi_device.region_size);
-out_reqreg:
-       misc_deregister(&sonypi_misc_device);
-out_miscreg:
+ err_disable_pcidev:
        if (pcidev)
                pci_disable_device(pcidev);
-out_pcienable:
-       kfifo_free(sonypi_device.fifo);
-out_fifo:
-       pci_dev_put(sonypi_device.dev);
-       return ret;
+ err_put_pcidev:
+       pci_dev_put(pcidev);
+       kfifo_free(&sonypi_device.fifo);
+
+       return error;
 }
 
-static void __devexit sonypi_remove(void)
+static int __devexit sonypi_remove(struct platform_device *dev)
 {
        sonypi_disable();
 
-       platform_device_unregister(sonypi_device.pdev);
+       synchronize_irq(sonypi_device.irq);
+       flush_scheduled_work();
 
        if (useinput) {
-               input_unregister_device(&sonypi_device.input_key_dev);
-               kfree(sonypi_device.input_key_dev.name);
-               input_unregister_device(&sonypi_device.input_jog_dev);
-               kfree(sonypi_device.input_jog_dev.name);
-               kfifo_free(sonypi_device.input_fifo);
+               input_unregister_device(sonypi_device.input_key_dev);
+               input_unregister_device(sonypi_device.input_jog_dev);
+               kfifo_free(&sonypi_device.input_fifo);
        }
 
+       misc_deregister(&sonypi_misc_device);
+
        free_irq(sonypi_device.irq, sonypi_irq);
        release_region(sonypi_device.ioport1, sonypi_device.region_size);
-       misc_deregister(&sonypi_misc_device);
-       if (sonypi_device.dev)
+
+       if (sonypi_device.dev) {
                pci_disable_device(sonypi_device.dev);
-       kfifo_free(sonypi_device.fifo);
-       pci_dev_put(sonypi_device.dev);
-       printk(KERN_INFO "sonypi: removed.\n");
+               pci_dev_put(sonypi_device.dev);
+       }
+
+       kfifo_free(&sonypi_device.fifo);
+
+       return 0;
 }
 
+#ifdef CONFIG_PM
+static int old_camera_power;
+
+static int sonypi_suspend(struct platform_device *dev, pm_message_t state)
+{
+       old_camera_power = sonypi_device.camera_power;
+       sonypi_disable();
+
+       return 0;
+}
+
+static int sonypi_resume(struct platform_device *dev)
+{
+       sonypi_enable(old_camera_power);
+       return 0;
+}
+#else
+#define sonypi_suspend NULL
+#define sonypi_resume  NULL
+#endif
+
+static void sonypi_shutdown(struct platform_device *dev)
+{
+       sonypi_disable();
+}
+
+static struct platform_driver sonypi_driver = {
+       .driver         = {
+               .name   = "sonypi",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sonypi_probe,
+       .remove         = __devexit_p(sonypi_remove),
+       .shutdown       = sonypi_shutdown,
+       .suspend        = sonypi_suspend,
+       .resume         = sonypi_resume,
+};
+
+static struct platform_device *sonypi_platform_device;
+
 static struct dmi_system_id __initdata sonypi_dmi_table[] = {
        {
                .ident = "Sony Vaio",
@@ -1377,26 +1517,52 @@ static struct dmi_system_id __initdata sonypi_dmi_table[] = {
 
 static int __init sonypi_init(void)
 {
-       int ret;
+       int error;
+
+       printk(KERN_INFO
+               "sonypi: Sony Programmable I/O Controller Driver v%s.\n",
+               SONYPI_DRIVER_VERSION);
 
        if (!dmi_check_system(sonypi_dmi_table))
                return -ENODEV;
 
-       ret = driver_register(&sonypi_driver);
-       if (ret)
-               return ret;
+       error = platform_driver_register(&sonypi_driver);
+       if (error)
+               return error;
 
-       ret = sonypi_probe();
-       if (ret)
-               driver_unregister(&sonypi_driver);
+       sonypi_platform_device = platform_device_alloc("sonypi", -1);
+       if (!sonypi_platform_device) {
+               error = -ENOMEM;
+               goto err_driver_unregister;
+       }
 
-       return ret;
+       error = platform_device_add(sonypi_platform_device);
+       if (error)
+               goto err_free_device;
+
+#ifdef CONFIG_ACPI
+       if (acpi_bus_register_driver(&sonypi_acpi_driver) >= 0)
+               acpi_driver_registered = 1;
+#endif
+
+       return 0;
+
+ err_free_device:
+       platform_device_put(sonypi_platform_device);
+ err_driver_unregister:
+       platform_driver_unregister(&sonypi_driver);
+       return error;
 }
 
 static void __exit sonypi_exit(void)
 {
-       driver_unregister(&sonypi_driver);
-       sonypi_remove();
+#ifdef CONFIG_ACPI
+       if (acpi_driver_registered)
+               acpi_bus_unregister_driver(&sonypi_acpi_driver);
+#endif
+       platform_device_unregister(sonypi_platform_device);
+       platform_driver_unregister(&sonypi_driver);
+       printk(KERN_INFO "sonypi: removed.\n");
 }
 
 module_init(sonypi_init);