Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platf...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Oct 2010 15:28:13 +0000 (08:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Oct 2010 15:28:13 +0000 (08:28 -0700)
* '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
  ...

24 files changed:
Documentation/ABI/testing/sysfs-devices-system-ibm-rtl [new file with mode: 0644]
MAINTAINERS
arch/x86/include/asm/olpc.h
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/eeepc-wmi.c
drivers/platform/x86/hdaps.c [moved from drivers/hwmon/hdaps.c with 99% similarity]
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/ibm_rtl.c [new file with mode: 0644]
drivers/platform/x86/ideapad-laptop.c [moved from drivers/platform/x86/ideapad_acpi.c with 53% similarity]
drivers/platform/x86/intel_pmic_gpio.c
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/panasonic-laptop.c
drivers/platform/x86/topstar-laptop.c
drivers/platform/x86/toshiba_acpi.c
drivers/platform/x86/wmi.c
drivers/platform/x86/xo1-rfkill.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl
new file mode 100644 (file)
index 0000000..b82deea
--- /dev/null
@@ -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.
+
+
index 69aa8fe..3e95a81 100644 (file)
@@ -2646,10 +2646,10 @@ F:      drivers/net/greth*
 
 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>
index 101229b..42a978c 100644 (file)
@@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits);
 /* EC commands */
 
 #define EC_FIRMWARE_REV                0x08
+#define EC_WLAN_ENTER_RESET    0x35
+#define EC_WLAN_LEAVE_RESET    0x25
 
 /* SCI source values */
 
index 97499d0..e382da3 100644 (file)
@@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45
          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
index e3c2484..ec9cb73 100644 (file)
@@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A)   += g760a.o
 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
index cff7cc2..faec777 100644 (file)
@@ -92,6 +92,7 @@ config DELL_WMI
        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 @@ config HP_WMI
        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 @@ config PANASONIC_LAPTOP
        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 @@ config SONYPI_COMPAT
          ---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 @@ config THINKPAD_ACPI_HOTKEY_POLL
          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 @@ config TOPSTAR_LAPTOP
        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 @@ config ACPI_TOSHIBA
        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 @@ config INTEL_IPS
          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
index 85fb2b8..9950ccc 100644 (file)
@@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF)         += acerhdf.o
 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_INTEL_SCU_IPC)   += intel_scu_ipc.o
 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
index 2badee2..c8c6537 100644 (file)
@@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void)
                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;
        }
index b756e07..60a5a5c 100644 (file)
@@ -236,7 +236,6 @@ struct asus_laptop {
        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 @@ static const struct key_entry asus_keymap[] = {
        {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_notify(struct asus_laptop *asus)
 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;
-
-               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);
-               }
+       if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
+           acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
+           !lcd_switch_handle)
+               return 0;
 
-               asus->backlight_device = bd;
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = 15;
 
-               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 ssize_t store_gps(struct device *dev, struct device_attribute *attr,
  */
 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 @@ static int asus_rfkill_init(struct asus_laptop *asus)
 
        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 @@ static int asus_input_init(struct asus_laptop *asus)
        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 @@ static void asus_input_exit(struct asus_laptop *asus)
                sparse_keymap_free(asus->inputdev);
                input_unregister_device(asus->inputdev);
        }
