Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (44 commits)
  eeepc-wmi: Add cpufv sysfs interface
  eeepc-wmi: add additional hotkeys
  panasonic-laptop: Simplify calls to acpi_pcc_retrieve_biosdata
  panasonic-laptop: Handle errors properly if they happen
  intel_pmic_gpio: fix off-by-one value range checking
  IBM Real-Time "SMI Free" mode driver -v7
  Add OLPC XO-1 rfkill driver
  Move hdaps driver to platform/x86
  ideapad-laptop: Fix Makefile
  intel_pmic_gpio: swap the bits and mask args for intel_scu_ipc_update_register
  ideapad: Add param: no_bt_rfkill
  ideapad: Change the driver name to ideapad-laptop
  ideapad: rewrite the sw rfkill set
  ideapad: rewrite the hw rfkill notify
  ideapad: use EC command to control camera
  ideapad: use return value of _CFG to tell if device exist or not
  ideapad: make sure we bind on the correct device
  ideapad: check VPC bit before sync rfkill hw status
  ideapad: add ACPI helpers
  dell-laptop: Add debugfs support
  ...
diff --git a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl
new file mode 100644
index 0000000..b82deea
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl
@@ -0,0 +1,22 @@
+What:           state
+Date:           Sep 2010
+KernelVersion:  2.6.37
+Contact:        Vernon Mauery <vernux@us.ibm.com>
+Description:    The state file allows a means by which to change in and
+                out of Premium Real-Time Mode (PRTM), as well as the
+                ability to query the current state.
+                    0 => PRTM off
+                    1 => PRTM enabled
+Users:          The ibm-prtm userspace daemon uses this interface.
+
+
+What:           version
+Date:           Sep 2010
+KernelVersion:  2.6.37
+Contact:        Vernon Mauery <vernux@us.ibm.com>
+Description:    The version file provides a means by which to query
+                the RTL table version that lives in the Extended
+                BIOS Data Area (EBDA).
+Users:          The ibm-prtm userspace daemon uses this interface.
+
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 69aa8fe..3e95a81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2646,10 +2646,10 @@
 
 HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
 M:	Frank Seidel <frank@f-seidel.de>
-L:	lm-sensors@lm-sensors.org
+L:	platform-driver-x86@vger.kernel.org
 W:	http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
 S:	Maintained
-F:	drivers/hwmon/hdaps.c
+F:	drivers/platform/x86/hdaps.c
 
 HWPOISON MEMORY FAILURE HANDLING
 M:	Andi Kleen <andi@firstfloor.org>
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 101229b..42a978c 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -89,6 +89,8 @@
 /* EC commands */
 
 #define EC_FIRMWARE_REV		0x08
+#define EC_WLAN_ENTER_RESET	0x35
+#define EC_WLAN_LEAVE_RESET	0x25
 
 /* SCI source values */
 
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 97499d0..e382da3 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1088,26 +1088,6 @@
 	  This driver provides support for the Ultra45 workstation environmental
 	  sensors.
 
-config SENSORS_HDAPS
-	tristate "IBM Hard Drive Active Protection System (hdaps)"
-	depends on INPUT && X86
-	select INPUT_POLLDEV
-	default n
-	help
-	  This driver provides support for the IBM Hard Drive Active Protection
-	  System (hdaps), which provides an accelerometer and other misc. data.
-	  ThinkPads starting with the R50, T41, and X40 are supported.  The
-	  accelerometer data is readable via sysfs.
-
-	  This driver also provides an absolute input class device, allowing
-	  the laptop to act as a pinball machine-esque joystick.
-
-	  If your ThinkPad is not recognized by the driver, please update to latest
-	  BIOS. This is especially the case for some R52 ThinkPads.
-
-	  Say Y here if you have an applicable laptop and want to experience
-	  the awesome power of hdaps.
-
 config SENSORS_LIS3_SPI
 	tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
 	depends on !ACPI && SPI_MASTER && INPUT
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e3c2484..ec9cb73 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -52,7 +52,6 @@
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)	+= gl520sm.o
 obj-$(CONFIG_SENSORS_ULTRA45)	+= ultra45_env.o
-obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
 obj-$(CONFIG_SENSORS_IBMAEM)	+= ibmaem.o
 obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index cff7cc2..faec777 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -92,6 +92,7 @@
 	tristate "Dell WMI extras"
 	depends on ACPI_WMI
 	depends on INPUT
+	select INPUT_SPARSEKMAP
 	---help---
 	  Say Y here if you want to support WMI-based hotkeys on Dell laptops.
 
@@ -140,6 +141,7 @@
 	depends on ACPI_WMI
 	depends on INPUT
 	depends on RFKILL || RFKILL = n
+	select INPUT_SPARSEKMAP
 	help
 	 Say Y here if you want to support WMI-based hotkeys on HP laptops and
 	 to read data from WMI such as docking or ambient light sensor state.
@@ -171,6 +173,7 @@
 	tristate "Panasonic Laptop Extras"
 	depends on INPUT && ACPI
 	depends on BACKLIGHT_CLASS_DEVICE
+	select INPUT_SPARSEKMAP
 	---help---
 	  This driver adds support for access to backlight control and hotkeys
 	  on Panasonic Let's Note laptops.
@@ -219,8 +222,8 @@
 	  ---help---
 	  Build the sonypi driver compatibility code into the sony-laptop driver.
 
-config IDEAPAD_ACPI
-	tristate "Lenovo IdeaPad ACPI Laptop Extras"
+config IDEAPAD_LAPTOP
+	tristate "Lenovo IdeaPad Laptop Extras"
 	depends on ACPI
 	depends on RFKILL
 	help
@@ -365,6 +368,26 @@
 	  If you are not sure, say Y here.  The driver enables polling only if
 	  it is strictly necessary to do so.
 
+config SENSORS_HDAPS
+	tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
+	depends on INPUT && X86
+	select INPUT_POLLDEV
+	default n
+	help
+	  This driver provides support for the IBM Hard Drive Active Protection
+	  System (hdaps), which provides an accelerometer and other misc. data.
+	  ThinkPads starting with the R50, T41, and X40 are supported.  The
+	  accelerometer data is readable via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  If your ThinkPad is not recognized by the driver, please update to latest
+	  BIOS. This is especially the case for some R52 ThinkPads.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of hdaps.
+
 config INTEL_MENLOW
 	tristate "Thermal Management driver for Intel menlow platform"
 	depends on ACPI_THERMAL
@@ -478,6 +501,7 @@
 	tristate "Topstar Laptop Extras"
 	depends on ACPI
 	depends on INPUT
+	select INPUT_SPARSEKMAP
 	---help---
 	  This driver adds support for hotkeys found on Topstar laptops.
 
@@ -492,6 +516,7 @@
 	depends on INPUT
 	depends on RFKILL || RFKILL = n
 	select INPUT_POLLDEV
+	select INPUT_SPARSEKMAP
 	---help---
 	  This driver adds support for access to certain system settings
 	  on "legacy free" Toshiba laptops.  These laptops can be recognized by
@@ -590,4 +615,28 @@
 	  functionality.  If in doubt, say Y here; it will only load on
 	  supported platforms.
 
+config IBM_RTL
+	tristate "Device driver to enable PRTL support"
+	depends on X86 && PCI
+	---help---
+	 Enable support for IBM Premium Real Time Mode (PRTM).
+	 This module will allow you the enter and exit PRTM in the BIOS via
+	 sysfs on platforms that support this feature.  System in PRTM will
+	 not receive CPU-generated SMIs for recoverable errors.  Use of this
+	 feature without proper support may void your hardware warranty.
+
+	 If the proper BIOS support is found the driver will load and create
+	 /sys/devices/system/ibm_rtl/.  The "state" variable will indicate
+	 whether or not the BIOS is in PRTM.
+	 state = 0 (BIOS SMIs on)
+	 state = 1 (BIOS SMIs off)
+
+config XO1_RFKILL
+	tristate "OLPC XO-1 software RF kill switch"
+	depends on OLPC
+	depends on RFKILL
+	---help---
+	  Support for enabling/disabling the WLAN interface on the OLPC XO-1
+	  laptop.
+
 endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 85fb2b84..9950ccc 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -15,8 +15,9 @@
 obj-$(CONFIG_HP_WMI)		+= hp-wmi.o
 obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
-obj-$(CONFIG_IDEAPAD_ACPI)	+= ideapad_acpi.o
+obj-$(CONFIG_IDEAPAD_LAPTOP)	+= ideapad-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
+obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
 obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
 obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o
 obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o
@@ -30,4 +31,5 @@
 obj-$(CONFIG_RAR_REGISTER)	+= intel_rar_register.o
 obj-$(CONFIG_INTEL_IPS)		+= intel_ips.o
 obj-$(CONFIG_GPIO_INTEL_PMIC)	+= intel_pmic_gpio.o
-
+obj-$(CONFIG_XO1_RFKILL)	+= xo1-rfkill.o
+obj-$(CONFIG_IBM_RTL)		+= ibm_rtl.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 2badee2..c8c6537 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -1314,7 +1314,7 @@
 		AMW0_find_mailled();
 
 	if (!interface) {
-		printk(ACER_ERR "No or unsupported WMI interface, unable to "
+		printk(ACER_INFO "No or unsupported WMI interface, unable to "
 				"load\n");
 		return -ENODEV;
 	}
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index b756e07..60a5a5c 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -236,7 +236,6 @@
 	u8 light_level;		/* light sensor level */
 	u8 light_switch;	/* light sensor switch value */
 	u16 event_count[128];	/* count for each event TODO make this better */
-	u16 *keycode_map;
 };
 
 static const struct key_entry asus_keymap[] = {
@@ -278,6 +277,7 @@
 	{KE_KEY, 0x99, { KEY_PHONE } },
 	{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
 	{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
+	{KE_KEY, 0xb5, { KEY_CALC } },
 	{KE_END, 0},
 };
 
@@ -639,29 +639,29 @@
 static int asus_backlight_init(struct asus_laptop *asus)
 {
 	struct backlight_device *bd;
-	struct device *dev = &asus->platform_device->dev;
 	struct backlight_properties props;
 
-	if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) &&
-	    !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) &&
-	    lcd_switch_handle) {
-		memset(&props, 0, sizeof(struct backlight_properties));
-		props.max_brightness = 15;
+	if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
+	    acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
+	    !lcd_switch_handle)
+		return 0;
 
-		bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
-					       asus, &asusbl_ops, &props);
-		if (IS_ERR(bd)) {
-			pr_err("Could not register asus backlight device\n");
-			asus->backlight_device = NULL;
-			return PTR_ERR(bd);
-		}
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.max_brightness = 15;
 
-		asus->backlight_device = bd;
-
-		bd->props.power = FB_BLANK_UNBLANK;
-		bd->props.brightness = asus_read_brightness(bd);
-		backlight_update_status(bd);
+	bd = backlight_device_register(ASUS_LAPTOP_FILE,
+				       &asus->platform_device->dev, asus,
+				       &asusbl_ops, &props);
+	if (IS_ERR(bd)) {
+		pr_err("Could not register asus backlight device\n");
+		asus->backlight_device = NULL;
+		return PTR_ERR(bd);
 	}
+
+	asus->backlight_device = bd;
+	bd->props.brightness = asus_read_brightness(bd);
+	bd->props.power = FB_BLANK_UNBLANK;
+	backlight_update_status(bd);
 	return 0;
 }
 
@@ -1065,9 +1065,9 @@
  */
 static int asus_gps_rfkill_set(void *data, bool blocked)
 {
-	acpi_handle handle = data;
+	struct asus_laptop *asus = data;
 
-	return asus_gps_switch(handle, !blocked);
+	return asus_gps_switch(asus, !blocked);
 }
 
 static const struct rfkill_ops asus_gps_rfkill_ops = {
@@ -1094,7 +1094,7 @@
 
 	asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
 					RFKILL_TYPE_GPS,
-					&asus_gps_rfkill_ops, NULL);
+					&asus_gps_rfkill_ops, asus);
 	if (!asus->gps_rfkill)
 		return -EINVAL;
 
@@ -1130,7 +1130,6 @@
 	input->phys = ASUS_LAPTOP_FILE "/input0";
 	input->id.bustype = BUS_HOST;
 	input->dev.parent = &asus->platform_device->dev;
-	input_set_drvdata(input, asus);
 
 	error = sparse_keymap_setup(input, asus_keymap, NULL);
 	if (error) {
@@ -1159,6 +1158,7 @@
 		sparse_keymap_free(asus->inputdev);
 		input_unregister_device(asus->inputdev);
 	}
