diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 53 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 6 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 171 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 77 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 256 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 16 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 56 | ||||
-rw-r--r-- | drivers/platform/x86/hdaps.c | 637 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 172 | ||||
-rw-r--r-- | drivers/platform/x86/ibm_rtl.c | 341 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c (renamed from drivers/platform/x86/ideapad_acpi.c) | 238 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmic_gpio.c | 26 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/panasonic-laptop.c | 194 | ||||
-rw-r--r-- | drivers/platform/x86/topstar-laptop.c | 161 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 191 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 308 | ||||
-rw-r--r-- | drivers/platform/x86/xo1-rfkill.c | 85 |
19 files changed, 1980 insertions, 1011 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cff7cc2c1f0..faec777b1ed 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -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 diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 85fb2b84f57..9950ccc940b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -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 diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 2badee2fdee..c8c65375bfe 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -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; } diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b756e07d41b..60a5a5c6b50 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -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; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 4413975912e..cf8a89a0d8f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -25,6 +25,8 @@ #include <linux/mm.h> #include <linux/i8042.h> #include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -325,6 +327,75 @@ 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); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 08fb70f6d9b..77f1d55414c 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <acpi/acpi_drivers.h> #include <linux/acpi.h> #include <linux/string.h> @@ -44,78 +45,70 @@ 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); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6b8e06206c4..b2edfdcdcb8 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -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); diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9dc50fbf3d0..462ceab93f8 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -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); diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c new file mode 100644 index 00000000000..067bf36d32f --- /dev/null +++ b/drivers/platform/x86/hdaps.c @@ -0,0 +1,637 @@ +/* + * 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> + * + * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads + * starting with the R40, T41, and X40. It provides a basic two-axis + * accelerometer and other data, such as the device's temperature. + * + * This driver is based on the document by Mark A. Smith available at + * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial + * and error. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/input-polldev.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/dmi.h> +#include <linux/jiffies.h> +#include <linux/io.h> + +#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ +#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ + +#define HDAPS_PORT_STATE 0x1611 /* device state */ +#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ +#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ +#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ +#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ +#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ +#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ +#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ +#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ + +#define STATE_FRESH 0x50 /* accelerometer data is fresh */ + +#define KEYBD_MASK 0x20 /* set if keyboard activity */ +#define MOUSE_MASK 0x40 /* set if mouse activity */ +#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ +#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ + +#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ +#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ + +#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/ +#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ +#define HDAPS_INPUT_FLAT 4 + +#define HDAPS_X_AXIS (1 << 0) +#define HDAPS_Y_AXIS (1 << 1) +#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) + +static struct platform_device *pdev; +static struct input_polled_dev *hdaps_idev; +static unsigned int hdaps_invert; +static u8 km_activity; +static int rest_x; +static int rest_y; + +static DEFINE_MUTEX(hdaps_mtx); + +/* + * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx. + */ +static inline u8 __get_latch(u16 port) +{ + return inb(port) & 0xff; +} + +/* + * __check_latch - Check a port latch for a given value. Returns zero if the + * port contains the given value. Callers must hold hdaps_mtx. + */ +static inline int __check_latch(u16 port, u8 val) +{ + if (__get_latch(port) == val) + return 0; + return -EINVAL; +} + +/* + * __wait_latch - Wait up to 100us for a port latch to get a certain value, + * returning zero if the value is obtained. Callers must hold hdaps_mtx. + */ +static int __wait_latch(u16 port, u8 val) +{ + unsigned int i; + + for (i = 0; i < 20; i++) { + if (!__check_latch(port, val)) + return 0; + udelay(5); + } + + return -EIO; +} + +/* + * __device_refresh - request a refresh from the accelerometer. Does not wait + * for refresh to complete. Callers must hold hdaps_mtx. + */ +static void __device_refresh(void) +{ + udelay(200); + if (inb(0x1604) != STATE_FRESH) { + outb(0x11, 0x1610); + outb(0x01, 0x161f); + } +} + +/* + * __device_refresh_sync - request a synchronous refresh from the + * accelerometer. We wait for the refresh to complete. Returns zero if + * successful and nonzero on error. Callers must hold hdaps_mtx. + */ +static int __device_refresh_sync(void) +{ + __device_refresh(); + return __wait_latch(0x1604, STATE_FRESH); +} + +/* + * __device_complete - indicate to the accelerometer that we are done reading + * data, and then initiate an async refresh. Callers must hold hdaps_mtx. + */ +static inline void __device_complete(void) +{ + inb(0x161f); + inb(0x1604); + __device_refresh(); +} + +/* + * hdaps_readb_one - reads a byte from a single I/O port, placing the value in + * the given pointer. Returns zero on success or a negative error on failure. + * Can sleep. + */ +static int hdaps_readb_one(unsigned int port, u8 *val) +{ + int ret; + + mutex_lock(&hdaps_mtx); + + /* do a sync refresh -- we need to be sure that we read fresh data */ + ret = __device_refresh_sync(); + if (ret) + goto out; + + *val = inb(port); + __device_complete(); + +out: + mutex_unlock(&hdaps_mtx); + return ret; +} + +/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ +static int __hdaps_read_pair(unsigned int port1, unsigned int port2, + int *x, int *y) +{ + /* do a sync refresh -- we need to be sure that we read fresh data */ + if (__device_refresh_sync()) + return -EIO; + + *y = inw(port2); + *x = inw(port1); + km_activity = inb(HDAPS_PORT_KMACT); + __device_complete(); + + /* hdaps_invert is a bitvector to negate the axes */ + if (hdaps_invert & HDAPS_X_AXIS) + *x = -*x; + if (hdaps_invert & HDAPS_Y_AXIS) + *y = -*y; + + return 0; +} + +/* + * hdaps_read_pair - reads the values from a pair of ports, placing the values + * in the given pointers. Returns zero on success. Can sleep. + */ +static int hdaps_read_pair(unsigned int port1, unsigned int port2, + int *val1, int *val2) +{ + int ret; + + mutex_lock(&hdaps_mtx); + ret = __hdaps_read_pair(port1, port2, val1, val2); + mutex_unlock(&hdaps_mtx); + + return ret; +} + +/* + * hdaps_device_init - initialize the accelerometer. Returns zero on success + * and negative error code on failure. Can sleep. + */ +static int hdaps_device_init(void) +{ + int total, ret = -ENXIO; + + mutex_lock(&hdaps_mtx); + + outb(0x13, 0x1610); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + + /* + * Most ThinkPads return 0x01. + * + * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops + * have "inverted" axises. + * + * The 0x02 value occurs when the chip has been previously initialized. + */ + if (__check_latch(0x1611, 0x03) && + __check_latch(0x1611, 0x02) && + __check_latch(0x1611, 0x01)) + goto out; + + printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n", + __get_latch(0x1611)); + + outb(0x17, 0x1610); + outb(0x81, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + if (__wait_latch(0x1612, 0x60)) + goto out; + if (__wait_latch(0x1613, 0x00)) + goto out; + outb(0x14, 0x1610); + outb(0x01, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + outb(0x10, 0x1610); + outb(0xc8, 0x1611); + outb(0x00, 0x1612); + outb(0x02, 0x1613); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__device_refresh_sync()) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + + /* we have done our dance, now let's wait for the applause */ + for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { + int x, y; + + /* a read of the device helps push it into action */ + __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); + if (!__wait_latch(0x1611, 0x02)) { + ret = 0; + break; + } + + msleep(INIT_WAIT_MSECS); + } + +out: + mutex_unlock(&hdaps_mtx); + return ret; +} + + +/* Device model stuff */ + +static int hdaps_probe(struct platform_device *dev) +{ + int ret; + + ret = hdaps_device_init(); + if (ret) + return ret; + + printk(KERN_INFO "hdaps: device successfully initialized.\n"); + return 0; +} + +static int hdaps_resume(struct platform_device *dev) +{ + return hdaps_device_init(); +} + +static struct platform_driver hdaps_driver = { + .probe = hdaps_probe, + .resume = hdaps_resume, + .driver = { + .name = "hdaps", + .owner = THIS_MODULE, + }, +}; + +/* + * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx. + */ +static void hdaps_calibrate(void) +{ + __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); +} + +static void hdaps_mousedev_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + int x, y; + + mutex_lock(&hdaps_mtx); + + if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) + goto out; + + input_report_abs(input_dev, ABS_X, x - rest_x); + input_report_abs(input_dev, ABS_Y, y - rest_y); + input_sync(input_dev); + +out: + mutex_unlock(&hdaps_mtx); +} + + +/* Sysfs Files */ + +static ssize_t hdaps_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_variance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_temp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_temp2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_keyboard_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); +} + +static ssize_t hdaps_mouse_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); +} + +static ssize_t hdaps_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); +} + +static ssize_t hdaps_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + mutex_lock(&hdaps_mtx); + hdaps_calibrate(); + mutex_unlock(&hdaps_mtx); + + return count; +} + +static ssize_t hdaps_invert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hdaps_invert); +} + +static ssize_t hdaps_invert_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int invert; + + if (sscanf(buf, "%d", &invert) != 1 || + invert < 0 || invert > HDAPS_BOTH_AXES) + return -EINVAL; + + hdaps_invert = invert; + hdaps_calibrate(); + + return count; +} + +static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); +static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); +static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); +static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); +static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); +static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); +static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); +static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); + +static struct attribute *hdaps_attributes[] = { + &dev_attr_position.attr, + &dev_attr_variance.attr, + &dev_attr_temp1.attr, + &dev_attr_temp2.attr, + &dev_attr_keyboard_activity.attr, + &dev_attr_mouse_activity.attr, + &dev_attr_calibrate.attr, + &dev_attr_invert.attr, + NULL, +}; + +static struct attribute_group hdaps_attribute_group = { + .attrs = hdaps_attributes, +}; + + +/* Module stuff */ + +/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ +static int __init hdaps_dmi_match(const struct dmi_system_id *id) +{ + printk(KERN_INFO "hdaps: %s detected.\n", id->ident); + return 1; +} + +/* hdaps_dmi_match_invert - found an inverted match. */ +static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) +{ + hdaps_invert = (unsigned long)id->driver_data; + printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n", + hdaps_invert); + return hdaps_dmi_match(id); +} + +#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ + .ident = vendor " " model, \ + .callback = hdaps_dmi_match_invert, \ + .driver_data = (void *)axes, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ + HDAPS_DMI_MATCH_INVERT(vendor, model, 0) + +/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match + "ThinkPad T42p", so the order of the entries matters. + If your ThinkPad is not recognized, please update to latest + BIOS. This is especially the case for some R52 ThinkPads. */ +static struct dmi_system_id __initdata hdaps_whitelist[] = { + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), + { .ident = NULL } +}; + +static int __init hdaps_init(void) +{ + struct input_dev *idev; + int ret; + + if (!dmi_check_system(hdaps_whitelist)) { + printk(KERN_WARNING "hdaps: supported laptop not found!\n"); + ret = -ENODEV; + goto out; + } + + if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { + ret = -ENXIO; + goto out; + } + + ret = platform_driver_register(&hdaps_driver); + if (ret) + goto out_region; + + pdev = platform_device_register_simple("hdaps", -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto out_driver; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); + if (ret) + goto out_device; + + hdaps_idev = input_allocate_polled_device(); + if (!hdaps_idev) { + ret = -ENOMEM; + goto out_group; + } + + hdaps_idev->poll = hdaps_mousedev_poll; + hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; + + /* initial calibrate for the input device */ + hdaps_calibrate(); + + /* initialize the input class */ + idev = hdaps_idev->input; + idev->name = "hdaps"; + idev->phys = "isa1600/input0"; + idev->id.bustype = BUS_ISA; + idev->dev.parent = &pdev->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(idev, ABS_X, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + + ret = input_register_polled_device(hdaps_idev); + if (ret) + goto out_idev; + + printk(KERN_INFO "hdaps: driver successfully loaded.\n"); + return 0; + +out_idev: + input_free_polled_device(hdaps_idev); +out_group: + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); +out_device: + platform_device_unregister(pdev); +out_driver: + platform_driver_unregister(&hdaps_driver); +out_region: + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); +out: + printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); + return ret; +} + +static void __exit hdaps_exit(void) +{ + input_unregister_polled_device(hdaps_idev); + input_free_polled_device(hdaps_idev); + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); + platform_device_unregister(pdev); + platform_driver_unregister(&hdaps_driver); + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); + + printk(KERN_INFO "hdaps: driver unloaded.\n"); +} + +module_init(hdaps_init); +module_exit(hdaps_exit); + +module_param_named(invert, hdaps_invert, int, 0); +MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " + "2 invert y-axis, 3 invert both axes."); + +MODULE_AUTHOR("Robert Love"); +MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c1741142a4c..1dac659b5e0 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/rfkill.h> @@ -88,24 +89,16 @@ 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 index 00000000000..3c2c6b91ecb --- /dev/null +++ b/drivers/platform/x86/ibm_rtl.c @@ -0,0 +1,341 @@ +/* + * IBM Real-Time Linux driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2010 + * + * Author: Keith Mannthey <kmannth@us.ibm.com> + * Vernon Mauery <vernux@us.ibm.com> + * + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/sysdev.h> +#include <linux/dmi.h> +#include <linux/mutex.h> +#include <asm/bios_ebda.h> + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Show debug output"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>"); +MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>"); + +#define RTL_ADDR_TYPE_IO 1 +#define RTL_ADDR_TYPE_MMIO 2 + +#define RTL_CMD_ENTER_PRTM 1 +#define RTL_CMD_EXIT_PRTM 2 + +/* The RTL table as presented by the EBDA: */ +struct ibm_rtl_table { + char signature[5]; /* signature should be "_RTL_" */ + u8 version; + u8 rt_status; + u8 command; + u8 command_status; + u8 cmd_address_type; + u8 cmd_granularity; + u8 cmd_offset; + u16 reserve1; + u32 cmd_port_address; /* platform dependent address */ + u32 cmd_port_value; /* platform dependent value */ +} __attribute__((packed)); + +/* to locate "_RTL_" signature do a masked 5-byte integer compare */ +#define RTL_SIGNATURE 0x0000005f4c54525fULL +#define RTL_MASK 0x000000ffffffffffULL + +#define RTL_DEBUG(A, ...) do { \ + if (debug) \ + pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ +} while (0) + +static DEFINE_MUTEX(rtl_lock); +static struct ibm_rtl_table __iomem *rtl_table; +static void __iomem *ebda_map; +static void __iomem *rtl_cmd_addr; +static u8 rtl_cmd_type; +static u8 rtl_cmd_width; + +static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) +{ + if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + return ioremap(addr, len); + return ioport_map(addr, len); +} + +static void rtl_port_unmap(void __iomem *addr) +{ + if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + iounmap(addr); + else + ioport_unmap(addr); +} + +static int ibm_rtl_write(u8 value) +{ + int ret = 0, count = 0; + static u32 cmd_port_val; + + RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); + + value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; + + mutex_lock(&rtl_lock); + + if (ioread8(&rtl_table->rt_status) != value) { + iowrite8(value, &rtl_table->command); + + switch (rtl_cmd_width) { + case 8: + cmd_port_val = ioread8(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite8((u8)cmd_port_val, rtl_cmd_addr); + break; + case 16: + cmd_port_val = ioread16(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite16((u16)cmd_port_val, rtl_cmd_addr); + break; + case 32: + cmd_port_val = ioread32(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite32(cmd_port_val, rtl_cmd_addr); + break; + } + + while (ioread8(&rtl_table->command)) { + msleep(10); + if (count++ > 500) { + pr_err("ibm-rtl: Hardware not responding to " + "mode switch request\n"); + ret = -EIO; + break; + } + + } + + if (ioread8(&rtl_table->command_status)) { + RTL_DEBUG("command_status reports failed command\n"); + ret = -EIO; + } + } + + mutex_unlock(&rtl_lock); + return ret; +} + +static ssize_t rtl_show_version(struct sysdev_class * dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); +} + +static ssize_t rtl_show_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); +} + +static ssize_t rtl_set_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + const char *buf, + size_t count) +{ + ssize_t ret; + + if (count < 1 || count > 2) + return -EINVAL; + + switch (buf[0]) { + case '0': + ret = ibm_rtl_write(0); + break; + case '1': + ret = ibm_rtl_write(1); + break; + default: + ret = -EINVAL; + } + if (ret >= 0) + ret = count; + + return ret; +} + +static struct sysdev_class class_rtl = { + .name = "ibm_rtl", +}; + +static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); +static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); + +static struct sysdev_class_attribute *rtl_attributes[] = { + &attr_version, + &attr_state, + NULL +}; + + +static int rtl_setup_sysfs(void) { + int ret, i; + ret = sysdev_class_register(&class_rtl); + + if (!ret) { + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_create_file(&class_rtl, rtl_attributes[i]); + } + return ret; +} + +static void rtl_teardown_sysfs(void) { + int i; + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); + sysdev_class_unregister(&class_rtl); +} + +static int dmi_check_cb(const struct dmi_system_id *id) +{ + RTL_DEBUG("found IBM server '%s'\n", id->ident); + return 0; +} + +#define ibm_dmi_entry(NAME, TYPE) \ +{ \ + .ident = NAME, \ + .matches = { \ + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ + }, \ + .callback = dmi_check_cb \ +} + +static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { + ibm_dmi_entry("BladeCenter LS21", "7971"), + ibm_dmi_entry("BladeCenter LS22", "7901"), + ibm_dmi_entry("BladeCenter HS21 XM", "7995"), + ibm_dmi_entry("BladeCenter HS22", "7870"), + ibm_dmi_entry("BladeCenter HS22V", "7871"), + ibm_dmi_entry("System x3550 M2", "7946"), + ibm_dmi_entry("System x3650 M2", "7947"), + ibm_dmi_entry("System x3550 M3", "7944"), + ibm_dmi_entry("System x3650 M3", "7945"), + { } +}; + +static int __init ibm_rtl_init(void) { + unsigned long ebda_addr, ebda_size; + unsigned int ebda_kb; + int ret = -ENODEV, i; + + if (force) + pr_warning("ibm-rtl: module loaded by force\n"); + /* first ensure that we are running on IBM HW */ + else if (!dmi_check_system(ibm_rtl_dmi_table)) + return -ENODEV; + + /* Get the address for the Extended BIOS Data Area */ + ebda_addr = get_bios_ebda(); + if (!ebda_addr) { + RTL_DEBUG("no BIOS EBDA found\n"); + return -ENODEV; + } + + ebda_map = ioremap(ebda_addr, 4); + if (!ebda_map) + return -ENOMEM; + + /* First word in the EDBA is the Size in KB */ + ebda_kb = ioread16(ebda_map); + RTL_DEBUG("EBDA is %d kB\n", ebda_kb); + + if (ebda_kb == 0) + goto out; + + iounmap(ebda_map); + ebda_size = ebda_kb*1024; + + /* Remap the whole table */ + ebda_map = ioremap(ebda_addr, ebda_size); + if (!ebda_map) + return -ENOMEM; + + /* search for the _RTL_ signature at the start of the table */ + for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { + struct ibm_rtl_table __iomem * tmp; + tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); + if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { + phys_addr_t addr; + unsigned int plen; + RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); + rtl_table = tmp; + /* The address, value, width and offset are platform + * dependent and found in the ibm_rtl_table */ + rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); + rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); + RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", + rtl_cmd_width, rtl_cmd_type); + addr = ioread32(&rtl_table->cmd_port_address); + RTL_DEBUG("addr = %#llx\n", addr); + plen = rtl_cmd_width/sizeof(char); + rtl_cmd_addr = rtl_port_map(addr, plen); + RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); + if (!rtl_cmd_addr) { + ret = -ENOMEM; + break; + } + ret = rtl_setup_sysfs(); + break; + } + } + +out: + if (ret) { + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } + + return ret; +} + +static void __exit ibm_rtl_exit(void) +{ + if (rtl_table) { + RTL_DEBUG("cleaning up"); + /* do not leave the machine in SMI-free mode */ + ibm_rtl_write(0); + /* unmap, unlink and remove all traces */ + rtl_teardown_sysfs(); + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } +} + +module_init(ibm_rtl_init); +module_exit(ibm_rtl_exit); diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad-laptop.c index 798496353e8..5ff12205aa6 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -35,112 +35,162 @@ #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", ¶ms, &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", ¶ms, 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 = { diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 5cdcff65391..f540ff96c53 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -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); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6abe18e638e..41a9e34899a 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -23,6 +23,7 @@ #include <linux/pm.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/sfi.h> #include <asm/mrst.h> #include <asm/intel_scu_ipc.h> diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ec01c3d8fc5..cc1e0ba104d 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -128,6 +128,7 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #ifndef ACPI_HOTKEY_COMPONENT @@ -200,30 +201,29 @@ 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); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index ff4b476f195..1d07d6d09f2 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/acpi.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #define ACPI_TOPSTAR_CLASS "topstar" @@ -26,52 +27,37 @@ struct 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); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 7d67a45bb2b..06f304f46e0 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -48,6 +48,7 @@ #include <linux/platform_device.h> #include <linux/rfkill.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/leds.h> #include <linux/slab.h> @@ -121,36 +122,28 @@ 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); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b2978a04317..104b77c87ef 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -27,6 +27,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -44,9 +46,8 @@ 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 index 00000000000..e549eeeda12 --- /dev/null +++ b/drivers/platform/x86/xo1-rfkill.c @@ -0,0 +1,85 @@ +/* + * Support for rfkill through the OLPC XO-1 laptop embedded controller + * + * Copyright (C) 2010 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rfkill.h> + +#include <asm/olpc.h> + +static int rfkill_set_block(void *data, bool blocked) +{ + unsigned char cmd; + if (blocked) + cmd = EC_WLAN_ENTER_RESET; + else + cmd = EC_WLAN_LEAVE_RESET; + + return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); +} + +static const struct rfkill_ops rfkill_ops = { + .set_block = rfkill_set_block, +}; + +static int __devinit xo1_rfkill_probe(struct platform_device *pdev) +{ + struct rfkill *rfk; + int r; + + rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN, + &rfkill_ops, NULL); + if (!rfk) + return -ENOMEM; + + r = rfkill_register(rfk); + if (r) { + rfkill_destroy(rfk); + return r; + } + + platform_set_drvdata(pdev, rfk); + return 0; +} + +static int __devexit xo1_rfkill_remove(struct platform_device *pdev) +{ + struct rfkill *rfk = platform_get_drvdata(pdev); + rfkill_unregister(rfk); + rfkill_destroy(rfk); + return 0; +} + +static struct platform_driver xo1_rfkill_driver = { + .driver = { + .name = "xo1-rfkill", + .owner = THIS_MODULE, + }, + .probe = xo1_rfkill_probe, + .remove = __devexit_p(xo1_rfkill_remove), +}; + +static int __init xo1_rfkill_init(void) +{ + return platform_driver_register(&xo1_rfkill_driver); +} + +static void __exit xo1_rfkill_exit(void) +{ + platform_driver_unregister(&xo1_rfkill_driver); +} + +MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:xo1-rfkill"); + +module_init(xo1_rfkill_init); +module_exit(xo1_rfkill_exit); |