+       asus->inputdev = NULL;
 }
 
 /*
@@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
 
 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;
-
-       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 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
+};
 
-static int asus_sysfs_init(struct asus_laptop *asus)
+static mode_t asus_sysfs_is_visible(struct kobject *kobj,
+                                   struct attribute *attr,
+                                   int idx)
 {
-       struct platform_device *device = asus->platform_device;
-       int err;
+       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;
 
-       err = device_create_file(&device->dev, &dev_attr_infos);
-       if (err)
-               return err;
+       if (attr == &dev_attr_wlan.attr) {
+               supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
 
-       if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) {
-               err = device_create_file(&device->dev, &dev_attr_wlan);
-               if (err)
-                       return err;
-       }
+       } else if (attr == &dev_attr_bluetooth.attr) {
+               supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
 
-       if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) {
-               err = device_create_file(&device->dev, &dev_attr_bluetooth);
-               if (err)
-                       return err;
-       }
+       } else if (attr == &dev_attr_display.attr) {
+               supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
 
-       if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
-               err = device_create_file(&device->dev, &dev_attr_display);
-               if (err)
-                       return err;
-       }
+       } else if (attr == &dev_attr_ledd.attr) {
+               supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
 
-       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;
-       }
+       } 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);
 
-       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;
+       } 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 err;
+       return supported ? attr->mode : 0;
 }
 
+
+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 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
        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 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
        return result;
 }
 
+static bool asus_device_present;
+
 static int __devinit asus_acpi_add(struct acpi_device *device)
 {
        struct asus_laptop *asus;
index 4413975..cf8a89a 100644 (file)
@@ -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 @@ static const struct rfkill_ops dell_rfkill_ops = {
        .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 @@ static int __init dell_init(void)
                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 @@ fail_platform_driver:
 
 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);
index 08fb70f..77f1d55 100644 (file)
@@ -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 @@ static int acpi_video;
 
 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, 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, 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 }
 };
 
 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 @@ static u16 bios_to_linux_keycode[256] = {
        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 @@ static void dell_wmi_notify(u32 value, void *context)
        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 @@ static void dell_wmi_notify(u32 value, void *context)
                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 @@ static void dell_wmi_notify(u32 value, void *context)
                         * 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;
-
-       dell_wmi_keymap = dell_new_wmi_keymap;
+       keymap[hotkey_num].type = KE_END;
 
+       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 = input_register_device(dell_wmi_input_dev);
+               err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
 
-       if (err) {
-               input_free_device(dell_wmi_input_dev);
-               return err;
+               /*
+                * 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)
+               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 @@ static int __init dell_wmi_init(void)
        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 @@ static int __init dell_wmi_init(void)
 
        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);
index 6b8e062..b2edfdc 100644 (file)
@@ -165,6 +165,7 @@ struct eeepc_laptop {
        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 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
        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 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
                sparse_keymap_free(eeepc->inputdev);
                input_unregister_device(eeepc->inputdev);
        }
+       eeepc->inputdev = NULL;
 }
 
 /*
@@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
        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 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
        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);
index 9dc50fb..462ceab 100644 (file)
@@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
 
 #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 @@ static const struct key_entry eeepc_wmi_keymap[] = {
        { 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 @@ static void eeepc_wmi_notify(u32 value, void *context)
        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 @@ static int __init eeepc_wmi_init(void)
                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 @@ static void __exit eeepc_wmi_exit(void)
 {
        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);
similarity index 99%
rename from drivers/hwmon/hdaps.c
rename to drivers/platform/x86/hdaps.c
index bfd42f1..067bf36 100644 (file)
@@ -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>
index c174114..1dac659 100644 (file)
@@ -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 @@ struct bios_return {
        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(als, S_IRUGO | S_IWUSR, show_als, set_als);
 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 @@ static void hp_wmi_notify(u32 value, void *context)
                                           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 void hp_wmi_notify(u32 value, void *context)
 
 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 @@ static int __init hp_wmi_input_setup(void)
        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 @@ static int __init hp_wmi_input_setup(void)
                            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 @@ static int __init hp_wmi_init(void)
        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_add:
 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 (file)
index 0000000..3c2c6b9
--- /dev/null
@@ -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);
similarity index 53%
rename from drivers/platform/x86/ideapad_acpi.c
rename to drivers/platform/x86/ideapad-laptop.c
index 7984963..5ff1220 100644 (file)
 #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[] = {
-       /* camera has no rfkill */
-       { "ideapad_wlan",       RFKILL_TYPE_WLAN },
-       { "ideapad_bluetooth",  RFKILL_TYPE_BLUETOOTH },
-       { "ideapad_3g",         RFKILL_TYPE_WWAN },
-       { "ideapad_killsw",     RFKILL_TYPE_WLAN }
+       { "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 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;
+static bool no_bt_rfkill;
+module_param(no_bt_rfkill, bool, 0444);
+MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
 
-       output.length = sizeof(out_obj);
-       output.pointer = &out_obj;
+/*
+ * ACPI Helpers
+ */
+#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
 
-       in_param.type = ACPI_TYPE_INTEGER;
-       in_param.integer.value = device + 1;
+static int read_method_int(acpi_handle handle, const char *method, int *val)
+{
+       acpi_status status;
+       unsigned long long result;
 
-       status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
+       status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
        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;
+               *val = -1;
+               return -1;
+       } else {
+               *val = result;
+               return 0;
        }
-       return out_obj.integer.value;
 }
 
