Merge git://git.infradead.org/iommu-2.6
[sfrench/cifs-2.6.git] / drivers / platform / x86 / sony-laptop.c
index f6cdc8929fd7ee8e739f9135e512d30843424e5f..a90ec5cb2f20dcb547df43cb7a0f5f45c9abeab9 100644 (file)
@@ -2,7 +2,7 @@
  * ACPI Sony Notebook Control Driver (SNC and SPIC)
  *
  * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
- * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+ * Copyright (C) 2007-2009 Mattia Dongili <malattia@linux.it>
  *
  * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
  * which are copyrighted by their respective authors.
@@ -46,7 +46,6 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
-#include <linux/smp_lock.h>
 #include <linux/types.h>
 #include <linux/backlight.h>
 #include <linux/platform_device.h>
@@ -64,6 +63,7 @@
 #include <asm/uaccess.h>
 #include <linux/sonypi.h>
 #include <linux/sony-laptop.h>
+#include <linux/rfkill.h>
 #ifdef CONFIG_SONYPI_COMPAT
 #include <linux/poll.h>
 #include <linux/miscdevice.h>
@@ -123,6 +123,18 @@ MODULE_PARM_DESC(minor,
                 "default is -1 (automatic)");
 #endif
 
+enum sony_nc_rfkill {
+       SONY_WIFI,
+       SONY_BLUETOOTH,
+       SONY_WWAN,
+       SONY_WIMAX,
+       SONY_RFKILL_MAX,
+};
+
+static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
+static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
+static void sony_nc_rfkill_update(void);
+
 /*********** Input Devices ***********/
 
 #define SONY_LAPTOP_BUF_SIZE   128
@@ -134,6 +146,7 @@ struct sony_laptop_input_s {
        spinlock_t              fifo_lock;
        struct workqueue_struct *wq;
 };
+
 static struct sony_laptop_input_s sony_laptop_input = {
        .users = ATOMIC_INIT(0),
 };
@@ -212,6 +225,13 @@ static int sony_laptop_input_index[] = {
        49,     /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
        50,     /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
        51,     /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */
+       52,     /* 65 SONYPI_EVENT_MODEKEY_PRESSED */
+       53,     /* 66 SONYPI_EVENT_PKEY_P4 */
+       54,     /* 67 SONYPI_EVENT_PKEY_P5 */
+       55,     /* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */
+       56,     /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */
+       57,     /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
+       -1,     /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
 };
 
 static int sony_laptop_input_keycode_map[] = {
@@ -266,7 +286,13 @@ static int sony_laptop_input_keycode_map[] = {
        KEY_WLAN,       /* 48 SONYPI_EVENT_WIRELESS_OFF */
        KEY_ZOOMIN,     /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
        KEY_ZOOMOUT,    /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
-       KEY_EJECTCD     /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */
+       KEY_EJECTCD,    /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */
+       KEY_F13,        /* 52 SONYPI_EVENT_MODEKEY_PRESSED */
+       KEY_PROG4,      /* 53 SONYPI_EVENT_PKEY_P4 */
+       KEY_F14,        /* 54 SONYPI_EVENT_PKEY_P5 */
+       KEY_F15,        /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */
+       KEY_VOLUMEUP,   /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
+       KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
 };
 
 /* release buttons after a short delay if pressed */
@@ -371,7 +397,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
        sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
        if (!sony_laptop_input.wq) {
                printk(KERN_ERR DRV_PFX
-                               "Unabe to create workqueue.\n");
+                               "Unable to create workqueue.\n");
                error = -ENXIO;
                goto err_free_kfifo;
        }
@@ -836,13 +862,19 @@ struct sony_nc_event {
        u8      event;
 };
 