+	asus->inputdev = NULL;
 }
 
 /*
@@ -1200,111 +1200,100 @@
 
 static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
 static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
-static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth,
-		   store_bluetooth);
+static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
+		   show_bluetooth, store_bluetooth);
 static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
 static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
 static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
 static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
 static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
 
-static void asus_sysfs_exit(struct asus_laptop *asus)
-{
-	struct platform_device *device = asus->platform_device;
+static struct attribute *asus_attributes[] = {
+	&dev_attr_infos.attr,
+	&dev_attr_wlan.attr,
+	&dev_attr_bluetooth.attr,
+	&dev_attr_display.attr,
+	&dev_attr_ledd.attr,
+	&dev_attr_ls_level.attr,
+	&dev_attr_ls_switch.attr,
+	&dev_attr_gps.attr,
+	NULL
+};
 
-	device_remove_file(&device->dev, &dev_attr_infos);
-	device_remove_file(&device->dev, &dev_attr_wlan);
-	device_remove_file(&device->dev, &dev_attr_bluetooth);
-	device_remove_file(&device->dev, &dev_attr_display);
-	device_remove_file(&device->dev, &dev_attr_ledd);
-	device_remove_file(&device->dev, &dev_attr_ls_switch);
-	device_remove_file(&device->dev, &dev_attr_ls_level);
-	device_remove_file(&device->dev, &dev_attr_gps);
+static mode_t asus_sysfs_is_visible(struct kobject *kobj,
+				    struct attribute *attr,
+				    int idx)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct asus_laptop *asus = platform_get_drvdata(pdev);
+	acpi_handle handle = asus->handle;
+	bool supported;
+
+	if (attr == &dev_attr_wlan.attr) {
+		supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
+
+	} else if (attr == &dev_attr_bluetooth.attr) {
+		supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
+
+	} else if (attr == &dev_attr_display.attr) {
+		supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
+
+	} else if (attr == &dev_attr_ledd.attr) {
+		supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
+
+	} else if (attr == &dev_attr_ls_switch.attr ||
+		   attr == &dev_attr_ls_level.attr) {
+		supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
+			    !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
+
+	} else if (attr == &dev_attr_gps.attr) {
+		supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
+			    !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
+			    !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
+	} else {
+		supported = true;
+	}
+
+	return supported ? attr->mode : 0;
 }
 
-static int asus_sysfs_init(struct asus_laptop *asus)
-{
-	struct platform_device *device = asus->platform_device;
-	int err;
 
-	err = device_create_file(&device->dev, &dev_attr_infos);
-	if (err)
-		return err;
-
-	if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_wlan);
-		if (err)
-			return err;
-	}
-
-	if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_bluetooth);
-		if (err)
-			return err;
-	}
-
-	if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_display);
-		if (err)
-			return err;
-	}
-
-	if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_ledd);
-		if (err)
-			return err;
-	}
-
-	if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
-	    !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_ls_switch);
-		if (err)
-			return err;
-		err = device_create_file(&device->dev, &dev_attr_ls_level);
-		if (err)
-			return err;
-	}
-
-	if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
-	    !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
-	    !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) {
-		err = device_create_file(&device->dev, &dev_attr_gps);
-		if (err)
-			return err;
-	}
-
-	return err;
-}
+static const struct attribute_group asus_attr_group = {
+	.is_visible	= asus_sysfs_is_visible,
+	.attrs		= asus_attributes,
+};
 
 static int asus_platform_init(struct asus_laptop *asus)
 {
-	int err;
+	int result;
 
 	asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
 	if (!asus->platform_device)
 		return -ENOMEM;
 	platform_set_drvdata(asus->platform_device, asus);
 
-	err = platform_device_add(asus->platform_device);
-	if (err)
+	result = platform_device_add(asus->platform_device);
+	if (result)
 		goto fail_platform_device;
 
-	err = asus_sysfs_init(asus);
-	if (err)
+	result = sysfs_create_group(&asus->platform_device->dev.kobj,
+				    &asus_attr_group);
+	if (result)
 		goto fail_sysfs;
+
 	return 0;
 
 fail_sysfs:
-	asus_sysfs_exit(asus);
 	platform_device_del(asus->platform_device);
 fail_platform_device:
 	platform_device_put(asus->platform_device);
-	return err;
+	return result;
 }
 
 static void asus_platform_exit(struct asus_laptop *asus)
 {
-	asus_sysfs_exit(asus);
+	sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
 	platform_device_unregister(asus->platform_device);
 }
 
@@ -1428,8 +1417,6 @@
 	return AE_OK;
 }
 
-static bool asus_device_present;
-
 static int __devinit asus_acpi_init(struct asus_laptop *asus)
 {
 	int result = 0;
@@ -1474,6 +1461,8 @@
 	return result;
 }
 
+static bool asus_device_present;
+
 static int __devinit asus_acpi_add(struct acpi_device *device)
 {
 	struct asus_laptop *asus;
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 4413975..cf8a89a 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -25,6 +25,8 @@
 #include <linux/mm.h>
 #include <linux/i8042.h>
 #include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
@@ -325,6 +327,75 @@
 	.query = dell_rfkill_query,
 };
 
+static struct dentry *dell_laptop_dir;
+
+static int dell_debugfs_show(struct seq_file *s, void *data)
+{
+	int status;
+
+	get_buffer();
+	dell_send_request(buffer, 17, 11);
+	status = buffer->output[1];
+	release_buffer();
+
+	seq_printf(s, "status:\t0x%X\n", status);
+	seq_printf(s, "Bit 0 : Hardware switch supported:   %lu\n",
+		   status & BIT(0));
+	seq_printf(s, "Bit 1 : Wifi locator supported:      %lu\n",
+		  (status & BIT(1)) >> 1);
+	seq_printf(s, "Bit 2 : Wifi is supported:           %lu\n",
+		  (status & BIT(2)) >> 2);
+	seq_printf(s, "Bit 3 : Bluetooth is supported:      %lu\n",
+		  (status & BIT(3)) >> 3);
+	seq_printf(s, "Bit 4 : WWAN is supported:           %lu\n",
+		  (status & BIT(4)) >> 4);
+	seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
+		  (status & BIT(5)) >> 5);
+	seq_printf(s, "Bit 8 : Wifi is installed:           %lu\n",
+		  (status & BIT(8)) >> 8);
+	seq_printf(s, "Bit 9 : Bluetooth is installed:      %lu\n",
+		  (status & BIT(9)) >> 9);
+	seq_printf(s, "Bit 10: WWAN is installed:           %lu\n",
+		  (status & BIT(10)) >> 10);
+	seq_printf(s, "Bit 16: Hardware switch is on:       %lu\n",
+		  (status & BIT(16)) >> 16);
+	seq_printf(s, "Bit 17: Wifi is blocked:             %lu\n",
+		  (status & BIT(17)) >> 17);
+	seq_printf(s, "Bit 18: Bluetooth is blocked:        %lu\n",
+		  (status & BIT(18)) >> 18);
+	seq_printf(s, "Bit 19: WWAN is blocked:             %lu\n",
+		  (status & BIT(19)) >> 19);
+
+	seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
+	seq_printf(s, "Bit 0 : Wifi controlled by switch:      %lu\n",
+		   hwswitch_state & BIT(0));
+	seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
+		   (hwswitch_state & BIT(1)) >> 1);
+	seq_printf(s, "Bit 2 : WWAN controlled by switch:      %lu\n",
+		   (hwswitch_state & BIT(2)) >> 2);
+	seq_printf(s, "Bit 7 : Wireless switch config locked:  %lu\n",
+		   (hwswitch_state & BIT(7)) >> 7);
+	seq_printf(s, "Bit 8 : Wifi locator enabled:           %lu\n",
+		   (hwswitch_state & BIT(8)) >> 8);
+	seq_printf(s, "Bit 15: Wifi locator setting locked:    %lu\n",
+		   (hwswitch_state & BIT(15)) >> 15);
+
+	return 0;
+}
+
+static int dell_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dell_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations dell_debugfs_fops = {
+	.owner = THIS_MODULE,
+	.open = dell_debugfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
 static void dell_update_rfkill(struct work_struct *ignored)
 {
 	if (wifi_rfkill)
@@ -556,6 +627,11 @@
 		goto fail_filter;
 	}
 
+	dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
+	if (dell_laptop_dir != NULL)
+		debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
+				    &dell_debugfs_fops);
+
 #ifdef CONFIG_ACPI
 	/* In the event of an ACPI backlight being available, don't
 	 * register the platform controller.
@@ -615,6 +691,7 @@
 
 static void __exit dell_exit(void)
 {
+	debugfs_remove_recursive(dell_laptop_dir);
 	i8042_remove_filter(dell_laptop_i8042_filter);
 	cancel_delayed_work_sync(&dell_rfkill_work);
 	backlight_device_unregister(dell_backlight_device);
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 08fb70f..77f1d55 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <acpi/acpi_drivers.h>
 #include <linux/acpi.h>
 #include <linux/string.h>
@@ -44,78 +45,70 @@
 
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
 
-struct key_entry {
-	char type;		/* See KE_* below */
-	u16 code;
-	u16 keycode;
-};
-
-enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
-
 /*
  * Certain keys are flagged as KE_IGNORE. All of these are either
  * notifications (rather than requests for change) or are also sent
  * via the keyboard controller so should not be sent again.
  */
 