-static int ideapad_dev_get_state(int device)
+static int method_vpcr(acpi_handle handle, int cmd, int *ret)
 {
        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;
+       unsigned long long result;
+       struct acpi_object_list params;
+       union acpi_object in_obj;
 
-       output.length = sizeof(out_obj);
-       output.pointer = &out_obj;
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = cmd;
 
-       in_param.type = ACPI_TYPE_INTEGER;
-       in_param.integer.value = device + 1;
+       status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
 
-       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;
+               *ret = -1;
+               return -1;
+       } else {
+               *ret = result;
+               return 0;
        }
-       return out_obj.integer.value;
 }
 
-static int ideapad_dev_set_state(int device, int state)
+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;
-       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;
+       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(NULL, "\\_SB_.SECN", &input, NULL);
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
-               return -ENODEV;
-       }
+       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)
 {
-       int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
-       if (state < 0)
-               return state;
+       struct ideapad_private *priv = dev_get_drvdata(dev);
+       acpi_handle handle = priv->handle;
+       unsigned long result;
 
-       return sprintf(buf, "%d\n", state);
+       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 = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
+       ret = write_ec_cmd(handle, 0x1E, state);
        if (ret < 0)
                return ret;
        return count;
@@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked)
 
        if (device == IDEAPAD_DEV_KILLSW)
                return -EINVAL;
-       return ideapad_dev_set_state(device, !blocked);
+
+       return write_ec_cmd(ideapad_priv->handle,
+                           ideapad_rfk_data[device].opcode,
+                           !blocked);
 }
 
 static struct rfkill_ops ideapad_rfk_ops = {
@@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = {
 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);
+       acpi_handle handle = priv->handle;
+       unsigned long hw_blocked;
        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)
+       if (read_ec_data(handle, 0x23, &hw_blocked))
                return;
+       hw_blocked = !hw_blocked;
 
-       for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
+       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));
+                       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-1].name, &adevice->dev,
-                                     ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
+       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]);
@@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
 
 static int ideapad_acpi_add(struct acpi_device *adevice)
 {
-       int i;
+       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++) {
-               devs_present[i] = ideapad_dev_exists(i);
-               if (devs_present[i] < 0)
-                       return devs_present[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 */
@@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice)
                }
        }
 
+       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;
@@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
 
 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
 {
-       ideapad_sync_rfk_state(adevice);
+       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 = {
index 5cdcff6..f540ff9 100644 (file)
@@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip,
 
        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 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
        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 @@ static int pmic_irq_type(unsigned irq, unsigned type)
        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);
index 6abe18e..41a9e34 100644 (file)
@@ -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>
 
index ec01c3d..cc1e0ba 100644 (file)
 #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 @@ static struct acpi_driver acpi_pcc_driver = {
                        },
 };
 
-#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 inline int acpi_pcc_get_sqty(struct acpi_device *device)
        }
 }
 
-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 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
        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 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
        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 @@ static int bl_get(struct backlight_device *bd)
 {
        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 @@ static int bl_set_status(struct backlight_device *bd)
        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 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
        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 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
        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 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
        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 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
        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 @@ static struct attribute_group pcc_attr_group = {
 
 /* 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 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
 
        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 void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
 
 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);
-
-       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;
+       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;
 
-       /* load initial keymap */
-       memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+       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;
+       }
 
-       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);
+       error = input_register_device(input_dev);
+       if (error) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Unable to register input device\n"));
+               goto err_free_keymap;
+       }
 
-       input_set_drvdata(pcc->input_dev, pcc);
+       pcc->input_dev = input_dev;
+       return 0;
 
-       rc = input_register_device(pcc->input_dev);
-       if (rc < 0)
-               input_free_device(pcc->input_dev);
+ err_free_keymap:
+       sparse_keymap_free(input_dev);
+ err_free_dev:
+       input_free_device(input_dev);
+       return error;
+}
 
-       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 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
        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 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
                                                   &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 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
 
 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 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
 
        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);
index ff4b476..1d07d6d 100644 (file)
@@ -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 topstar_hkey {
        struct input_dev *inputdev;
 };
 
-struct tps_key_entry {
-       u8 code;
-       u16 keycode;
-};
-
-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;
+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 } },
 