-static struct sony_nc_event sony_nc_events[] = {
+static struct sony_nc_event sony_100_events[] = {
        { 0x90, SONYPI_EVENT_PKEY_P1 },
        { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED },
-       { 0x91, SONYPI_EVENT_PKEY_P1 },
+       { 0x91, SONYPI_EVENT_PKEY_P2 },
        { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0x81, SONYPI_EVENT_FNKEY_F1 },
        { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x82, SONYPI_EVENT_FNKEY_F2 },
+       { 0x02, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x83, SONYPI_EVENT_FNKEY_F3 },
+       { 0x03, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x84, SONYPI_EVENT_FNKEY_F4 },
+       { 0x04, SONYPI_EVENT_FNKEY_RELEASED },
        { 0x85, SONYPI_EVENT_FNKEY_F5 },
        { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
        { 0x86, SONYPI_EVENT_FNKEY_F6 },
@@ -860,42 +892,83 @@ static struct sony_nc_event sony_nc_events[] = {
        { 0, 0 },
 };
 
+static struct sony_nc_event sony_127_events[] = {
+       { 0x81, SONYPI_EVENT_MODEKEY_PRESSED },
+       { 0x01, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x82, SONYPI_EVENT_PKEY_P1 },
+       { 0x02, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x83, SONYPI_EVENT_PKEY_P2 },
+       { 0x03, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x84, SONYPI_EVENT_PKEY_P3 },
+       { 0x04, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x85, SONYPI_EVENT_PKEY_P4 },
+       { 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x86, SONYPI_EVENT_PKEY_P5 },
+       { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED },
+       { 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0, 0 },
+};
+
 /*
  * ACPI callbacks
  */
 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
 {
        u32 ev = event;
-       int result;
 
        if (ev >= 0x90) {
                /* New-style event */
-               int origev = ev;
+               int result;
+               int key_handle = 0;
                ev -= 0x90;
 
-               if (sony_find_snc_handle(0x100) == ev) {
-                       int i;
-
-                       if (sony_call_snc_handle(0x100, 0x200, &result))
-                               dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
-                       else
+               if (sony_find_snc_handle(0x100) == ev)
+                       key_handle = 0x100;
+               if (sony_find_snc_handle(0x127) == ev)
+                       key_handle = 0x127;
+
+               if (key_handle) {
+                       struct sony_nc_event *key_event;
+
+                       if (sony_call_snc_handle(key_handle, 0x200, &result)) {
+                               dprintk("sony_acpi_notify, unable to decode"
+                                       " event 0x%.2x 0x%.2x\n", key_handle,
+                                       ev);
+                               /* restore the original event */
+                               ev = event;
+                       } else {
                                ev = result & 0xFF;
 
-                       for (i = 0; sony_nc_events[i].event; i++) {
-                               if (sony_nc_events[i].data == ev) {
-                                       ev = sony_nc_events[i].event;
-                                       break;
+                               if (key_handle == 0x100)
+                                       key_event = sony_100_events;
+                               else
+                                       key_event = sony_127_events;
+
+                               for (; key_event->data; key_event++) {
+                                       if (key_event->data == ev) {
+                                               ev = key_event->event;
+                                               break;
+                                       }
                                }
-                       }
 
-                       if (!sony_nc_events[i].data)
-                               printk(KERN_INFO DRV_PFX
-                                      "Unknown event: %x %x\n", origev, ev);
+                               if (!key_event->data)
+                                       printk(KERN_INFO DRV_PFX
+                                                       "Unknown event: 0x%x 0x%x\n",
+                                                       key_handle,
+                                                       ev);
+                               else
+                                       sony_laptop_report_input_event(ev);
+                       }
+               } else if (sony_find_snc_handle(0x124) == ev) {
+                       sony_nc_rfkill_update();
+                       return;
                }
-       }
+       } else
+               sony_laptop_report_input_event(ev);
 
        dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
-       sony_laptop_report_input_event(ev);
        acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
 }
 
@@ -973,6 +1046,172 @@ static int sony_nc_resume(struct acpi_device *device)
        return 0;
 }
 
+static void sony_nc_rfkill_cleanup(void)
+{
+       int i;
+
+       for (i = 0; i < SONY_RFKILL_MAX; i++) {
+               if (sony_rfkill_devices[i])
+                       rfkill_unregister(sony_rfkill_devices[i]);
+       }
+}
+
+static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
+{
+       int result;
+       int argument = sony_rfkill_address[(long) data];
+
+       sony_call_snc_handle(0x124, 0x200, &result);
+       if (result & 0x1) {
+               sony_call_snc_handle(0x124, argument, &result);
+               if (result & 0xf)
+                       *state = RFKILL_STATE_UNBLOCKED;
+               else
+                       *state = RFKILL_STATE_SOFT_BLOCKED;
+       } else {
+               *state = RFKILL_STATE_HARD_BLOCKED;
+       }
+
+       return 0;
+}
+
+static int sony_nc_rfkill_set(void *data, enum rfkill_state state)
+{
+       int result;
+       int argument = sony_rfkill_address[(long) data] + 0x100;
+
+       if (state == RFKILL_STATE_UNBLOCKED)
+               argument |= 0xff0000;
+
+       return sony_call_snc_handle(0x124, argument, &result);
+}
+
+static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wifi_rfkill;
+
+       sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
+       if (!sony_wifi_rfkill)
+               return -1;
+       sony_wifi_rfkill->name = "sony-wifi";
+       sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wifi_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wifi_rfkill->user_claim_unsupported = 1;
+       sony_wifi_rfkill->data = (void *)SONY_WIFI;
+       err = rfkill_register(sony_wifi_rfkill);
+       if (err)
+               rfkill_free(sony_wifi_rfkill);
+       else
+               sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_bluetooth_rfkill;
+
+       sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
+                                               RFKILL_TYPE_BLUETOOTH);
+       if (!sony_bluetooth_rfkill)
+               return -1;
+       sony_bluetooth_rfkill->name = "sony-bluetooth";
+       sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get;
+       sony_bluetooth_rfkill->user_claim_unsupported = 1;
+       sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH;
+       err = rfkill_register(sony_bluetooth_rfkill);
+       if (err)
+               rfkill_free(sony_bluetooth_rfkill);
+       else
+               sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wwan_rfkill;
+
+       sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+       if (!sony_wwan_rfkill)
+               return -1;
+       sony_wwan_rfkill->name = "sony-wwan";
+       sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wwan_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wwan_rfkill->user_claim_unsupported = 1;
+       sony_wwan_rfkill->data = (void *)SONY_WWAN;
+       err = rfkill_register(sony_wwan_rfkill);
+       if (err)
+               rfkill_free(sony_wwan_rfkill);
+       else
+               sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wimax_rfkill;
+
+       sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+       if (!sony_wimax_rfkill)
+               return -1;
+       sony_wimax_rfkill->name = "sony-wimax";
+       sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wimax_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wimax_rfkill->user_claim_unsupported = 1;
+       sony_wimax_rfkill->data = (void *)SONY_WIMAX;
+       err = rfkill_register(sony_wimax_rfkill);
+       if (err)
+               rfkill_free(sony_wimax_rfkill);
+       else
+               sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
+       return err;
+}
+
+static void sony_nc_rfkill_update()
+{
+       int i;
+       enum rfkill_state state;
+
+       for (i = 0; i < SONY_RFKILL_MAX; i++) {
+               if (sony_rfkill_devices[i]) {
+                       sony_rfkill_devices[i]->
+                               get_state(sony_rfkill_devices[i]->data,
+                                         &state);
+                       rfkill_force_state(sony_rfkill_devices[i], state);
+               }
+       }
+}
+
+static int sony_nc_rfkill_setup(struct acpi_device *device)
+{
+       int result, ret;
+
+       if (sony_find_snc_handle(0x124) == -1)
+               return -1;
+
+       ret = sony_call_snc_handle(0x124, 0xb00, &result);
+       if (ret) {
+               printk(KERN_INFO DRV_PFX
+                      "Unable to enumerate rfkill devices: %x\n", ret);
+               return ret;
+       }
+
+       if (result & 0x1)
+               sony_nc_setup_wifi_rfkill(device);
+       if (result & 0x2)
+               sony_nc_setup_bluetooth_rfkill(device);
+       if (result & 0x1c)
+               sony_nc_setup_wwan_rfkill(device);
+       if (result & 0x20)
+               sony_nc_setup_wimax_rfkill(device);
+
+       return 0;
+}
+
 static int sony_nc_add(struct acpi_device *device)
 {
        acpi_status status;
@@ -1026,13 +1265,14 @@ static int sony_nc_add(struct acpi_device *device)
                                         &handle))) {
                dprintk("Doing SNC setup\n");
                sony_nc_function_setup(device);
+               sony_nc_rfkill_setup(device);
        }
 
        /* setup input devices and helper fifo */
        result = sony_laptop_setup_input(device);
        if (result) {
                printk(KERN_ERR DRV_PFX
-                               "Unabe to create input devices.\n");
+                               "Unable to create input devices.\n");
                goto outwalk;
        }
 