-static struct key_entry dell_legacy_wmi_keymap[] = {
-	{KE_KEY, 0xe045, KEY_PROG1},
-	{KE_KEY, 0xe009, KEY_EJECTCD},
+static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
+	{ KE_KEY, 0xe045, { KEY_PROG1 } },
+	{ KE_KEY, 0xe009, { KEY_EJECTCD } },
 
 	/* These also contain the brightness level at offset 6 */
-	{KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
+	{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
+	{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
 
 	/* Battery health status button */
-	{KE_KEY, 0xe007, KEY_BATTERY},
+	{ KE_KEY, 0xe007, { KEY_BATTERY } },
 
 	/* This is actually for all radios. Although physically a
 	 * switch, the notification does not provide an indication of
 	 * state and so it should be reported as a key */
-	{KE_KEY, 0xe008, KEY_WLAN},
+	{ KE_KEY, 0xe008, { KEY_WLAN } },
 
 	/* The next device is at offset 6, the active devices are at
 	   offset 8 and the attached devices at offset 10 */
-	{KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
+	{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
 
-	{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
+	{ KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
 
 	/* BIOS error detected */
-	{KE_IGNORE, 0xe00d, KEY_RESERVED},
+	{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
 
 	/* Wifi Catcher */
-	{KE_KEY, 0xe011, KEY_PROG2},
+	{ KE_KEY, 0xe011, {KEY_PROG2 } },
 
 	/* Ambient light sensor toggle */
-	{KE_IGNORE, 0xe013, KEY_RESERVED},
+	{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
 
-	{KE_IGNORE, 0xe020, KEY_MUTE},
-	{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
-	{KE_IGNORE, 0xe030, KEY_VOLUMEUP},
-	{KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
-	{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
-	{KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
-	{KE_IGNORE, 0xe045, KEY_NUMLOCK},
-	{KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
-	{KE_END, 0}
+	{ KE_IGNORE, 0xe020, { KEY_MUTE } },
+	{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
+	{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
+	{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
+	{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
+	{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
+	{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
+	{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
+	{ KE_END, 0 }
 };
 
 static bool dell_new_hk_type;
 
-struct dell_new_keymap_entry {
+struct dell_bios_keymap_entry {
 	u16 scancode;
 	u16 keycode;
 };
 
-struct dell_hotkey_table {
+struct dell_bios_hotkey_table {
 	struct dmi_header header;
-	struct dell_new_keymap_entry keymap[];
+	struct dell_bios_keymap_entry keymap[];
 
 };
 
-static struct key_entry *dell_new_wmi_keymap;
+static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
 
-static u16 bios_to_linux_keycode[256] = {
+static const u16 bios_to_linux_keycode[256] __initconst = {
 
 	KEY_MEDIA,	KEY_NEXTSONG,	KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
 	KEY_STOPCD,	KEY_UNKNOWN,	KEY_UNKNOWN,	KEY_UNKNOWN,
@@ -138,68 +131,11 @@
 	KEY_PROG3
 };
 
-
-static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
-
 static struct input_dev *dell_wmi_input_dev;
 
-static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code)
-{
-	struct key_entry *key;
-
-	for (key = dell_wmi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
-
-	return NULL;
-}
-
-static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode)
-{
-	struct key_entry *key;
-
-	for (key = dell_wmi_keymap; key->type != KE_END; key++)
-		if (key->type == KE_KEY && keycode == key->keycode)
-			return key;
-
-	return NULL;
-}
-
-static int dell_wmi_getkeycode(struct input_dev *dev,
-				unsigned int scancode, unsigned int *keycode)
-{
-	struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
-
-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int dell_wmi_setkeycode(struct input_dev *dev,
-				unsigned int scancode, unsigned int keycode)
-{
-	struct key_entry *key;
-	unsigned int old_keycode;
-
-	key = dell_wmi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!dell_wmi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
-	}
-	return -EINVAL;
-}
-
 static void dell_wmi_notify(u32 value, void *context)
 {
 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
-	static struct key_entry *key;
 	union acpi_object *obj;
 	acpi_status status;
 
@@ -212,8 +148,10 @@
 	obj = (union acpi_object *)response.pointer;
 
 	if (obj && obj->type == ACPI_TYPE_BUFFER) {
+		const struct key_entry *key;
 		int reported_key;
 		u16 *buffer_entry = (u16 *)obj->buffer.pointer;
+
 		if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
 			printk(KERN_INFO "dell-wmi: Received unknown WMI event"
 					 " (0x%x)\n", buffer_entry[1]);
@@ -226,8 +164,8 @@
 		else
 			reported_key = (int)buffer_entry[1] & 0xffff;
 
-		key = dell_wmi_get_entry_by_scancode(reported_key);
-
+		key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+							reported_key);
 		if (!key) {
 			printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
 				reported_key);
@@ -237,92 +175,98 @@
 			 * come via ACPI */
 			;
 		} else {
-			input_report_key(dell_wmi_input_dev, key->keycode, 1);
-			input_sync(dell_wmi_input_dev);
-			input_report_key(dell_wmi_input_dev, key->keycode, 0);
-			input_sync(dell_wmi_input_dev);
+			sparse_keymap_report_entry(dell_wmi_input_dev, key,
+						   1, true);
 		}
 	}
 	kfree(obj);
 }
 
-
-static void setup_new_hk_map(const struct dmi_header *dm)
+static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
 {
-
+	int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
+				sizeof(struct dell_bios_keymap_entry);
+	struct key_entry *keymap;
 	int i;
-	int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
-	struct dell_hotkey_table *table =
-		container_of(dm, struct dell_hotkey_table, header);
 
-	dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
-				      sizeof(struct key_entry), GFP_KERNEL);
+	keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
+	if (!keymap)
+		return NULL;
 
 	for (i = 0; i < hotkey_num; i++) {
-		dell_new_wmi_keymap[i].type = KE_KEY;
-		dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
-		dell_new_wmi_keymap[i].keycode =
-			(table->keymap[i].keycode > 255) ? 0 :
-			bios_to_linux_keycode[table->keymap[i].keycode];
+		const struct dell_bios_keymap_entry *bios_entry =
+					&dell_bios_hotkey_table->keymap[i];
+		keymap[i].type = KE_KEY;
+		keymap[i].code = bios_entry->scancode;
+		keymap[i].keycode = bios_entry->keycode < 256 ?
+				    bios_to_linux_keycode[bios_entry->keycode] :
+				    KEY_RESERVED;
 	}
 
-	dell_new_wmi_keymap[i].type = KE_END;
-	dell_new_wmi_keymap[i].code = 0;
-	dell_new_wmi_keymap[i].keycode = 0;
+	keymap[hotkey_num].type = KE_END;
 
-	dell_wmi_keymap = dell_new_wmi_keymap;
-
+	return keymap;
 }
 
-
-static void find_hk_type(const struct dmi_header *dm, void *dummy)
-{
-
-	if ((dm->type == 0xb2) && (dm->length > 6)) {
-		dell_new_hk_type = true;
-		setup_new_hk_map(dm);
-	}
-
-}
-
-
 static int __init dell_wmi_input_setup(void)
 {
-	struct key_entry *key;
 	int err;
 
 	dell_wmi_input_dev = input_allocate_device();
-
 	if (!dell_wmi_input_dev)
 		return -ENOMEM;
 
 	dell_wmi_input_dev->name = "Dell WMI hotkeys";
 	dell_wmi_input_dev->phys = "wmi/input0";
 	dell_wmi_input_dev->id.bustype = BUS_HOST;
-	dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
-	dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode;
 
-	for (key = dell_wmi_keymap; key->type != KE_END; key++) {
-		switch (key->type) {
-		case KE_KEY:
-			set_bit(EV_KEY, dell_wmi_input_dev->evbit);
-			set_bit(key->keycode, dell_wmi_input_dev->keybit);
-			break;
-		case KE_SW:
-			set_bit(EV_SW, dell_wmi_input_dev->evbit);
-			set_bit(key->keycode, dell_wmi_input_dev->swbit);
-			break;
+	if (dell_new_hk_type) {
+		const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
+		if (!keymap) {
+			err = -ENOMEM;
+			goto err_free_dev;
 		}
+
+		err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
+
+		/*
+		 * Sparse keymap library makes a copy of keymap so we
+		 * don't need the original one that was allocated.
+		 */
+		kfree(keymap);
+	} else {
+		err = sparse_keymap_setup(dell_wmi_input_dev,
+					  dell_wmi_legacy_keymap, NULL);
 	}
+	if (err)
+		goto err_free_dev;
 
 	err = input_register_device(dell_wmi_input_dev);
-
-	if (err) {
-		input_free_device(dell_wmi_input_dev);
-		return err;
-	}
+	if (err)
+		goto err_free_keymap;
 
 	return 0;
+
+ err_free_keymap:
+	sparse_keymap_free(dell_wmi_input_dev);
+ err_free_dev:
+	input_free_device(dell_wmi_input_dev);
+	return err;
+}
+
+static void dell_wmi_input_destroy(void)
+{
+	sparse_keymap_free(dell_wmi_input_dev);
+	input_unregister_device(dell_wmi_input_dev);
+}
+
+static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
+{
+	if (dm->type == 0xb2 && dm->length > 6) {
+		dell_new_hk_type = true;
+		dell_bios_hotkey_table =
+			container_of(dm, struct dell_bios_hotkey_table, header);
+	}
 }
 
 static int __init dell_wmi_init(void)
@@ -339,18 +283,13 @@
 	acpi_video = acpi_video_backlight_support();
 
 	err = dell_wmi_input_setup();
-	if (err) {
-		if (dell_new_hk_type)
-			kfree(dell_wmi_keymap);
+	if (err)
 		return err;
-	}
 
 	status = wmi_install_notify_handler(DELL_EVENT_GUID,
 					 dell_wmi_notify, NULL);
 	if (ACPI_FAILURE(status)) {
-		input_unregister_device(dell_wmi_input_dev);
-		if (dell_new_hk_type)
-			kfree(dell_wmi_keymap);
+		dell_wmi_input_destroy();
 		printk(KERN_ERR
 			"dell-wmi: Unable to register notify handler - %d\n",
 			status);
@@ -359,14 +298,11 @@
 
 	return 0;
 }
+module_init(dell_wmi_init);
 
 static void __exit dell_wmi_exit(void)
 {
 	wmi_remove_notify_handler(DELL_EVENT_GUID);
-	input_unregister_device(dell_wmi_input_dev);
-	if (dell_new_hk_type)
-		kfree(dell_wmi_keymap);
+	dell_wmi_input_destroy();
 }
-
-module_init(dell_wmi_init);
 module_exit(dell_wmi_exit);
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 6b8e062..b2edfdc 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -165,6 +165,7 @@
 	u16 event_count[128];		/* count for each event */
 
 	struct platform_device *platform_device;
+	struct acpi_device *device;		/* the device we are in */
 	struct device *hwmon_device;
 	struct backlight_device *backlight_device;
 
@@ -1193,9 +1194,9 @@
 	eeepc->inputdev = input;
 	return 0;
 
- err_free_keymap:
+err_free_keymap:
 	sparse_keymap_free(input);
- err_free_dev:
+err_free_dev:
 	input_free_device(input);
 	return error;
 }
@@ -1206,6 +1207,7 @@
 		sparse_keymap_free(eeepc->inputdev);
 		input_unregister_device(eeepc->inputdev);
 	}
+	eeepc->inputdev = NULL;
 }
 
 /*
@@ -1326,16 +1328,15 @@
 	cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
 }
 
-static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
-			   struct acpi_device *device)
+static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
 {
 	unsigned int init_flags;
 	int result;
 
-	result = acpi_bus_get_status(device);
+	result = acpi_bus_get_status(eeepc->device);
 	if (result)
 		return result;
-	if (!device->status.present) {
+	if (!eeepc->device->status.present) {
 		pr_err("Hotkey device not present, aborting\n");
 		return -ENODEV;
 	}
@@ -1384,12 +1385,13 @@
 	strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
 	strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
 	device->driver_data = eeepc;
+	eeepc->device = device;
 
 	eeepc->hotplug_disabled = hotplug_disabled;
 
 	eeepc_dmi_check(eeepc);
 
-	result = eeepc_acpi_init(eeepc, device);
+	result = eeepc_acpi_init(eeepc);
 	if (result)
 		goto fail_platform;
 	eeepc_enable_camera(eeepc);
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 9dc50fb..462ceab 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -57,6 +57,7 @@
 
 #define EEEPC_WMI_METHODID_DEVS	0x53564544
 #define EEEPC_WMI_METHODID_DSTS	0x53544344
+#define EEEPC_WMI_METHODID_CFVS	0x53564643
 
 #define EEEPC_WMI_DEVID_BACKLIGHT	0x00050012
 
@@ -69,6 +70,11 @@
 	{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
 	{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
 	{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
+	{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
+	{ KE_KEY, 0xe1, { KEY_F14 } },
+	{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
+	{ KE_KEY, 0xe0, { KEY_PROG1 } },
+	{ KE_KEY, 0x5c, { KEY_F15 } },
 	{ KE_END, 0},
 };
 
@@ -292,6 +298,49 @@
 	kfree(obj);
 }
 
+static int store_cpufv(struct device *dev, struct device_attribute *attr,
+		       const char *buf, size_t count)
+{
+	int value;
+	struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
+	acpi_status status;
+
+	if (!count || sscanf(buf, "%i", &value) != 1)
+		return -EINVAL;
+	if (value < 0 || value > 2)
+		return -EINVAL;
+
+	status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
+				     1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+	else
+		return count;
+}
+
+static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
+
+static void eeepc_wmi_sysfs_exit(struct platform_device *device)
+{
+	device_remove_file(&device->dev, &dev_attr_cpufv);
+}
+
+static int eeepc_wmi_sysfs_init(struct platform_device *device)
+{
+	int retval = -ENOMEM;
+
+	retval = device_create_file(&device->dev, &dev_attr_cpufv);
+	if (retval)
+		goto error_sysfs;
+
+	return 0;
+
+error_sysfs:
+	eeepc_wmi_sysfs_exit(platform_device);
+	return retval;
+}
+
 static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
 {
 	struct eeepc_wmi *eeepc;
@@ -387,8 +436,14 @@
 		goto del_dev;
 	}
 
+	err = eeepc_wmi_sysfs_init(platform_device);
+	if (err)
+		goto del_sysfs;
+
 	return 0;
 
+del_sysfs:
+	eeepc_wmi_sysfs_exit(platform_device);
 del_dev:
 	platform_device_del(platform_device);
 put_dev:
@@ -403,6 +458,7 @@
 {
 	struct eeepc_wmi *eeepc;
 
+	eeepc_wmi_sysfs_exit(platform_device);
 	eeepc = platform_get_drvdata(platform_device);
 	platform_driver_unregister(&platform_driver);
 	platform_device_unregister(platform_device);
diff --git a/drivers/hwmon/hdaps.c b/drivers/platform/x86/hdaps.c
similarity index 99%
rename from drivers/hwmon/hdaps.c
rename to drivers/platform/x86/hdaps.c
index bfd42f1..067bf36 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -1,5 +1,5 @@
 /*
- * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System
+ * hdaps.c - driver for IBM's Hard Drive Active Protection System
  *
  * Copyright (C) 2005 Robert Love <rml@novell.com>
  * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index c174114..1dac659 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/rfkill.h>
@@ -88,24 +89,16 @@
 	u32 value;
 };
 
-struct key_entry {
-	char type;		/* See KE_* below */
-	u16 code;
-	u16 keycode;
-};
-
-enum { KE_KEY, KE_END };
-
-static struct key_entry hp_wmi_keymap[] = {
-	{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
-	{KE_KEY, 0x20e6, KEY_PROG1},
-	{KE_KEY, 0x20e8, KEY_MEDIA},
-	{KE_KEY, 0x2142, KEY_MEDIA},
-	{KE_KEY, 0x213b, KEY_INFO},
-	{KE_KEY, 0x2169, KEY_DIRECTION},
-	{KE_KEY, 0x231b, KEY_HELP},
-	{KE_END, 0}
+static const struct key_entry hp_wmi_keymap[] = {
+	{ KE_KEY, 0x02,   { KEY_BRIGHTNESSUP } },
+	{ KE_KEY, 0x03,   { KEY_BRIGHTNESSDOWN } },
+	{ KE_KEY, 0x20e6, { KEY_PROG1 } },
+	{ KE_KEY, 0x20e8, { KEY_MEDIA } },
+	{ KE_KEY, 0x2142, { KEY_MEDIA } },
+	{ KE_KEY, 0x213b, { KEY_INFO } },
+	{ KE_KEY, 0x2169, { KEY_DIRECTION } },
+	{ KE_KEY, 0x231b, { KEY_HELP } },
+	{ KE_END, 0 }
 };
 
 static struct input_dev *hp_wmi_input_dev;
@@ -347,64 +340,9 @@
 static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
 static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
 
-static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code)
-{
-	struct key_entry *key;
-
-	for (key = hp_wmi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
-
-	return NULL;
-}
-
-static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode)
-{
-	struct key_entry *key;
-
-	for (key = hp_wmi_keymap; key->type != KE_END; key++)
-		if (key->type == KE_KEY && keycode == key->keycode)
-			return key;
-
-	return NULL;
-}
-
-static int hp_wmi_getkeycode(struct input_dev *dev,
-			     unsigned int scancode, unsigned int *keycode)
-{
-	struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
-
-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int hp_wmi_setkeycode(struct input_dev *dev,
-			     unsigned int scancode, unsigned int keycode)
-{
-	struct key_entry *key;
-	unsigned int old_keycode;
-
-	key = hp_wmi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!hp_wmi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
 static void hp_wmi_notify(u32 value, void *context)
 {
 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
-	static struct key_entry *key;
 	union acpi_object *obj;
 	u32 event_id, event_data;
 	int key_code = 0, ret;
@@ -465,19 +403,9 @@
 					   sizeof(key_code));
 		if (ret)
 			break;
-		key = hp_wmi_get_entry_by_scancode(key_code);
-		if (key) {
-			switch (key->type) {
-			case KE_KEY:
-				input_report_key(hp_wmi_input_dev,
-						 key->keycode, 1);
-				input_sync(hp_wmi_input_dev);
-				input_report_key(hp_wmi_input_dev,
-						 key->keycode, 0);
-				input_sync(hp_wmi_input_dev);
-				break;
-			}
-		} else
+
+		if (!sparse_keymap_report_event(hp_wmi_input_dev,
+						key_code, 1, true))
 			printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
 			       key_code);
 		break;
@@ -510,7 +438,7 @@
 
 static int __init hp_wmi_input_setup(void)
 {
-	struct key_entry *key;
+	acpi_status status;
 	int err;
 
 	hp_wmi_input_dev = input_allocate_device();
@@ -520,21 +448,14 @@
 	hp_wmi_input_dev->name = "HP WMI hotkeys";
 	hp_wmi_input_dev->phys = "wmi/input0";
 	hp_wmi_input_dev->id.bustype = BUS_HOST;
-	hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
-	hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
 
-	for (key = hp_wmi_keymap; key->type != KE_END; key++) {
-		switch (key->type) {
-		case KE_KEY:
-			set_bit(EV_KEY, hp_wmi_input_dev->evbit);
-			set_bit(key->keycode, hp_wmi_input_dev->keybit);
-			break;
-		}
-	}
+	__set_bit(EV_SW, hp_wmi_input_dev->evbit);
+	__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
+	__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
 
-	set_bit(EV_SW, hp_wmi_input_dev->evbit);
-	set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
-	set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+	err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
+	if (err)
+		goto err_free_dev;
 
 	/* Set initial hardware state */
 	input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
@@ -542,14 +463,32 @@
 			    hp_wmi_tablet_state());
 	input_sync(hp_wmi_input_dev);
 
-	err = input_register_device(hp_wmi_input_dev);
-
-	if (err) {
-		input_free_device(hp_wmi_input_dev);
-		return err;
+	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
+	if (ACPI_FAILURE(status)) {
+		err = -EIO;
+		goto err_free_keymap;
 	}
 
+	err = input_register_device(hp_wmi_input_dev);
+	if (err)
+		goto err_uninstall_notifier;
+
 	return 0;
+
+ err_uninstall_notifier:
+	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+ err_free_keymap:
+	sparse_keymap_free(hp_wmi_input_dev);
+ err_free_dev:
+	input_free_device(hp_wmi_input_dev);
+	return err;
+}
+
+static void hp_wmi_input_destroy(void)
+{
+	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+	sparse_keymap_free(hp_wmi_input_dev);
+	input_unregister_device(hp_wmi_input_dev);
 }
 
 static void cleanup_sysfs(struct platform_device *device)
@@ -704,15 +643,9 @@
 	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
 
 	if (event_capable) {
-		err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
-						 hp_wmi_notify, NULL);
-		if (ACPI_FAILURE(err))
-			return -EINVAL;
 		err = hp_wmi_input_setup();
-		if (err) {
-			wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+		if (err)
 			return err;
-		}
 	}
 
 	if (bios_capable) {
@@ -739,20 +672,17 @@
 err_device_alloc:
 	platform_driver_unregister(&hp_wmi_driver);
 err_driver_reg:
-	if (wmi_has_guid(HPWMI_EVENT_GUID)) {
-		input_unregister_device(hp_wmi_input_dev);
-		wmi_remove_notify_handler(HPWMI_EVENT_GUID);
-	}
+	if (event_capable)
+		hp_wmi_input_destroy();
 
 	return err;
 }
 
 static void __exit hp_wmi_exit(void)
 {
-	if (wmi_has_guid(HPWMI_EVENT_GUID)) {
-		wmi_remove_notify_handler(HPWMI_EVENT_GUID);
-		input_unregister_device(hp_wmi_input_dev);
-	}
+	if (wmi_has_guid(HPWMI_EVENT_GUID))
+		hp_wmi_input_destroy();
+
 	if (hp_wmi_platform_dev) {
 		platform_device_unregister(hp_wmi_platform_dev);
 		platform_driver_unregister(&hp_wmi_driver);
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
new file mode 100644
index 0000000..3c2c6b9
--- /dev/null
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -0,0 +1,341 @@
+/*
+ * IBM Real-Time Linux driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2010
+ *
+ * Author: Keith Mannthey <kmannth@us.ibm.com>
+ *         Vernon Mauery <vernux@us.ibm.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/sysdev.h>
+#include <linux/dmi.h>
+#include <linux/mutex.h>
+#include <asm/bios_ebda.h>
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Show debug output");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
+MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
+
+#define RTL_ADDR_TYPE_IO    1
+#define RTL_ADDR_TYPE_MMIO  2
+
+#define RTL_CMD_ENTER_PRTM  1
+#define RTL_CMD_EXIT_PRTM   2
+
+/* The RTL table as presented by the EBDA: */
+struct ibm_rtl_table {
+	char signature[5]; /* signature should be "_RTL_" */
+	u8 version;
+	u8 rt_status;
+	u8 command;
+	u8 command_status;
+	u8 cmd_address_type;
+	u8 cmd_granularity;
+	u8 cmd_offset;
+	u16 reserve1;
+	u32 cmd_port_address; /* platform dependent address */
+	u32 cmd_port_value;   /* platform dependent value */
+} __attribute__((packed));
+
+/* to locate "_RTL_" signature do a masked 5-byte integer compare */
+#define RTL_SIGNATURE 0x0000005f4c54525fULL
+#define RTL_MASK      0x000000ffffffffffULL
+
+#define RTL_DEBUG(A, ...) do { \
+	if (debug) \
+		pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
+} while (0)
+
+static DEFINE_MUTEX(rtl_lock);
+static struct ibm_rtl_table __iomem *rtl_table;
+static void __iomem *ebda_map;
+static void __iomem *rtl_cmd_addr;
+static u8 rtl_cmd_type;
+static u8 rtl_cmd_width;
+
+static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
+{
+	if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
+		return ioremap(addr, len);
+	return ioport_map(addr, len);
+}
+
+static void rtl_port_unmap(void __iomem *addr)
+{
+	if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
+		iounmap(addr);
+	else
+		ioport_unmap(addr);
+}
+
+static int ibm_rtl_write(u8 value)
+{
+	int ret = 0, count = 0;
+	static u32 cmd_port_val;
+
+	RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
+
+	value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
+
+	mutex_lock(&rtl_lock);
+
+	if (ioread8(&rtl_table->rt_status) != value) {
+		iowrite8(value, &rtl_table->command);
+
+		switch (rtl_cmd_width) {
+		case 8:
+			cmd_port_val = ioread8(&rtl_table->cmd_port_value);
+			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+			iowrite8((u8)cmd_port_val, rtl_cmd_addr);
+			break;
+		case 16:
+			cmd_port_val = ioread16(&rtl_table->cmd_port_value);
+			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+			iowrite16((u16)cmd_port_val, rtl_cmd_addr);
+			break;
+		case 32:
+			cmd_port_val = ioread32(&rtl_table->cmd_port_value);
+			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
+			iowrite32(cmd_port_val, rtl_cmd_addr);
+			break;
+		}
+
+		while (ioread8(&rtl_table->command)) {
+			msleep(10);
+			if (count++ > 500) {
+				pr_err("ibm-rtl: Hardware not responding to "
+					"mode switch request\n");
+				ret = -EIO;
+				break;
+			}
+
+		}
+
+		if (ioread8(&rtl_table->command_status)) {
+			RTL_DEBUG("command_status reports failed command\n");
+			ret = -EIO;
+		}
+	}
+
+	mutex_unlock(&rtl_lock);
+	return ret;
+}
+
+static ssize_t rtl_show_version(struct sysdev_class * dev,
+                                struct sysdev_class_attribute *attr,
+                                char *buf)
+{
+	return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
+}
+
+static ssize_t rtl_show_state(struct sysdev_class *dev,
+                              struct sysdev_class_attribute *attr,
+                              char *buf)
+{
+	return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
+}
+
+static ssize_t rtl_set_state(struct sysdev_class *dev,
+                             struct sysdev_class_attribute *attr,
+                             const char *buf,
+                             size_t count)
+{
+	ssize_t ret;
+
+	if (count < 1 || count > 2)
+		return -EINVAL;
+
+	switch (buf[0]) {
+	case '0':
+		ret = ibm_rtl_write(0);
+		break;
+	case '1':
+		ret = ibm_rtl_write(1);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret >= 0)
+		ret = count;
+
+	return ret;
+}
+
+static struct sysdev_class class_rtl = {
+	.name = "ibm_rtl",
+};
+
+static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
+static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
+
+static struct sysdev_class_attribute *rtl_attributes[] = {
+	&attr_version,
+	&attr_state,
+	NULL
+};
+
+
+static int rtl_setup_sysfs(void) {
+	int ret, i;
+	ret = sysdev_class_register(&class_rtl);
+
+	if (!ret) {
+		for (i = 0; rtl_attributes[i]; i ++)
+			sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
+	}
+	return ret;
+}
+
+static void rtl_teardown_sysfs(void) {
+	int i;
+	for (i = 0; rtl_attributes[i]; i ++)
+		sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
+	sysdev_class_unregister(&class_rtl);
+}
+
+static int dmi_check_cb(const struct dmi_system_id *id)
+{
+	RTL_DEBUG("found IBM server '%s'\n", id->ident);
+	return 0;
+}
+
+#define ibm_dmi_entry(NAME, TYPE)                  \
+{                                                  \
+	.ident = NAME,                             \
+	.matches = {                               \
+		DMI_MATCH(DMI_SYS_VENDOR, "IBM"),  \
+		DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
+	},                                         \
+	.callback = dmi_check_cb                   \
+}
+
+static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
+	ibm_dmi_entry("BladeCenter LS21", "7971"),
+	ibm_dmi_entry("BladeCenter LS22", "7901"),
+	ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
+	ibm_dmi_entry("BladeCenter HS22", "7870"),
+	ibm_dmi_entry("BladeCenter HS22V", "7871"),
+	ibm_dmi_entry("System x3550 M2", "7946"),
+	ibm_dmi_entry("System x3650 M2", "7947"),
+	ibm_dmi_entry("System x3550 M3", "7944"),
+	ibm_dmi_entry("System x3650 M3", "7945"),
+	{ }
+};
+
+static int __init ibm_rtl_init(void) {
+	unsigned long ebda_addr, ebda_size;
+	unsigned int ebda_kb;
+	int ret = -ENODEV, i;
+
+	if (force)
+		pr_warning("ibm-rtl: module loaded by force\n");
+	/* first ensure that we are running on IBM HW */
+	else if (!dmi_check_system(ibm_rtl_dmi_table))
+		return -ENODEV;
+
+	/* Get the address for the Extended BIOS Data Area */
+	ebda_addr = get_bios_ebda();
+	if (!ebda_addr) {
+		RTL_DEBUG("no BIOS EBDA found\n");
+		return -ENODEV;
+	}
+
+	ebda_map = ioremap(ebda_addr, 4);
+	if (!ebda_map)
+		return -ENOMEM;
+
+	/* First word in the EDBA is the Size in KB */
+	ebda_kb = ioread16(ebda_map);
+	RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
+
+	if (ebda_kb == 0)
+		goto out;
+
+	iounmap(ebda_map);
+	ebda_size = ebda_kb*1024;
+
+	/* Remap the whole table */
+	ebda_map = ioremap(ebda_addr, ebda_size);
+	if (!ebda_map)
+		return -ENOMEM;
+
+	/* search for the _RTL_ signature at the start of the table */
+	for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
+		struct ibm_rtl_table __iomem * tmp;
+		tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
+		if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
+			phys_addr_t addr;
+			unsigned int plen;
+			RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
+			rtl_table = tmp;
+			/* The address, value, width and offset are platform
+			 * dependent and found in the ibm_rtl_table */
+			rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
+			rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
+			RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
+			      rtl_cmd_width, rtl_cmd_type);
+			addr = ioread32(&rtl_table->cmd_port_address);
+			RTL_DEBUG("addr = %#llx\n", addr);
+			plen = rtl_cmd_width/sizeof(char);
+			rtl_cmd_addr = rtl_port_map(addr, plen);
+			RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
+			if (!rtl_cmd_addr) {
+				ret = -ENOMEM;
+				break;
+			}
+			ret = rtl_setup_sysfs();
+			break;
+		}
+	}
+
+out:
+	if (ret) {
+		iounmap(ebda_map);
+		rtl_port_unmap(rtl_cmd_addr);
+	}
+
+	return ret;
+}
+
+static void __exit ibm_rtl_exit(void)
+{
+	if (rtl_table) {
+		RTL_DEBUG("cleaning up");
+		/* do not leave the machine in SMI-free mode */
+		ibm_rtl_write(0);
+		/* unmap, unlink and remove all traces */
+		rtl_teardown_sysfs();
+		iounmap(ebda_map);
+		rtl_port_unmap(rtl_cmd_addr);
+	}
+}
+
+module_init(ibm_rtl_init);
+module_exit(ibm_rtl_exit);
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
new file mode 100644
index 0000000..5ff1220
--- /dev/null
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -0,0 +1,394 @@
+/*
+ *  ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
+ *
+ *  Copyright © 2010 Intel Corporation
+ *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/rfkill.h>
+
+#define IDEAPAD_DEV_CAMERA	0
+#define IDEAPAD_DEV_WLAN	1
+#define IDEAPAD_DEV_BLUETOOTH	2
+#define IDEAPAD_DEV_3G		3
+#define IDEAPAD_DEV_KILLSW	4
+
+struct ideapad_private {
+	acpi_handle handle;
+	struct rfkill *rfk[5];
+} *ideapad_priv;
+
+static struct {
+	char *name;
+	int cfgbit;
+	int opcode;
+	int type;
+} ideapad_rfk_data[] = {
+	{ "ideapad_camera",	19, 0x1E, NUM_RFKILL_TYPES },
+	{ "ideapad_wlan",	18, 0x15, RFKILL_TYPE_WLAN },
+	{ "ideapad_bluetooth",	16, 0x17, RFKILL_TYPE_BLUETOOTH },
+	{ "ideapad_3g",		17, 0x20, RFKILL_TYPE_WWAN },
+	{ "ideapad_killsw",	0,  0,    RFKILL_TYPE_WLAN }
+};
+
+static bool no_bt_rfkill;
+module_param(no_bt_rfkill, bool, 0444);
+MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
+
+/*
+ * ACPI Helpers
+ */
+#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
+
+static int read_method_int(acpi_handle handle, const char *method, int *val)
+{
+	acpi_status status;
+	unsigned long long result;
+
+	status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
+	if (ACPI_FAILURE(status)) {
+		*val = -1;
+		return -1;
+	} else {
+		*val = result;
+		return 0;
+	}
+}
+
+static int method_vpcr(acpi_handle handle, int cmd, int *ret)
+{
+	acpi_status status;
+	unsigned long long result;
+	struct acpi_object_list params;
+	union acpi_object in_obj;
+
+	params.count = 1;
+	params.pointer = &in_obj;
+	in_obj.type = ACPI_TYPE_INTEGER;
+	in_obj.integer.value = cmd;
+
+	status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
+
+	if (ACPI_FAILURE(status)) {
+		*ret = -1;
+		return -1;
+	} else {
+		*ret = result;
+		return 0;
+	}
+}
+
+static int method_vpcw(acpi_handle handle, int cmd, int data)
+{
+	struct acpi_object_list params;
+	union acpi_object in_obj[2];
+	acpi_status status;
+
+	params.count = 2;
+	params.pointer = in_obj;
+	in_obj[0].type = ACPI_TYPE_INTEGER;
+	in_obj[0].integer.value = cmd;
+	in_obj[1].type = ACPI_TYPE_INTEGER;
+	in_obj[1].integer.value = data;
+
+	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
+	if (status != AE_OK)
+		return -1;
+	return 0;
+}
+
+static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
+{
+	int val;
+	unsigned long int end_jiffies;
+
+	if (method_vpcw(handle, 1, cmd))
+		return -1;
+
+	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
+	     time_before(jiffies, end_jiffies);) {
+		schedule();
+		if (method_vpcr(handle, 1, &val))
+			return -1;
+		if (val == 0) {
+			if (method_vpcr(handle, 0, &val))
+				return -1;
+			*data = val;
+			return 0;
+		}
+	}
+	pr_err("timeout in read_ec_cmd\n");
+	return -1;
+}
+
+static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
+{
+	int val;
+	unsigned long int end_jiffies;
+
+	if (method_vpcw(handle, 0, data))
+		return -1;
+	if (method_vpcw(handle, 1, cmd))
+		return -1;
+
+	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
+	     time_before(jiffies, end_jiffies);) {
+		schedule();
+		if (method_vpcr(handle, 1, &val))
+			return -1;
+		if (val == 0)
+			return 0;
+	}
+	pr_err("timeout in write_ec_cmd\n");
+	return -1;
+}
+/* the above is ACPI helpers */
+
+static ssize_t show_ideapad_cam(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct ideapad_private *priv = dev_get_drvdata(dev);
+	acpi_handle handle = priv->handle;
+	unsigned long result;
+
+	if (read_ec_data(handle, 0x1D, &result))
+		return sprintf(buf, "-1\n");
+	return sprintf(buf, "%lu\n", result);
+}
+
+static ssize_t store_ideapad_cam(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct ideapad_private *priv = dev_get_drvdata(dev);
+	acpi_handle handle = priv->handle;
+	int ret, state;
+
+	if (!count)
+		return 0;
+	if (sscanf(buf, "%i", &state) != 1)
+		return -EINVAL;
+	ret = write_ec_cmd(handle, 0x1E, state);
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
+static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+
+static int ideapad_rfk_set(void *data, bool blocked)
+{
+	int device = (unsigned long)data;
+
+	if (device == IDEAPAD_DEV_KILLSW)
+		return -EINVAL;
+
+	return write_ec_cmd(ideapad_priv->handle,
+			    ideapad_rfk_data[device].opcode,
+			    !blocked);
+}
+
+static struct rfkill_ops ideapad_rfk_ops = {
+	.set_block = ideapad_rfk_set,
+};
+
+static void ideapad_sync_rfk_state(struct acpi_device *adevice)
+{
+	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+	acpi_handle handle = priv->handle;
+	unsigned long hw_blocked;
+	int i;
+
+	if (read_ec_data(handle, 0x23, &hw_blocked))
+		return;
+	hw_blocked = !hw_blocked;
+
+	for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
+		if (priv->rfk[i])
+			rfkill_set_hw_state(priv->rfk[i], hw_blocked);
+}
+
+static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
+{
+	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+	int ret;
+	unsigned long sw_blocked;
+
+	if (no_bt_rfkill &&
+	    (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
+		/* Force to enable bluetooth when no_bt_rfkill=1 */
+		write_ec_cmd(ideapad_priv->handle,
+			     ideapad_rfk_data[dev].opcode, 1);
+		return 0;
+	}
+
+	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
+				      ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
+				      (void *)(long)dev);
+	if (!priv->rfk[dev])
+		return -ENOMEM;
+
+	if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
+			 &sw_blocked)) {
+		rfkill_init_sw_state(priv->rfk[dev], 0);
+	} else {
+		sw_blocked = !sw_blocked;
+		rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
+	}
+
+	ret = rfkill_register(priv->rfk[dev]);
+	if (ret) {
+		rfkill_destroy(priv->rfk[dev]);
+		return ret;
+	}
+	return 0;
+}
+
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
+{
+	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+
+	if (!priv->rfk[dev])
+		return;
+
+	rfkill_unregister(priv->rfk[dev]);
+	rfkill_destroy(priv->rfk[dev]);
+}
+
+static const struct acpi_device_id ideapad_device_ids[] = {
+	{ "VPC2004", 0},
+	{ "", 0},
+};
+MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
+
+static int ideapad_acpi_add(struct acpi_device *adevice)
+{
+	int i, cfg;
+	int devs_present[5];
+	struct ideapad_private *priv;
+
+	if (read_method_int(adevice->handle, "_CFG", &cfg))
+		return -ENODEV;
+
+	for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
+		if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
+			devs_present[i] = 1;
+		else
+			devs_present[i] = 0;
+	}
+
+	/* The hardware switch is always present */
+	devs_present[IDEAPAD_DEV_KILLSW] = 1;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	if (devs_present[IDEAPAD_DEV_CAMERA]) {
+		int ret = device_create_file(&adevice->dev, &dev_attr_camera_power);
+		if (ret) {
+			kfree(priv);
+			return ret;
+		}
+	}
+
+	priv->handle = adevice->handle;
+	dev_set_drvdata(&adevice->dev, priv);
+	ideapad_priv = priv;
+	for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
+		if (!devs_present[i])
+			continue;
+
+		ideapad_register_rfkill(adevice, i);
+	}
+	ideapad_sync_rfk_state(adevice);
+	return 0;
+}
+
+static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
+{
+	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+	int i;
+
+	device_remove_file(&adevice->dev, &dev_attr_camera_power);
+
+	for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
+		ideapad_unregister_rfkill(adevice, i);
+
+	dev_set_drvdata(&adevice->dev, NULL);
+	kfree(priv);
+	return 0;
+}
+
+static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
+{
+	acpi_handle handle = adevice->handle;
+	unsigned long vpc1, vpc2, vpc_bit;
+
+	if (read_ec_data(handle, 0x10, &vpc1))
+		return;
+	if (read_ec_data(handle, 0x1A, &vpc2))
+		return;
+
+	vpc1 = (vpc2 << 8) | vpc1;
+	for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
+		if (test_bit(vpc_bit, &vpc1)) {
+			if (vpc_bit == 9)
+				ideapad_sync_rfk_state(adevice);
+		}
+	}
+}
+
+static struct acpi_driver ideapad_acpi_driver = {
+	.name = "ideapad_acpi",
+	.class = "IdeaPad",
+	.ids = ideapad_device_ids,
+	.ops.add = ideapad_acpi_add,
+	.ops.remove = ideapad_acpi_remove,
+	.ops.notify = ideapad_acpi_notify,
+	.owner = THIS_MODULE,
+};
+
+
+static int __init ideapad_acpi_module_init(void)
+{
+	acpi_bus_register_driver(&ideapad_acpi_driver);
+
+	return 0;
+}
+
+
+static void __exit ideapad_acpi_module_exit(void)
+{
+	acpi_bus_unregister_driver(&ideapad_acpi_driver);
+
+}
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("IdeaPad ACPI Extras");
+MODULE_LICENSE("GPL");
+
+module_init(ideapad_acpi_module_init);
+module_exit(ideapad_acpi_module_exit);
diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c
deleted file mode 100644
index 7984963..0000000
--- a/drivers/platform/x86/ideapad_acpi.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- *  ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
- *
- *  Copyright © 2010 Intel Corporation
- *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- *  02110-1301, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-#include <linux/rfkill.h>
-
-#define IDEAPAD_DEV_CAMERA	0
-#define IDEAPAD_DEV_WLAN	1
-#define IDEAPAD_DEV_BLUETOOTH	2
-#define IDEAPAD_DEV_3G		3
-#define IDEAPAD_DEV_KILLSW	4
-
-struct ideapad_private {
-	struct rfkill *rfk[5];
-};
-
-static struct {
-	char *name;
-	int type;
-} ideapad_rfk_data[] = {
-	/* camera has no rfkill */
-	{ "ideapad_wlan",	RFKILL_TYPE_WLAN },
-	{ "ideapad_bluetooth",	RFKILL_TYPE_BLUETOOTH },
-	{ "ideapad_3g",		RFKILL_TYPE_WWAN },
-	{ "ideapad_killsw",	RFKILL_TYPE_WLAN }
-};
-
-static int ideapad_dev_exists(int device)
-{
-	acpi_status status;
-	union acpi_object in_param;
-	struct acpi_object_list input = { 1, &in_param };
-	struct acpi_buffer output;
-	union acpi_object out_obj;
-
-	output.length = sizeof(out_obj);
-	output.pointer = &out_obj;
-
-	in_param.type = ACPI_TYPE_INTEGER;
-	in_param.integer.value = device + 1;
-
-	status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
-		return -ENODEV;
-	}
-	if (out_obj.type != ACPI_TYPE_INTEGER) {
-		printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
-		return -ENODEV;
-	}
-	return out_obj.integer.value;
-}
-
-static int ideapad_dev_get_state(int device)
-{
-	acpi_status status;
-	union acpi_object in_param;
-	struct acpi_object_list input = { 1, &in_param };
-	struct acpi_buffer output;
-	union acpi_object out_obj;
-
-	output.length = sizeof(out_obj);
-	output.pointer = &out_obj;
-
-	in_param.type = ACPI_TYPE_INTEGER;
-	in_param.integer.value = device + 1;
-
-	status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
-		return -ENODEV;
-	}
-	if (out_obj.type != ACPI_TYPE_INTEGER) {
-		printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
-		return -ENODEV;
-	}
-	return out_obj.integer.value;
-}
-
-static int ideapad_dev_set_state(int device, int state)
-{
-	acpi_status status;
-	union acpi_object in_params[2];
-	struct acpi_object_list input = { 2, in_params };
-
-	in_params[0].type = ACPI_TYPE_INTEGER;
-	in_params[0].integer.value = device + 1;
-	in_params[1].type = ACPI_TYPE_INTEGER;
-	in_params[1].integer.value = state;
-
-	status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
-		return -ENODEV;
-	}
-	return 0;
-}
-static ssize_t show_ideapad_cam(struct device *dev,
-				struct device_attribute *attr,
-				char *buf)
-{
-	int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
-	if (state < 0)
-		return state;
-
-	return sprintf(buf, "%d\n", state);
-}
-
-static ssize_t store_ideapad_cam(struct device *dev,
-				 struct device_attribute *attr,
-				 const char *buf, size_t count)
-{
-	int ret, state;
-
-	if (!count)
-		return 0;
-	if (sscanf(buf, "%i", &state) != 1)
-		return -EINVAL;
-	ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
-	if (ret < 0)
-		return ret;
-	return count;
-}
-
-static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
-
-static int ideapad_rfk_set(void *data, bool blocked)
-{
-	int device = (unsigned long)data;
-
-	if (device == IDEAPAD_DEV_KILLSW)
-		return -EINVAL;
-	return ideapad_dev_set_state(device, !blocked);
-}
-
-static struct rfkill_ops ideapad_rfk_ops = {
-	.set_block = ideapad_rfk_set,
-};
-
-static void ideapad_sync_rfk_state(struct acpi_device *adevice)
-{
-	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-	int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
-	int i;
-
-	rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked);
-	for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
-		if (priv->rfk[i])
-			rfkill_set_hw_state(priv->rfk[i], hw_blocked);
-	if (hw_blocked)
-		return;
-
-	for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
-		if (priv->rfk[i])
-			rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i));
-}
-
-static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
-{
-	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-	int ret;
-
-	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev,
-				      ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
-				      (void *)(long)dev);
-	if (!priv->rfk[dev])
-		return -ENOMEM;
-
-	ret = rfkill_register(priv->rfk[dev]);
-	if (ret) {
-		rfkill_destroy(priv->rfk[dev]);
-		return ret;
-	}
-	return 0;
-}
-
-static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
-{
-	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-
-	if (!priv->rfk[dev])
-		return;
-
-	rfkill_unregister(priv->rfk[dev]);
-	rfkill_destroy(priv->rfk[dev]);
-}
-
-static const struct acpi_device_id ideapad_device_ids[] = {
-	{ "VPC2004", 0},
-	{ "", 0},
-};
-MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
-
-static int ideapad_acpi_add(struct acpi_device *adevice)
-{
-	int i;
-	int devs_present[5];
-	struct ideapad_private *priv;
-
-	for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
-		devs_present[i] = ideapad_dev_exists(i);
-		if (devs_present[i] < 0)
-			return devs_present[i];
-	}
-
-	/* The hardware switch is always present */
-	devs_present[IDEAPAD_DEV_KILLSW] = 1;
-
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	if (devs_present[IDEAPAD_DEV_CAMERA]) {
-		int ret = device_create_file(&adevice->dev, &dev_attr_camera_power);
-		if (ret) {
-			kfree(priv);
-			return ret;
-		}
-	}
-
-	dev_set_drvdata(&adevice->dev, priv);
-	for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
-		if (!devs_present[i])
-			continue;
-
-		ideapad_register_rfkill(adevice, i);
-	}
-	ideapad_sync_rfk_state(adevice);
-	return 0;
-}
-
-static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
-{
-	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-	int i;
-
-	device_remove_file(&adevice->dev, &dev_attr_camera_power);
-
-	for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
-		ideapad_unregister_rfkill(adevice, i);
-
-	dev_set_drvdata(&adevice->dev, NULL);
-	kfree(priv);
-	return 0;
-}
-
-static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
-{
-	ideapad_sync_rfk_state(adevice);
-}
-
-static struct acpi_driver ideapad_acpi_driver = {
-	.name = "ideapad_acpi",
-	.class = "IdeaPad",
-	.ids = ideapad_device_ids,
-	.ops.add = ideapad_acpi_add,
-	.ops.remove = ideapad_acpi_remove,
-	.ops.notify = ideapad_acpi_notify,
-	.owner = THIS_MODULE,
-};
-
-
-static int __init ideapad_acpi_module_init(void)
-{
-	acpi_bus_register_driver(&ideapad_acpi_driver);
-
-	return 0;
-}
-
-
-static void __exit ideapad_acpi_module_exit(void)
-{
-	acpi_bus_unregister_driver(&ideapad_acpi_driver);
-
-}
-
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("IdeaPad ACPI Extras");
-MODULE_LICENSE("GPL");
-
-module_init(ideapad_acpi_module_init);
-module_exit(ideapad_acpi_module_exit);
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
index 5cdcff6..f540ff9 100644
--- a/drivers/platform/x86/intel_pmic_gpio.c
+++ b/drivers/platform/x86/intel_pmic_gpio.c
@@ -142,16 +142,16 @@
 
 	if (offset < 8)/* it is GPIO */
 		rc = intel_scu_ipc_update_register(GPIO0 + offset,
-				GPIO_DRV | GPIO_DOU | GPIO_DIR,
-				GPIO_DRV | (value ? GPIO_DOU : 0));
+				GPIO_DRV | (value ? GPIO_DOU : 0),
+				GPIO_DRV | GPIO_DOU | GPIO_DIR);
 	else if (offset < 16)/* it is GPOSW */
 		rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