-       return NULL;
-}
-
-static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
-{
-       struct tps_key_entry *key;
+       /* Known non hotkey events don't handled or that we don't care yet */
+       { KE_IGNORE, 0x8e, },
+       { KE_IGNORE, 0x8f, },
+       { KE_IGNORE, 0x90, },
 
-       for (key = topstar_keymap; key->code; key++)
-               if (code == key->keycode)
-                       return key;
+       /*
+        * '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 } },
 
-       return NULL;
-}
+       { KE_END, 0 }
+};
 
 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 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)
                *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 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
        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;
+               return -ENOMEM;
        }
-       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);
+
+       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;
        }
-       if (input_register_device(hkey->inputdev)) {
+
+       error = input_register_device(input);
+       if (error) {
                pr_err("Unable to register input device\n");
-               input_free_device(hkey->inputdev);
-               return -ENODEV;
+               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 @@ static int acpi_topstar_remove(struct acpi_device *device, int type)
 
        acpi_topstar_fncx_switch(device, false);
 
+       sparse_keymap_free(tps_hkey->inputdev);
        input_unregister_device(tps_hkey->inputdev);
        kfree(tps_hkey);
 
index 7d67a45..06f304f 100644 (file)
@@ -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 @@ static const struct acpi_device_id toshiba_device_ids[] = {
 };
 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 @@ static struct backlight_ops toshiba_backlight_data = {
         .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 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
                        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 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
        } 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 @@ static int toshiba_acpi_setup_keyboard(char *device)
        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;
+       }
+
+       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;
        }
 
-       result = input_register_device(toshiba_acpi.hotkey_dev);
-       if (result) {
+       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 @@ static void toshiba_acpi_exit(void)
        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);
 
index b2978a0..104b77c 100644 (file)
@@ -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 @@ MODULE_LICENSE("GPL");
 
 #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 @@ struct wmi_block {
        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 @@ static struct acpi_driver acpi_wmi_driver = {
                .add = acpi_wmi_add,
                .remove = acpi_wmi_remove,
                .notify = acpi_wmi_notify,
-               },
+       },
 };
 
 /*
@@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = {
  */
 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 @@ static int wmi_gtoa(const char *in, char *out)
        for (i = 10; i <= 15; i++)
                out += sprintf(out, "%02X", in[i] & 0xFF);
 
-       out = '\0';
+       *out = '\0';
        return 0;
 }
 
@@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
        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 @@ const struct acpi_buffer *in)
 }
 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 @@ static void wmi_notify_debug(u32 value, void *context)
 
        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 @@ static void wmi_notify_debug(u32 value, void *context)
        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 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
        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 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
 /*
  * 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 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
 
        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 int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 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;
-
-       /* 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;
+       int error;
+       char guid_string[37];
 
-               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;
 
-static void wmi_remove_devs(void)
-{
-       struct guid_block *gblock;
-       struct wmi_block *wblock;
-       struct list_head *p;
-       struct device *guid_dev;
+       wblock->dev.class = &wmi_class;
 
-       /* Delete devices for all the GUIDs */
-       list_for_each(p, &wmi_blocks.list) {
-               wblock = list_entry(p, struct wmi_block, list);
+       wmi_gtoa(gblock->guid, guid_string);
+       dev_set_name(&wblock->dev, guid_string);
 
-               guid_dev = wblock->dev;
-               gblock = &wblock->gblock;
+       dev_set_drvdata(&wblock->dev, wblock);
 
-               device_remove_file(guid_dev, &dev_attr_modalias);
+       error = device_register(&wblock->dev);
+       if (error)
+               goto err_free;
 
-               device_unregister(guid_dev);
-       }
-}
+       list_add_tail(&wblock->list, &wmi_block_list);
+       return wblock;
 
-static void wmi_class_exit(void)
-{
-       wmi_remove_devs();
-       class_unregister(&wmi_class);
+err_free:
+       kfree(wblock);
+err_out:
+       return ERR_PTR(error);
 }
 
-static int wmi_class_init(void)
+static void wmi_free_devices(void)
 {
-       int ret;
-
-       ret = class_register(&wmi_class);
-       if (ret)
-               return ret;
+       struct wmi_block *wblock, *next;
 
-       ret = wmi_create_devs();
-       if (ret)
-               wmi_class_exit();
-
-       return ret;
+       /* Delete devices for all the GUIDs */
+       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 @@ static acpi_status parse_wdg(acpi_handle handle)
 {
        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 @@ static acpi_status parse_wdg(acpi_handle handle)
                */
                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 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
        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 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
                                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 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
 {
        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_remove(struct acpi_device *device, int type)
 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 (file)
index 0000000..e549eee
--- /dev/null
@@ -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);