@@ -1132,6 +1372,7 @@ static int sony_nc_add(struct acpi_device *device)
        sony_laptop_remove_input();
 
       outwalk:
+       sony_nc_rfkill_cleanup();
        return result;
 }
 
@@ -1157,6 +1398,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
 
        sony_pf_remove();
        sony_laptop_remove_input();
+       sony_nc_rfkill_cleanup();
        dprintk(SONY_NC_DRIVER_NAME " removed.\n");
 
        return 0;
@@ -1196,7 +1438,6 @@ static struct acpi_driver sony_nc_driver = {
 #define SONYPI_TYPE1_OFFSET    0x04
 #define SONYPI_TYPE2_OFFSET    0x12
 #define SONYPI_TYPE3_OFFSET    0x12
-#define SONYPI_TYPE4_OFFSET    0x12
 
 struct sony_pic_ioport {
        struct acpi_resource_io io1;
@@ -1329,6 +1570,7 @@ static struct sonypi_event sonypi_pkeyev[] = {
        { 0x01, SONYPI_EVENT_PKEY_P1 },
        { 0x02, SONYPI_EVENT_PKEY_P2 },
        { 0x04, SONYPI_EVENT_PKEY_P3 },
+       { 0x20, SONYPI_EVENT_PKEY_P1 },
        { 0, 0 }
 };
 
@@ -1372,6 +1614,7 @@ static struct sonypi_event sonypi_zoomev[] = {
        { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
        { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
        { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
+       { 0x04, SONYPI_EVENT_ZOOM_PRESSED },
        { 0, 0 }
 };
 
@@ -1402,6 +1645,19 @@ static struct sonypi_event sonypi_batteryev[] = {
        { 0, 0 }
 };
 
+/* The set of possible volume events */
+static struct sonypi_event sonypi_volumeev[] = {
+       { 0x01, SONYPI_EVENT_VOLUME_INC_PRESSED },
+       { 0x02, SONYPI_EVENT_VOLUME_DEC_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible brightness events */
+static struct sonypi_event sonypi_brightnessev[] = {
+       { 0x80, SONYPI_EVENT_BRIGHTNESS_PRESSED },
+       { 0, 0 }
+};
+
 static struct sonypi_eventtypes type1_events[] = {
        { 0, 0xffffffff, sonypi_releaseev },
        { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
@@ -1439,17 +1695,11 @@ static struct sonypi_eventtypes type3_events[] = {
        { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
        { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
        { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0 },
-};
-static struct sonypi_eventtypes type4_events[] = {
-       { 0, 0xffffffff, sonypi_releaseev },
-       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
-       { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
-       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
-       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
        { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },
        { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },
        { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { 0x05, SONYPI_PKEY_MASK, sonypi_volumeev },
+       { 0x05, SONYPI_PKEY_MASK, sonypi_brightnessev },
        { 0 },
 };
 
@@ -1512,11 +1762,11 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
 /*
  * minidrivers for SPIC models
  */
-static int type4_handle_irq(const u8 data_mask, const u8 ev)
+static int type3_handle_irq(const u8 data_mask, const u8 ev)
 {
        /*
         * 0x31 could mean we have to take some extra action and wait for
-        * the next irq for some Type4 models, it will generate a new
+        * the next irq for some Type3 models, it will generate a new
         * irq and we can read new data from the device:
         *  - 0x5c and 0x5f requires 0xA0
         *  - 0x61 requires 0xB3
@@ -1546,16 +1796,10 @@ static struct device_ctrl spic_types[] = {
        },
        {
                .model = SONYPI_DEVICE_TYPE3,
-               .handle_irq = NULL,
+               .handle_irq = type3_handle_irq,
                .evport_offset = SONYPI_TYPE3_OFFSET,
                .event_types = type3_events,
        },
-       {
-               .model = SONYPI_DEVICE_TYPE4,
-               .handle_irq = type4_handle_irq,
-               .evport_offset = SONYPI_TYPE4_OFFSET,
-               .event_types = type4_events,
-       },
 };
 
 static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
@@ -1579,14 +1823,21 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
        if (pcidev) {
-               dev->control = &spic_types[3];
+               dev->control = &spic_types[2];
                goto out;
        }
 
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
        if (pcidev) {
-               dev->control = &spic_types[3];
+               dev->control = &spic_types[2];
+               goto out;
+       }
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_ICH9_1, NULL);
+       if (pcidev) {
+               dev->control = &spic_types[2];
                goto out;
        }
 
@@ -1599,8 +1850,7 @@ out:
 
        printk(KERN_INFO DRV_PFX "detected Type%d model\n",
                        dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 :
-                       dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 :
-                       dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4);
+                       dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
 }
 
 /* camera tests and poweron/poweroff */
@@ -1755,17 +2005,14 @@ int sony_pic_camera_command(int command, u8 value)
 EXPORT_SYMBOL(sony_pic_camera_command);
 
 /* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
-static void sony_pic_set_wwanpower(u8 state)
+static void __sony_pic_set_wwanpower(u8 state)
 {
        state = !!state;
-       mutex_lock(&spic_dev.lock);
-       if (spic_dev.wwan_power == state) {
-               mutex_unlock(&spic_dev.lock);
+       if (spic_dev.wwan_power == state)
                return;
-       }
        sony_pic_call2(0xB0, state);
+       sony_pic_call1(0x82);
        spic_dev.wwan_power = state;
-       mutex_unlock(&spic_dev.lock);
 }
 
 static ssize_t sony_pic_wwanpower_store(struct device *dev,
@@ -1777,7 +2024,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev,
                return -EINVAL;
 
        value = simple_strtoul(buffer, NULL, 10);
-       sony_pic_set_wwanpower(value);
+       mutex_lock(&spic_dev.lock);
+       __sony_pic_set_wwanpower(value);
+       mutex_unlock(&spic_dev.lock);
 
        return count;
 }
@@ -1918,12 +2167,7 @@ static struct sonypi_compat_s sonypi_compat = {
 
 static int sonypi_misc_fasync(int fd, struct file *filp, int on)
 {
-       int retval;
-
-       retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
-       if (retval < 0)
-               return retval;
-       return 0;
+       return fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
 }
 
 static int sonypi_misc_release(struct inode *inode, struct file *file)
@@ -1935,10 +2179,15 @@ static int sonypi_misc_release(struct inode *inode, struct file *file)
 static int sonypi_misc_open(struct inode *inode, struct file *file)
 {
        /* Flush input queue on first open */
-       lock_kernel();
+       unsigned long flags;
+
+       spin_lock_irqsave(sonypi_compat.fifo->lock, flags);
+
        if (atomic_inc_return(&sonypi_compat.open_count) == 1)
-               kfifo_reset(sonypi_compat.fifo);
-       unlock_kernel();
+               __kfifo_reset(sonypi_compat.fifo);
+
+       spin_unlock_irqrestore(sonypi_compat.fifo->lock, flags);
+
        return 0;
 }
 
@@ -1991,8 +2240,8 @@ static int ec_read16(u8 addr, u16 *value)
        return 0;
 }
 
-static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
-                            unsigned int cmd, unsigned long arg)
+static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
+                                                       unsigned long arg)
 {
        int ret = 0;
        void __user *argp = (void __user *)arg;
@@ -2126,7 +2375,7 @@ static const struct file_operations sonypi_misc_fops = {
        .open           = sonypi_misc_open,
        .release        = sonypi_misc_release,
        .fasync         = sonypi_misc_fasync,
-       .ioctl          = sonypi_misc_ioctl,
+       .unlocked_ioctl = sonypi_misc_ioctl,
 };
 
 static struct miscdevice sonypi_misc_device = {
@@ -2567,7 +2816,7 @@ static int sony_pic_add(struct acpi_device *device)
        result = sony_pic_possible_resources(device);
        if (result) {
                printk(KERN_ERR DRV_PFX
-                               "Unabe to read possible resources.\n");
+                               "Unable to read possible resources.\n");
                goto err_free_resources;
        }
 
@@ -2575,7 +2824,7 @@ static int sony_pic_add(struct acpi_device *device)
        result = sony_laptop_setup_input(device);
        if (result) {
                printk(KERN_ERR DRV_PFX
-                               "Unabe to create input devices.\n");
+                               "Unable to create input devices.\n");
                goto err_free_resources;
        }