-				GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
-				GPOSW_DRV | (value ? GPOSW_DOU : 0));
+				GPOSW_DRV | (value ? GPOSW_DOU : 0),
+				GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
 	else if (offset > 15 && offset < 24)/* it is GPO */
 		rc = intel_scu_ipc_update_register(GPO,
-				1 << (offset - 16),
-				value ? 1 << (offset - 16) : 0);
+				value ? 1 << (offset - 16) : 0,
+				1 << (offset - 16));
 	else {
 		printk(KERN_ERR
 			"%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
@@ -179,16 +179,16 @@
 {
 	if (offset < 8)/* it is GPIO */
 		intel_scu_ipc_update_register(GPIO0 + offset,
-			GPIO_DRV | GPIO_DOU,
-			GPIO_DRV | (value ? GPIO_DOU : 0));
+			GPIO_DRV | (value ? GPIO_DOU : 0),
+			GPIO_DRV | GPIO_DOU);
 	else if (offset < 16)/* it is GPOSW */
 		intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
-			GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
-			GPOSW_DRV | (value ? GPOSW_DOU : 0));
+			GPOSW_DRV | (value ? GPOSW_DOU : 0),
+			GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
 	else if (offset > 15 && offset < 24) /* it is GPO */
 		intel_scu_ipc_update_register(GPO,
-			1 << (offset - 16),
-			value ? 1 << (offset - 16) : 0);
+			value ? 1 << (offset - 16) : 0,
+			1 << (offset - 16));
 }
 
 static int pmic_irq_type(unsigned irq, unsigned type)
@@ -197,7 +197,7 @@
 	u32 gpio = irq - pg->irq_base;
 	unsigned long flags;
 
-	if (gpio > pg->chip.ngpio)
+	if (gpio >= pg->chip.ngpio)
 		return -EINVAL;
 
 	spin_lock_irqsave(&pg->irqtypes.lock, flags);
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 6abe18e..41a9e34 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -23,6 +23,7 @@
 #include <linux/pm.h>
 #include <linux/pci.h>
 #include <linux/interrupt.h>
+#include <linux/sfi.h>
 #include <asm/mrst.h>
 #include <asm/intel_scu_ipc.h>
 
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index ec01c3d..cc1e0ba 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -128,6 +128,7 @@
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 
 
 #ifndef ACPI_HOTKEY_COMPONENT
@@ -200,30 +201,29 @@
 			},
 };
 
-#define KEYMAP_SIZE		11
-static const unsigned int initial_keymap[KEYMAP_SIZE] = {
-	/*  0 */ KEY_RESERVED,
-	/*  1 */ KEY_BRIGHTNESSDOWN,
-	/*  2 */ KEY_BRIGHTNESSUP,
-	/*  3 */ KEY_DISPLAYTOGGLE,
-	/*  4 */ KEY_MUTE,
-	/*  5 */ KEY_VOLUMEDOWN,
-	/*  6 */ KEY_VOLUMEUP,
-	/*  7 */ KEY_SLEEP,
-	/*  8 */ KEY_PROG1, /* Change CPU boost */
-	/*  9 */ KEY_BATTERY,
-	/* 10 */ KEY_SUSPEND,
+static const struct key_entry panasonic_keymap[] = {
+	{ KE_KEY, 0, { KEY_RESERVED } },
+	{ KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
+	{ KE_KEY, 2, { KEY_BRIGHTNESSUP } },
+	{ KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
+	{ KE_KEY, 4, { KEY_MUTE } },
+	{ KE_KEY, 5, { KEY_VOLUMEDOWN } },
+	{ KE_KEY, 6, { KEY_VOLUMEUP } },
+	{ KE_KEY, 7, { KEY_SLEEP } },
+	{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
+	{ KE_KEY, 9, { KEY_BATTERY } },
+	{ KE_KEY, 10, { KEY_SUSPEND } },
+	{ KE_END, 0 }
 };
 
 struct pcc_acpi {
 	acpi_handle		handle;
 	unsigned long		num_sifr;
 	int			sticky_mode;
-	u32 			*sinf;
+	u32			*sinf;
 	struct acpi_device	*device;
 	struct input_dev	*input_dev;
 	struct backlight_device	*backlight;
-	unsigned int		keymap[KEYMAP_SIZE];
 };
 
 struct pcc_keyinput {
@@ -267,7 +267,7 @@
 	}
 }
 
-static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
 {
 	acpi_status status;
 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
@@ -285,6 +285,7 @@
 	hkey = buffer.pointer;
 	if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+		status = AE_ERROR;
 		goto end;
 	}
 
@@ -298,12 +299,12 @@
 	for (i = 0; i < hkey->package.count; i++) {
 		union acpi_object *element = &(hkey->package.elements[i]);
 		if (likely(element->type == ACPI_TYPE_INTEGER)) {
-			sinf[i] = element->integer.value;
+			pcc->sinf[i] = element->integer.value;
 		} else
 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 					 "Invalid HKEY.SINF data\n"));
 	}
-	sinf[hkey->package.count] = -1;
+	pcc->sinf[hkey->package.count] = -1;
 
 end:
 	kfree(buffer.pointer);
@@ -321,7 +322,7 @@
 {
 	struct pcc_acpi *pcc = bl_get_data(bd);
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	return pcc->sinf[SINF_AC_CUR_BRIGHT];
@@ -333,7 +334,7 @@
 	int bright = bd->props.brightness;
 	int rc;
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
@@ -367,7 +368,7 @@
 	struct acpi_device *acpi = to_acpi_device(dev);
 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
@@ -379,7 +380,7 @@
 	struct acpi_device *acpi = to_acpi_device(dev);
 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
@@ -391,7 +392,7 @@
 	struct acpi_device *acpi = to_acpi_device(dev);
 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
@@ -403,7 +404,7 @@
 	struct acpi_device *acpi = to_acpi_device(dev);
 	struct pcc_acpi *pcc = acpi_driver_data(acpi);
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+	if (!acpi_pcc_retrieve_biosdata(pcc))
 		return -EIO;
 
 	return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
@@ -446,56 +447,10 @@
 
 /* hotkey input device driver */
 
-static int pcc_getkeycode(struct input_dev *dev,
-			  unsigned int scancode, unsigned int *keycode)
-{
-	struct pcc_acpi *pcc = input_get_drvdata(dev);
-
-	if (scancode >= ARRAY_SIZE(pcc->keymap))
-		return -EINVAL;
-
-	*keycode = pcc->keymap[scancode];
-
-	return 0;
-}
-
-static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
-		if (pcc->keymap[i] == keycode)
-			return i+1;
-	}
-
-	return 0;
-}
-
-static int pcc_setkeycode(struct input_dev *dev,
-			  unsigned int scancode, unsigned int keycode)
-{
-	struct pcc_acpi *pcc = input_get_drvdata(dev);
-	int oldkeycode;
-
-	if (scancode >= ARRAY_SIZE(pcc->keymap))
-		return -EINVAL;
-
-	oldkeycode = pcc->keymap[scancode];
-	pcc->keymap[scancode] = keycode;
-
-	set_bit(keycode, dev->keybit);
-
-	if (!keymap_get_by_keycode(pcc, oldkeycode))
-		clear_bit(oldkeycode, dev->keybit);
-
-	return 0;
-}
-
 static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
 {
 	struct input_dev *hotk_input_dev = pcc->input_dev;
 	int rc;
-	int key_code, hkey_num;
 	unsigned long long result;
 
 	rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
@@ -508,25 +463,10 @@
 
 	acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
 
-	hkey_num = result & 0xf;
-
-	if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) {
+	if (!sparse_keymap_report_event(hotk_input_dev,
+					result & 0xf, result & 0x80, false))
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-				  "hotkey number out of range: %d\n",
-				  hkey_num));
-		return;
-	}
-
-	key_code = pcc->keymap[hkey_num];
-
-	if (key_code != KEY_RESERVED) {
-		int pushed = (result & 0x80) ? TRUE : FALSE;
-
-		input_report_key(hotk_input_dev, key_code, pushed);
-		input_sync(hotk_input_dev);
-	}
-
-	return;
+				  "Unknown hotkey event: %d\n", result));
 }
 
 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
@@ -545,40 +485,55 @@
 
 static int acpi_pcc_init_input(struct pcc_acpi *pcc)
 {
-	int i, rc;
+	struct input_dev *input_dev;
+	int error;
 
-	pcc->input_dev = input_allocate_device();
-	if (!pcc->input_dev) {
+	input_dev = input_allocate_device();
+	if (!input_dev) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				  "Couldn't allocate input device for hotkey"));
 		return -ENOMEM;
 	}
 
-	pcc->input_dev->evbit[0] = BIT(EV_KEY);
+	input_dev->name = ACPI_PCC_DRIVER_NAME;
+	input_dev->phys = ACPI_PCC_INPUT_PHYS;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
 
-	pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
-	pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
-	pcc->input_dev->id.bustype = BUS_HOST;
-	pcc->input_dev->id.vendor = 0x0001;
-	pcc->input_dev->id.product = 0x0001;
-	pcc->input_dev->id.version = 0x0100;
-	pcc->input_dev->getkeycode = pcc_getkeycode;
-	pcc->input_dev->setkeycode = pcc_setkeycode;
+	error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
+	if (error) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Unable to setup input device keymap\n"));
+		goto err_free_dev;
+	}
 
-	/* load initial keymap */
-	memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+	error = input_register_device(input_dev);
+	if (error) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Unable to register input device\n"));
+		goto err_free_keymap;
+	}
 
-	for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
-		__set_bit(pcc->keymap[i], pcc->input_dev->keybit);
-	__clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
+	pcc->input_dev = input_dev;
+	return 0;
 
-	input_set_drvdata(pcc->input_dev, pcc);
+ err_free_keymap:
+	sparse_keymap_free(input_dev);
+ err_free_dev:
+	input_free_device(input_dev);
+	return error;
+}
 
-	rc = input_register_device(pcc->input_dev);
-	if (rc < 0)
-		input_free_device(pcc->input_dev);
-
-	return rc;
+static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
+{
+	sparse_keymap_free(pcc->input_dev);
+	input_unregister_device(pcc->input_dev);
+	/*
+	 * No need to input_free_device() since core input API refcounts
+	 * and free()s the device.
+	 */
 }
 
 /* kernel module interface */
@@ -636,12 +591,13 @@
 	if (result) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				  "Error installing keyinput handler\n"));
-		goto out_hotkey;
+		goto out_sinf;
 	}
 
-	if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+	if (!acpi_pcc_retrieve_biosdata(pcc)) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				 "Couldn't retrieve BIOS data\n"));
+		result = -EIO;
 		goto out_input;
 	}
 	/* initialize backlight */
@@ -651,7 +607,7 @@
 						   &pcc_backlight_ops, &props);
 	if (IS_ERR(pcc->backlight)) {
 		result = PTR_ERR(pcc->backlight);
-		goto out_sinf;
+		goto out_input;
 	}
 
 	/* read the initial brightness setting from the hardware */
@@ -669,12 +625,10 @@
 
 out_backlight:
 	backlight_device_unregister(pcc->backlight);
+out_input:
+	acpi_pcc_destroy_input(pcc);
 out_sinf:
 	kfree(pcc->sinf);
-out_input:
-	input_unregister_device(pcc->input_dev);
-	/* no need to input_free_device() since core input API refcount and
-	 * free()s the device */
 out_hotkey:
 	kfree(pcc);
 
@@ -709,9 +663,7 @@
 
 	backlight_device_unregister(pcc->backlight);
 
-	input_unregister_device(pcc->input_dev);
-	/* no need to input_free_device() since core input API refcount and
-	 * free()s the device */
+	acpi_pcc_destroy_input(pcc);
 
 	kfree(pcc->sinf);
 	kfree(pcc);
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index ff4b476..1d07d6d 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 
 #define ACPI_TOPSTAR_CLASS "topstar"
 
@@ -26,52 +27,37 @@
 	struct input_dev *inputdev;
 };
 
-struct tps_key_entry {
-	u8 code;
-	u16 keycode;
+static const struct key_entry topstar_keymap[] = {
+	{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
+	{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
+	{ KE_KEY, 0x83, { KEY_VOLUMEUP } },
+	{ KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
+	{ KE_KEY, 0x85, { KEY_MUTE } },
+	{ KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
+	{ KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
+	{ KE_KEY, 0x88, { KEY_WLAN } },
+	{ KE_KEY, 0x8a, { KEY_WWW } },
+	{ KE_KEY, 0x8b, { KEY_MAIL } },
+	{ KE_KEY, 0x8c, { KEY_MEDIA } },
+
+	/* Known non hotkey events don't handled or that we don't care yet */
+	{ KE_IGNORE, 0x8e, },
+	{ KE_IGNORE, 0x8f, },
+	{ KE_IGNORE, 0x90, },
+
+	/*
+	 * 'G key' generate two event codes, convert to only
+	 * one event/key code for now, consider replacing by
+	 * a switch (3G switch - SW_3G?)
+	 */
+	{ KE_KEY, 0x96, { KEY_F14 } },
+	{ KE_KEY, 0x97, { KEY_F14 } },
+
+	{ KE_END, 0 }
 };
 
-static struct tps_key_entry topstar_keymap[] = {
-	{ 0x80, KEY_BRIGHTNESSUP },
-	{ 0x81, KEY_BRIGHTNESSDOWN },
-	{ 0x83, KEY_VOLUMEUP },
-	{ 0x84, KEY_VOLUMEDOWN },
-	{ 0x85, KEY_MUTE },
-	{ 0x86, KEY_SWITCHVIDEOMODE },
-	{ 0x87, KEY_F13 }, /* touchpad enable/disable key */
-	{ 0x88, KEY_WLAN },
-	{ 0x8a, KEY_WWW },
-	{ 0x8b, KEY_MAIL },
-	{ 0x8c, KEY_MEDIA },
-	{ 0x96, KEY_F14 }, /* G key? */
-	{ }
-};
-
-static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
-{
-	struct tps_key_entry *key;
-
-	for (key = topstar_keymap; key->code; key++)
-		if (code == key->code)
-			return key;
-
-	return NULL;
-}
-
-static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
-{
-	struct tps_key_entry *key;
-
-	for (key = topstar_keymap; key->code; key++)
-		if (code == key->keycode)
-			return key;
-
-	return NULL;
-}
-
 static void acpi_topstar_notify(struct acpi_device *device, u32 event)
 {
-	struct tps_key_entry *key;
 	static bool dup_evnt[2];
 	bool *dup;
 	struct topstar_hkey *hkey = acpi_driver_data(device);
@@ -86,27 +72,8 @@
 		*dup = true;
 	}
 
-	/*
-	 * 'G key' generate two event codes, convert to only
-	 * one event/key code for now (3G switch?)
-	 */
-	if (event == 0x97)
-		event = 0x96;
-
-	key = tps_get_key_by_scancode(event);
-	if (key) {
-		input_report_key(hkey->inputdev, key->keycode, 1);
-		input_sync(hkey->inputdev);
-		input_report_key(hkey->inputdev, key->keycode, 0);
-		input_sync(hkey->inputdev);
-		return;
-	}
-
-	/* Known non hotkey events don't handled or that we don't care yet */
-	if (event == 0x8e || event == 0x8f || event == 0x90)
-		return;
-
-	pr_info("unknown event = 0x%02x\n", event);
+	if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
+		pr_info("unknown event = 0x%02x\n", event);
 }
 
 static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
@@ -127,62 +94,41 @@
 	return 0;
 }
 
-static int topstar_getkeycode(struct input_dev *dev,
-				unsigned int scancode, unsigned int *keycode)
-{
-	struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
-
-	if (!key)
-		return -EINVAL;
-
-	*keycode = key->keycode;
-	return 0;
-}
-
-static int topstar_setkeycode(struct input_dev *dev,
-				unsigned int scancode, unsigned int keycode)
-{
-	struct tps_key_entry *key;
-	int old_keycode;
-
-	key = tps_get_key_by_scancode(scancode);
-
-	if (!key)
-		return -EINVAL;
-
-	old_keycode = key->keycode;
-	key->keycode = keycode;
-	set_bit(keycode, dev->keybit);
-	if (!tps_get_key_by_keycode(old_keycode))
-		clear_bit(old_keycode, dev->keybit);
-	return 0;
-}
-
 static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
 {
-	struct tps_key_entry *key;
+	struct input_dev *input;
+	int error;
 
-	hkey->inputdev = input_allocate_device();
-	if (!hkey->inputdev) {
+	input = input_allocate_device();
+	if (!input) {
 		pr_err("Unable to allocate input device\n");
-		return -ENODEV;
-	}
-	hkey->inputdev->name = "Topstar Laptop extra buttons";
-	hkey->inputdev->phys = "topstar/input0";
-	hkey->inputdev->id.bustype = BUS_HOST;
-	hkey->inputdev->getkeycode = topstar_getkeycode;
-	hkey->inputdev->setkeycode = topstar_setkeycode;
-	for (key = topstar_keymap; key->code; key++) {
-		set_bit(EV_KEY, hkey->inputdev->evbit);
-		set_bit(key->keycode, hkey->inputdev->keybit);
-	}
-	if (input_register_device(hkey->inputdev)) {
-		pr_err("Unable to register input device\n");
-		input_free_device(hkey->inputdev);
-		return -ENODEV;
+		return -ENOMEM;
 	}
 
+	input->name = "Topstar Laptop extra buttons";
+	input->phys = "topstar/input0";
+	input->id.bustype = BUS_HOST;
+
+	error = sparse_keymap_setup(input, topstar_keymap, NULL);
+	if (error) {
+		pr_err("Unable to setup input device keymap\n");
+		goto err_free_dev;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		pr_err("Unable to register input device\n");
+		goto err_free_keymap;
+	}
+
+	hkey->inputdev = input;
 	return 0;
+
+ err_free_keymap:
+	sparse_keymap_free(input);
+ err_free_dev:
+	input_free_device(input);
+	return error;
 }
 
 static int acpi_topstar_add(struct acpi_device *device)
@@ -216,6 +162,7 @@
 
 	acpi_topstar_fncx_switch(device, false);
 
+	sparse_keymap_free(tps_hkey->inputdev);
 	input_unregister_device(tps_hkey->inputdev);
 	kfree(tps_hkey);
 
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 7d67a45..06f304f 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -48,6 +48,7 @@
 #include <linux/platform_device.h>
 #include <linux/rfkill.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/leds.h>
 #include <linux/slab.h>
 
@@ -121,36 +122,28 @@
 };
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
 
-struct key_entry {
-	char type;
-	u16 code;
-	u16 keycode;
-};
-
-enum {KE_KEY, KE_END};
-
-static struct key_entry toshiba_acpi_keymap[]  = {
-	{KE_KEY, 0x101, KEY_MUTE},
-	{KE_KEY, 0x102, KEY_ZOOMOUT},
-	{KE_KEY, 0x103, KEY_ZOOMIN},
-	{KE_KEY, 0x13b, KEY_COFFEE},
-	{KE_KEY, 0x13c, KEY_BATTERY},
-	{KE_KEY, 0x13d, KEY_SLEEP},
-	{KE_KEY, 0x13e, KEY_SUSPEND},
-	{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
-	{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
-	{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
-	{KE_KEY, 0x142, KEY_WLAN},
-	{KE_KEY, 0x143, KEY_PROG1},
-	{KE_KEY, 0xb05, KEY_PROG2},
-	{KE_KEY, 0xb06, KEY_WWW},
-	{KE_KEY, 0xb07, KEY_MAIL},
-	{KE_KEY, 0xb30, KEY_STOP},
-	{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
-	{KE_KEY, 0xb32, KEY_NEXTSONG},
-	{KE_KEY, 0xb33, KEY_PLAYPAUSE},
-	{KE_KEY, 0xb5a, KEY_MEDIA},
-	{KE_END, 0, 0},
+static const struct key_entry toshiba_acpi_keymap[] __initconst = {
+	{ KE_KEY, 0x101, { KEY_MUTE } },
+	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
+	{ KE_KEY, 0x103, { KEY_ZOOMIN } },
+	{ KE_KEY, 0x13b, { KEY_COFFEE } },
+	{ KE_KEY, 0x13c, { KEY_BATTERY } },
+	{ KE_KEY, 0x13d, { KEY_SLEEP } },
+	{ KE_KEY, 0x13e, { KEY_SUSPEND } },
+	{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
+	{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
+	{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
+	{ KE_KEY, 0x142, { KEY_WLAN } },
+	{ KE_KEY, 0x143, { KEY_PROG1 } },
+	{ KE_KEY, 0xb05, { KEY_PROG2 } },
+	{ KE_KEY, 0xb06, { KEY_WWW } },
+	{ KE_KEY, 0xb07, { KEY_MAIL } },
+	{ KE_KEY, 0xb30, { KEY_STOP } },
+	{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
+	{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
+	{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
+	{ KE_KEY, 0xb5a, { KEY_MEDIA } },
+	{ KE_END, 0 },
 };
 
 /* utility
@@ -852,64 +845,9 @@
         .update_status  = set_lcd_status,
 };
 
-static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
-{
-	struct key_entry *key;
-
-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->code)
-			return key;
-
-	return NULL;
-}
-
-static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
-{
-	struct key_entry *key;
-
-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
-		if (code == key->keycode && key->type == KE_KEY)
-			return key;
-
-	return NULL;
-}
-
-static int toshiba_acpi_getkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int *keycode)
-{
-	struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
-
-	if (key && key->type == KE_KEY) {
-		*keycode = key->keycode;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int toshiba_acpi_setkeycode(struct input_dev *dev,
-				   unsigned int scancode, unsigned int keycode)
-{
-	struct key_entry *key;
-	unsigned int old_keycode;
-
-	key = toshiba_acpi_get_entry_by_scancode(scancode);
-	if (key && key->type == KE_KEY) {
-		old_keycode = key->keycode;
-		key->keycode = keycode;
-		set_bit(keycode, dev->keybit);
-		if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
-			clear_bit(old_keycode, dev->keybit);
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
 static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
 {
 	u32 hci_result, value;
-	struct key_entry *key;
 
 	if (event != 0x80)
 		return;
@@ -922,19 +860,11 @@
 			if (value & 0x80)
 				continue;
 
-			key = toshiba_acpi_get_entry_by_scancode
-				(value);
-			if (!key) {
+			if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
+							value, 1, true)) {
 				printk(MY_INFO "Unknown key %x\n",
 				       value);
-				continue;
 			}
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 1);
-			input_sync(toshiba_acpi.hotkey_dev);
-			input_report_key(toshiba_acpi.hotkey_dev,
-					 key->keycode, 0);
-			input_sync(toshiba_acpi.hotkey_dev);
 		} else if (hci_result == HCI_NOT_SUPPORTED) {
 			/* This is a workaround for an unresolved issue on
 			 * some machines where system events sporadically
@@ -945,34 +875,17 @@
 	} while (hci_result != HCI_EMPTY);
 }
 
-static int toshiba_acpi_setup_keyboard(char *device)
+static int __init toshiba_acpi_setup_keyboard(char *device)
 {
 	acpi_status status;
-	acpi_handle handle;
-	int result;
-	const struct key_entry *key;
+	int error;
 
-	status = acpi_get_handle(NULL, device, &handle);
+	status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
 	if (ACPI_FAILURE(status)) {
 		printk(MY_INFO "Unable to get notification device\n");
 		return -ENODEV;
 	}
 
-	toshiba_acpi.handle = handle;
-
-	status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to enable hotkeys\n");
-		return -ENODEV;
-	}
-
-	status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
-					      toshiba_acpi_notify, NULL);
-	if (ACPI_FAILURE(status)) {
-		printk(MY_INFO "Unable to install hotkey notification\n");
-		return -ENODEV;
-	}
-
 	toshiba_acpi.hotkey_dev = input_allocate_device();
 	if (!toshiba_acpi.hotkey_dev) {
 		printk(MY_INFO "Unable to register input device\n");
@@ -982,27 +895,54 @@
 	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
 	toshiba_acpi.hotkey_dev->phys = device;
 	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
-	toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
-	toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
 
-	for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
-		set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
-		set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
+	error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
+				    toshiba_acpi_keymap, NULL);
+	if (error)
+		goto err_free_dev;
+
+	status = acpi_install_notify_handler(toshiba_acpi.handle,
+				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_INFO "Unable to install hotkey notification\n");
+		error = -ENODEV;
+		goto err_free_keymap;
 	}
 
-	result = input_register_device(toshiba_acpi.hotkey_dev);
-	if (result) {
+	status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
+	if (ACPI_FAILURE(status)) {
+		printk(MY_INFO "Unable to enable hotkeys\n");
+		error = -ENODEV;
+		goto err_remove_notify;
+	}
+
+	error = input_register_device(toshiba_acpi.hotkey_dev);
+	if (error) {
 		printk(MY_INFO "Unable to register input device\n");
-		return result;
+		goto err_remove_notify;
 	}
 
 	return 0;
+
+ err_remove_notify:
+	acpi_remove_notify_handler(toshiba_acpi.handle,
+				   ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+ err_free_keymap:
+	sparse_keymap_free(toshiba_acpi.hotkey_dev);
+ err_free_dev:
+	input_free_device(toshiba_acpi.hotkey_dev);
+	toshiba_acpi.hotkey_dev = NULL;
+	return error;
 }
 
 static void toshiba_acpi_exit(void)
 {
-	if (toshiba_acpi.hotkey_dev)
+	if (toshiba_acpi.hotkey_dev) {
+		acpi_remove_notify_handler(toshiba_acpi.handle,
+				ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
+		sparse_keymap_free(toshiba_acpi.hotkey_dev);
 		input_unregister_device(toshiba_acpi.hotkey_dev);
+	}
 
 	if (toshiba_acpi.bt_rfk) {
 		rfkill_unregister(toshiba_acpi.bt_rfk);
@@ -1017,9 +957,6 @@
 	if (toshiba_proc_dir)
 		remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
 
-	acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
-				   toshiba_acpi_notify);
-
 	if (toshiba_acpi.illumination_installed)
 		led_classdev_unregister(&toshiba_led);
 
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index b2978a0..104b77c 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -27,6 +27,8 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/types.h>
@@ -44,9 +46,8 @@
 
 #define ACPI_WMI_CLASS "wmi"
 
-#define PREFIX "ACPI: WMI: "
-
 static DEFINE_MUTEX(wmi_data_lock);
+static LIST_HEAD(wmi_block_list);
 
 struct guid_block {
 	char guid[16];
@@ -67,10 +68,9 @@
 	acpi_handle handle;
 	wmi_notify_handler handler;
 	void *handler_data;
-	struct device *dev;
+	struct device dev;
 };
 
-static struct wmi_block wmi_blocks;
 
 /*
  * If the GUID data block is marked as expensive, we must enable and
@@ -110,7 +110,7 @@
 		.add = acpi_wmi_add,
 		.remove = acpi_wmi_remove,
 		.notify = acpi_wmi_notify,
-		},
+	},
 };
 
 /*
@@ -128,30 +128,18 @@
  */
 static int wmi_parse_hexbyte(const u8 *src)
 {
-	unsigned int x; /* For correct wrapping */
 	int h;
+	int value;
 
 	/* high part */
-	x = src[0];
-	if (x - '0' <= '9' - '0') {
-		h = x - '0';
-	} else if (x - 'a' <= 'f' - 'a') {
-		h = x - 'a' + 10;
-	} else if (x - 'A' <= 'F' - 'A') {
-		h = x - 'A' + 10;
-	} else {
+	h = value = hex_to_bin(src[0]);
+	if (value < 0)
 		return -1;
-	}
-	h <<= 4;
 
 	/* low part */
-	x = src[1];
-	if (x - '0' <= '9' - '0')
-		return h | (x - '0');
-	if (x - 'a' <= 'f' - 'a')
-		return h | (x - 'a' + 10);
-	if (x - 'A' <= 'F' - 'A')
-		return h | (x - 'A' + 10);
+	value = hex_to_bin(src[1]);
+	if (value >= 0)
+		return (h << 4) | value;
 	return -1;
 }
 
@@ -232,7 +220,7 @@
 	for (i = 10; i <= 15; i++)
 		out += sprintf(out, "%02X", in[i] & 0xFF);
 
-	out = '\0';
+	*out = '\0';
 	return 0;
 }
 
@@ -246,7 +234,7 @@
 	wmi_parse_guid(guid_string, tmp);
 	wmi_swap_bytes(tmp, guid_input);
 
-	list_for_each(p, &wmi_blocks.list) {
+	list_for_each(p, &wmi_block_list) {
 		wblock = list_entry(p, struct wmi_block, list);
 		block = &wblock->gblock;
 
@@ -487,30 +475,29 @@
 }
 EXPORT_SYMBOL_GPL(wmi_set_block);
 
-static void wmi_dump_wdg(struct guid_block *g)
+static void wmi_dump_wdg(const struct guid_block *g)
 {
 	char guid_string[37];
 
 	wmi_gtoa(g->guid, guid_string);
-	printk(KERN_INFO PREFIX "%s:\n", guid_string);
-	printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
-	       g->object_id[0], g->object_id[1]);
-	printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
-	printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
-	printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
-	printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
+
+	pr_info("%s:\n", guid_string);
+	pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
+	pr_info("\tnotify_id: %02X\n", g->notify_id);
+	pr_info("\treserved: %02X\n", g->reserved);
+	pr_info("\tinstance_count: %d\n", g->instance_count);
+	pr_info("\tflags: %#x ", g->flags);
 	if (g->flags) {
-		printk(" ");
 		if (g->flags & ACPI_WMI_EXPENSIVE)
-			printk("ACPI_WMI_EXPENSIVE ");
+			pr_cont("ACPI_WMI_EXPENSIVE ");
 		if (g->flags & ACPI_WMI_METHOD)
-			printk("ACPI_WMI_METHOD ");
+			pr_cont("ACPI_WMI_METHOD ");
 		if (g->flags & ACPI_WMI_STRING)
-			printk("ACPI_WMI_STRING ");
+			pr_cont("ACPI_WMI_STRING ");
 		if (g->flags & ACPI_WMI_EVENT)
-			printk("ACPI_WMI_EVENT ");
+			pr_cont("ACPI_WMI_EVENT ");
 	}
-	printk("\n");
+	pr_cont("\n");
 
 }
 
@@ -522,7 +509,7 @@
 
 	status = wmi_get_event_data(value, &response);
 	if (status != AE_OK) {
-		printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
+		pr_info("bad event status 0x%x\n", status);
 		return;
 	}
 
@@ -531,22 +518,22 @@
 	if (!obj)
 		return;
 
-	printk(KERN_INFO PREFIX "DEBUG Event ");
+	pr_info("DEBUG Event ");
 	switch(obj->type) {
 	case ACPI_TYPE_BUFFER:
-		printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
+		pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
 		break;
 	case ACPI_TYPE_STRING:
-		printk("STRING_TYPE - %s\n", obj->string.pointer);
+		pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
 		break;
 	case ACPI_TYPE_INTEGER:
-		printk("INTEGER_TYPE - %llu\n", obj->integer.value);
+		pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
 		break;
 	case ACPI_TYPE_PACKAGE:
-		printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
+		pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
 		break;
 	default:
-		printk("object type 0x%X\n", obj->type);
+		pr_cont("object type 0x%X\n", obj->type);
 	}
 	kfree(obj);
 }
@@ -633,7 +620,7 @@
 	params[0].type = ACPI_TYPE_INTEGER;
 	params[0].integer.value = event;
 
-	list_for_each(p, &wmi_blocks.list) {
+	list_for_each(p, &wmi_block_list) {
 		wblock = list_entry(p, struct wmi_block, list);
 		gblock = &wblock->gblock;
 
@@ -662,7 +649,7 @@
 /*
  * sysfs interface
  */
-static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 			     char *buf)
 {
 	char guid_string[37];
@@ -676,7 +663,11 @@
 
 	return sprintf(buf, "wmi:%s\n", guid_string);
 }
-static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+
+static struct device_attribute wmi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL
+};
 
 static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -702,108 +693,71 @@
 
 static void wmi_dev_free(struct device *dev)
 {
-	kfree(dev);
+	struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
+
+	kfree(wmi_block);
 }
 
 static struct class wmi_class = {
 	.name = "wmi",
 	.dev_release = wmi_dev_free,
 	.dev_uevent = wmi_dev_uevent,
+	.dev_attrs = wmi_dev_attrs,
 };
 
-static int wmi_create_devs(void)
+static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
+					   acpi_handle handle)
 {
-	int result;
-	char guid_string[37];
-	struct guid_block *gblock;
 	struct wmi_block *wblock;
-	struct list_head *p;
-	struct device *guid_dev;
+	int error;
+	char guid_string[37];
 
-	/* Create devices for all the GUIDs */
-	list_for_each(p, &wmi_blocks.list) {
-		wblock = list_entry(p, struct wmi_block, list);
-
-		guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
-		if (!guid_dev)
-			return -ENOMEM;
-
-		wblock->dev = guid_dev;
-
-		guid_dev->class = &wmi_class;
-		dev_set_drvdata(guid_dev, wblock);
-
-		gblock = &wblock->gblock;
-
-		wmi_gtoa(gblock->guid, guid_string);
-		dev_set_name(guid_dev, guid_string);
-
-		result = device_register(guid_dev);
-		if (result)
-			return result;
-
-		result = device_create_file(guid_dev, &dev_attr_modalias);
-		if (result)
-			return result;
+	wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
+	if (!wblock) {
+		error = -ENOMEM;
+		goto err_out;
 	}
 
-	return 0;
+	wblock->handle = handle;
+	wblock->gblock = *gblock;
+
+	wblock->dev.class = &wmi_class;
+
+	wmi_gtoa(gblock->guid, guid_string);
+	dev_set_name(&wblock->dev, guid_string);
+
+	dev_set_drvdata(&wblock->dev, wblock);
+
+	error = device_register(&wblock->dev);
+	if (error)
+		goto err_free;
+
+	list_add_tail(&wblock->list, &wmi_block_list);
+	return wblock;
+
+err_free:
+	kfree(wblock);
+err_out:
+	return ERR_PTR(error);
 }
 
-static void wmi_remove_devs(void)
+static void wmi_free_devices(void)
 {
-	struct guid_block *gblock;
-	struct wmi_block *wblock;
-	struct list_head *p;
-	struct device *guid_dev;
+	struct wmi_block *wblock, *next;
 
 	/* Delete devices for all the GUIDs */
-	list_for_each(p, &wmi_blocks.list) {
-		wblock = list_entry(p, struct wmi_block, list);
-
-		guid_dev = wblock->dev;
-		gblock = &wblock->gblock;
-
-		device_remove_file(guid_dev, &dev_attr_modalias);
-
-		device_unregister(guid_dev);
-	}
-}
-
-static void wmi_class_exit(void)
-{
-	wmi_remove_devs();
-	class_unregister(&wmi_class);
-}
-
-static int wmi_class_init(void)
-{
-	int ret;
-
-	ret = class_register(&wmi_class);
-	if (ret)
-		return ret;
-
-	ret = wmi_create_devs();
-	if (ret)
-		wmi_class_exit();
-
-	return ret;
+	list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
+		device_unregister(&wblock->dev);
 }
 
 static bool guid_already_parsed(const char *guid_string)
 {
-	struct guid_block *gblock;
 	struct wmi_block *wblock;
-	struct list_head *p;
 
-	list_for_each(p, &wmi_blocks.list) {
-		wblock = list_entry(p, struct wmi_block, list);
-		gblock = &wblock->gblock;
-
-		if (strncmp(gblock->guid, guid_string, 16) == 0)
+	list_for_each_entry(wblock, &wmi_block_list, list)
+		if (strncmp(wblock->gblock.guid, guid_string, 16) == 0)
 			return true;
-	}
+
 	return false;
 }
 
@@ -814,30 +768,29 @@
 {
 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
 	union acpi_object *obj;
-	struct guid_block *gblock;
+	const struct guid_block *gblock;
 	struct wmi_block *wblock;
 	char guid_string[37];
 	acpi_status status;
+	int retval;
 	u32 i, total;
 
 	status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
-
 	if (ACPI_FAILURE(status))
-		return status;
+		return -ENXIO;
 
 	obj = (union acpi_object *) out.pointer;
+	if (!obj)
+		return -ENXIO;
 
-	if (obj->type != ACPI_TYPE_BUFFER)
-		return AE_ERROR;
-
-	total = obj->buffer.length / sizeof(struct guid_block);
-
-	gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
-	if (!gblock) {
-		status = AE_NO_MEMORY;
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		retval = -ENXIO;
 		goto out_free_pointer;
 	}
 
+	gblock = (const struct guid_block *)obj->buffer.pointer;
+	total = obj->buffer.length / sizeof(struct guid_block);
+
 	for (i = 0; i < total; i++) {
 		/*
 		  Some WMI devices, like those for nVidia hooks, have a
@@ -848,34 +801,32 @@
 		*/
 		if (guid_already_parsed(gblock[i].guid) == true) {
 			wmi_gtoa(gblock[i].guid, guid_string);
-			printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
-				guid_string);
+			pr_info("Skipping duplicate GUID %s\n", guid_string);
 			continue;
 		}
+
 		if (debug_dump_wdg)
 			wmi_dump_wdg(&gblock[i]);
 
-		wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
-		if (!wblock) {
-			status = AE_NO_MEMORY;
-			goto out_free_gblock;
+		wblock = wmi_create_device(&gblock[i], handle);
+		if (IS_ERR(wblock)) {
+			retval = PTR_ERR(wblock);
+			wmi_free_devices();
+			break;
 		}
 
-		wblock->gblock = gblock[i];
-		wblock->handle = handle;
 		if (debug_event) {
 			wblock->handler = wmi_notify_debug;
-			status = wmi_method_enable(wblock, 1);
+			wmi_method_enable(wblock, 1);
 		}
-		list_add_tail(&wblock->list, &wmi_blocks.list);
 	}
 
-out_free_gblock:
-	kfree(gblock);
+	retval = 0;
+
 out_free_pointer:
 	kfree(out.pointer);
 
-	return status;
+	return retval;
 }
 
 /*
@@ -929,7 +880,7 @@
 	struct list_head *p;
 	char guid_string[37];
 
-	list_for_each(p, &wmi_blocks.list) {
+	list_for_each(p, &wmi_block_list) {
 		wblock = list_entry(p, struct wmi_block, list);
 		block = &wblock->gblock;
 
@@ -939,8 +890,7 @@
 				wblock->handler(event, wblock->handler_data);
 			if (debug_event) {
 				wmi_gtoa(wblock->gblock.guid, guid_string);
-				printk(KERN_INFO PREFIX "DEBUG Event GUID:"
-				       " %s\n", guid_string);
+				pr_info("DEBUG Event GUID: %s\n", guid_string);
 			}
 
 			acpi_bus_generate_netlink_event(
@@ -955,6 +905,7 @@
 {
 	acpi_remove_address_space_handler(device->handle,
 				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
+	wmi_free_devices();
 
 	return 0;
 }
@@ -962,68 +913,57 @@
 static int acpi_wmi_add(struct acpi_device *device)
 {
 	acpi_status status;
-	int result = 0;
+	int error;
 
 	status = acpi_install_address_space_handler(device->handle,
 						    ACPI_ADR_SPACE_EC,
 						    &acpi_wmi_ec_space_handler,
 						    NULL, NULL);
-	if (ACPI_FAILURE(status))
-		return -ENODEV;
-
-	status = parse_wdg(device->handle);
 	if (ACPI_FAILURE(status)) {
-		printk(KERN_ERR PREFIX "Error installing EC region handler\n");
+		pr_err("Error installing EC region handler\n");
 		return -ENODEV;
 	}
 
-	return result;
+	error = parse_wdg(device->handle);
+	if (error) {
+		acpi_remove_address_space_handler(device->handle,
+						  ACPI_ADR_SPACE_EC,
+						  &acpi_wmi_ec_space_handler);
+		pr_err("Failed to parse WDG method\n");
+		return error;
+	}
+
+	return 0;
 }
 
 static int __init acpi_wmi_init(void)
 {
-	int result;
-
-	INIT_LIST_HEAD(&wmi_blocks.list);
+	int error;
 
 	if (acpi_disabled)
 		return -ENODEV;
 
-	result = acpi_bus_register_driver(&acpi_wmi_driver);
+	error = class_register(&wmi_class);
+	if (error)
+		return error;
 
-	if (result < 0) {
-		printk(KERN_INFO PREFIX "Error loading mapper\n");
-		return -ENODEV;
+	error = acpi_bus_register_driver(&acpi_wmi_driver);
+	if (error) {
+		pr_err("Error loading mapper\n");
+		class_unregister(&wmi_class);
+		return error;
 	}
 
-	result = wmi_class_init();
-	if (result) {
-		acpi_bus_unregister_driver(&acpi_wmi_driver);
-		return result;
-	}
-
-	printk(KERN_INFO PREFIX "Mapper loaded\n");
-
-	return result;
+	pr_info("Mapper loaded\n");
+	return 0;
 }
 
 static void __exit acpi_wmi_exit(void)
 {
-	struct list_head *p, *tmp;
-	struct wmi_block *wblock;
-
-	wmi_class_exit();
-
 	acpi_bus_unregister_driver(&acpi_wmi_driver);
+	class_unregister(&wmi_class);
 
-	list_for_each_safe(p, tmp, &wmi_blocks.list) {
-		wblock = list_entry(p, struct wmi_block, list);
-
-		list_del(p);
-		kfree(wblock);
-	}
-
-	printk(KERN_INFO PREFIX "Mapper unloaded\n");
+	pr_info("Mapper unloaded\n");
 }
 
 subsys_initcall(acpi_wmi_init);
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
new file mode 100644
index 0000000..e549eee
--- /dev/null
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -0,0 +1,85 @@
+/*
+ * Support for rfkill through the OLPC XO-1 laptop embedded controller
+ *
+ * Copyright (C) 2010 One Laptop per Child
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+
+#include <asm/olpc.h>
+
+static int rfkill_set_block(void *data, bool blocked)
+{
+	unsigned char cmd;
+	if (blocked)
+		cmd = EC_WLAN_ENTER_RESET;
+	else
+		cmd = EC_WLAN_LEAVE_RESET;
+
+	return olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
+}
+
+static const struct rfkill_ops rfkill_ops = {
+	.set_block = rfkill_set_block,
+};
+
+static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
+{
+	struct rfkill *rfk;
+	int r;
+
+	rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN,
+			   &rfkill_ops, NULL);
+	if (!rfk)
+		return -ENOMEM;
+
+	r = rfkill_register(rfk);
+	if (r) {
+		rfkill_destroy(rfk);
+		return r;
+	}
+
+	platform_set_drvdata(pdev, rfk);
+	return 0;
+}
+
+static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
+{
+	struct rfkill *rfk = platform_get_drvdata(pdev);
+	rfkill_unregister(rfk);
+	rfkill_destroy(rfk);
+	return 0;
+}
+
+static struct platform_driver xo1_rfkill_driver = {
+	.driver = {
+		.name = "xo1-rfkill",
+		.owner = THIS_MODULE,
+	},
+	.probe		= xo1_rfkill_probe,
+	.remove		= __devexit_p(xo1_rfkill_remove),
+};
+
+static int __init xo1_rfkill_init(void)
+{
+	return platform_driver_register(&xo1_rfkill_driver);
+}
+
+static void __exit xo1_rfkill_exit(void)
+{
+	platform_driver_unregister(&xo1_rfkill_driver);
+}
+
+MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:xo1-rfkill");
+
+module_init(xo1_rfkill_init);
+module_exit(xo1_rfkill_exit);