diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 27 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 429 | ||||
-rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 19 | ||||
-rw-r--r-- | drivers/platform/x86/compal-laptop.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 11 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 609 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 259 | ||||
-rw-r--r-- | drivers/platform/x86/intel_ips.c | 36 | ||||
-rw-r--r-- | drivers/platform/x86/intel_ips.h | 21 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmic_gpio.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipcutil.c | 133 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 12 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 3 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 133 |
17 files changed, 1474 insertions, 244 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index faec777..d163bc2 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES config ACER_WMI tristate "Acer WMI Laptop Extras" depends on ACPI - depends on LEDS_CLASS - depends on NEW_LEDS + select LEDS_CLASS + select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 + depends on INPUT depends on RFKILL || RFKILL = n - select ACPI_WMI + depends on ACPI_WMI + select INPUT_SPARSEKMAP ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, @@ -131,7 +133,7 @@ config TC1100_WMI depends on !X86_64 depends on EXPERIMENTAL depends on ACPI - select ACPI_WMI + depends on ACPI_WMI ---help--- This is a driver for the WMI extensions (wireless and bluetooth power control) of the HP Compaq TC1100 tablet. @@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI depends on RFKILL + select INPUT_SPARSEKMAP help This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. @@ -425,7 +428,10 @@ config EEEPC_WMI depends on INPUT depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL || RFKILL = n select INPUT_SPARSEKMAP + select LEDS_CLASS + select NEW_LEDS ---help--- Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. @@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on ACPI - depends on LEDS_CLASS - depends on NEW_LEDS + select LEDS_CLASS + select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on INPUT depends on RFKILL || RFKILL = n @@ -576,6 +582,15 @@ config INTEL_SCU_IPC some embedded Intel x86 platforms. This is not needed for PC-type machines. +config INTEL_SCU_IPC_UTIL + tristate "Intel SCU IPC utility driver" + depends on INTEL_SCU_IPC + default y + ---help--- + The IPC Util driver provides an interface with the SCU enabling + low level access for debug work and updating the firmware. Say + N unless you will be doing this on an Intel MID platform. + config GPIO_INTEL_PMIC bool "Intel PMIC GPIO support" depends on INTEL_SCU_IPC && GPIOLIB diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9950ccc..4ec4ff8 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o +obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.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 diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1d0b707..c5c4b8c 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -37,6 +37,9 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/dmi.h> #include <acpi/acpi_drivers.h> @@ -48,6 +51,7 @@ MODULE_LICENSE("GPL"); #define ACER_ERR KERN_ERR ACER_LOGPREFIX #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX #define ACER_INFO KERN_INFO ACER_LOGPREFIX +#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX /* * Magic Number @@ -82,9 +86,82 @@ MODULE_LICENSE("GPL"); #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" +#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" + +/* + * Acer ACPI event GUIDs + */ +#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); +MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); + +enum acer_wmi_event_ids { + WMID_HOTKEY_EVENT = 0x1, +}; + +static const struct key_entry acer_wmi_keymap[] = { + {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ + {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ + {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ + {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ + {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ + {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ + {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ + {KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */ + {KE_END, 0} +}; + +static struct input_dev *acer_wmi_input_dev; + +struct event_return_value { + u8 function; + u8 key_num; + u16 device_state; + u32 reserved; +} __attribute__((packed)); + +/* + * GUID3 Get Device Status device flags + */ +#define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */ +#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ +#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ + +struct lm_input_params { + u8 function_num; /* Function Number */ + u16 commun_devices; /* Communication type devices default status */ + u16 devices; /* Other type devices default status */ + u8 lm_status; /* Launch Manager Status */ + u16 reserved; +} __attribute__((packed)); + +struct lm_return_value { + u8 error_code; /* Error Code */ + u8 ec_return_value; /* EC Return Value */ + u16 reserved; +} __attribute__((packed)); + +struct wmid3_gds_input_param { /* Get Device Status input parameter */ + u8 function_num; /* Function Number */ + u8 hotkey_number; /* Hotkey Number */ + u16 devices; /* Get Device */ +} __attribute__((packed)); + +struct wmid3_gds_return_value { /* Get Device Status return value*/ + u8 error_code; /* Error Code */ + u8 ec_return_value; /* EC Return Value */ + u16 devices; /* Current Device Status */ + u32 reserved; +} __attribute__((packed)); + +struct hotkey_function_type_aa { + u8 type; + u8 length; + u16 handle; + u16 commun_func_bitmap; +} __attribute__((packed)); /* * Interface capability flags @@ -116,15 +193,19 @@ static int mailled = -1; static int brightness = -1; static int threeg = -1; static int force_series; +static bool ec_raw_mode; +static bool has_type_aa; module_param(mailled, int, 0444); module_param(brightness, int, 0444); module_param(threeg, int, 0444); module_param(force_series, int, 0444); +module_param(ec_raw_mode, bool, 0444); MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); MODULE_PARM_DESC(force_series, "Force a different laptop series"); +MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); struct acer_data { int mailled; @@ -140,6 +221,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; +static struct rfkill *threeg_rfkill; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) return WMI_execute_u32(method_id, (u32)value, NULL); } +static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) +{ + struct hotkey_function_type_aa *type_aa; + + /* We are looking for OEM-specific Type AAh */ + if (header->type != 0xAA) + return; + + has_type_aa = true; + type_aa = (struct hotkey_function_type_aa *) header; + + printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n", + type_aa->commun_func_bitmap); + + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) + interface->capability |= ACER_CAP_WIRELESS; + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG) + interface->capability |= ACER_CAP_THREEG; + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) + interface->capability |= ACER_CAP_BLUETOOTH; +} + static acpi_status WMID_set_capabilities(void) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void) return AE_ERROR; } - /* Not sure on the meaning of the relevant bits yet to detect these */ - interface->capability |= ACER_CAP_WIRELESS; - interface->capability |= ACER_CAP_THREEG; + dmi_walk(type_aa_dmi_decode, NULL); + if (!has_type_aa) { + interface->capability |= ACER_CAP_WIRELESS; + interface->capability |= ACER_CAP_THREEG; + if (devices & 0x10) + interface->capability |= ACER_CAP_BLUETOOTH; + } /* WMID always provides brightness methods */ interface->capability |= ACER_CAP_BRIGHTNESS; - if (devices & 0x10) - interface->capability |= ACER_CAP_BLUETOOTH; - if (!(devices & 0x20)) max_brightness = 0x9; @@ -861,7 +966,8 @@ static void __init acer_commandline_init(void) * capability isn't available on the given interface */ set_u32(mailled, ACER_CAP_MAILLED); - set_u32(threeg, ACER_CAP_THREEG); + if (!has_type_aa) + set_u32(threeg, ACER_CAP_THREEG); set_u32(brightness, ACER_CAP_BRIGHTNESS); } @@ -948,6 +1054,79 @@ static void acer_backlight_exit(void) backlight_device_unregister(acer_backlight_device); } +static acpi_status wmid3_get_device_status(u32 *value, u16 device) +{ + struct wmid3_gds_return_value return_value; + acpi_status status; + union acpi_object *obj; + struct wmid3_gds_input_param params = { + .function_num = 0x1, + .hotkey_number = 0x01, + .devices = device, + }; + struct acpi_buffer input = { + sizeof(struct wmid3_gds_input_param), + ¶ms + }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 8) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); + kfree(obj); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Get Device Status failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + else + *value = !!(return_value.devices & device); + + return status; +} + +static acpi_status get_device_status(u32 *value, u32 cap) +{ + if (wmi_has_guid(WMID_GUID3)) { + u16 device; + + switch (cap) { + case ACER_CAP_WIRELESS: + device = ACER_WMID3_GDS_WIRELESS; + break; + case ACER_CAP_BLUETOOTH: + device = ACER_WMID3_GDS_BLUETOOTH; + break; + case ACER_CAP_THREEG: + device = ACER_WMID3_GDS_THREEG; + break; + default: + return AE_ERROR; + } + return wmid3_get_device_status(value, device); + + } else { + return get_u32(value, cap); + } +} + /* * Rfkill devices */ @@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored) rfkill_set_sw_state(bluetooth_rfkill, !state); } + if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { + status = wmid3_get_device_status(&state, + ACER_WMID3_GDS_THREEG); + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(threeg_rfkill, !state); + } + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); } @@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, { int err; struct rfkill *rfkill_dev; + u32 state; + acpi_status status; rfkill_dev = rfkill_alloc(name, dev, type, &acer_rfkill_ops, @@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev, if (!rfkill_dev) return ERR_PTR(-ENOMEM); + status = get_device_status(&state, cap); + if (ACPI_SUCCESS(status)) + rfkill_init_sw_state(rfkill_dev, !state); + err = rfkill_register(rfkill_dev); if (err) { rfkill_destroy(rfkill_dev); @@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *dev) } } + if (has_cap(ACER_CAP_THREEG)) { + threeg_rfkill = acer_rfkill_register(dev, + RFKILL_TYPE_WWAN, "acer-threeg", + ACER_CAP_THREEG); + if (IS_ERR(threeg_rfkill)) { + rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + return PTR_ERR(threeg_rfkill); + } + } + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; @@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void) rfkill_unregister(bluetooth_rfkill); rfkill_destroy(bluetooth_rfkill); } + + if (has_cap(ACER_CAP_THREEG)) { + rfkill_unregister(threeg_rfkill); + rfkill_destroy(threeg_rfkill); + } return; } @@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev, struct device_attribute *attr, char *buf) { u32 result; \ - acpi_status status = get_u32(&result, ACER_CAP_THREEG); + acpi_status status; + if (wmi_has_guid(WMID_GUID3)) + status = wmid3_get_device_status(&result, + ACER_WMID3_GDS_THREEG); + else + status = get_u32(&result, ACER_CAP_THREEG); if (ACPI_SUCCESS(status)) return sprintf(buf, "%u\n", result); return sprintf(buf, "Read error\n"); @@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); +static void acer_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct event_return_value return_value; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + printk(ACER_WARNING "bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + if (obj->type != ACPI_TYPE_BUFFER) { + printk(ACER_WARNING "Unknown response received %d\n", + obj->type); + kfree(obj); + return; + } + if (obj->buffer.length != 8) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return; + } + + return_value = *((struct event_return_value *)obj->buffer.pointer); + kfree(obj); + + switch (return_value.function) { + case WMID_HOTKEY_EVENT: + if (!sparse_keymap_report_event(acer_wmi_input_dev, + return_value.key_num, 1, true)) + printk(ACER_WARNING "Unknown key number - 0x%x\n", + return_value.key_num); + break; + default: + printk(ACER_WARNING "Unknown function number - %d - %d\n", + return_value.function, return_value.key_num); + break; + } +} + +static acpi_status +wmid3_set_lm_mode(struct lm_input_params *params, + struct lm_return_value *return_value) +{ + acpi_status status; + union acpi_object *obj; + + struct acpi_buffer input = { sizeof(struct lm_input_params), params }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 4) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + *return_value = *((struct lm_return_value *)obj->buffer.pointer); + kfree(obj); + + return status; +} + +static int acer_wmi_enable_ec_raw(void) +{ + struct lm_return_value return_value; + acpi_status status; + struct lm_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .lm_status = 0x00, /* Launch Manager Deactive */ + }; + + status = wmid3_set_lm_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Enabling EC raw mode failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + else + printk(ACER_INFO "Enabled EC raw mode"); + + return status; +} + +static int acer_wmi_enable_lm(void) +{ + struct lm_return_value return_value; + acpi_status status; + struct lm_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .lm_status = 0x01, /* Launch Manager Active */ + }; + + status = wmid3_set_lm_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Enabling Launch Manager failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + + return status; +} + +static int __init acer_wmi_input_setup(void) +{ + acpi_status status; + int err; + + acer_wmi_input_dev = input_allocate_device(); + if (!acer_wmi_input_dev) + return -ENOMEM; + + acer_wmi_input_dev->name = "Acer WMI hotkeys"; + acer_wmi_input_dev->phys = "wmi/input0"; + acer_wmi_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, + acer_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_keymap; + } + + err = input_register_device(acer_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + + return 0; + +err_uninstall_notifier: + wmi_remove_notify_handler(ACERWMID_EVENT_GUID); +err_free_keymap: + sparse_keymap_free(acer_wmi_input_dev); +err_free_dev: + input_free_device(acer_wmi_input_dev); + return err; +} + +static void acer_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(ACERWMID_EVENT_GUID); + sparse_keymap_free(acer_wmi_input_dev); + input_unregister_device(acer_wmi_input_dev); +} + /* * debugfs functions */ @@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void) "generic video driver\n"); } + if (wmi_has_guid(WMID_GUID3)) { + if (ec_raw_mode) { + if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { + printk(ACER_ERR "Cannot enable EC raw mode\n"); + return -ENODEV; + } + } else if (ACPI_FAILURE(acer_wmi_enable_lm())) { + printk(ACER_ERR "Cannot enable Launch Manager mode\n"); + return -ENODEV; + } + } else if (ec_raw_mode) { + printk(ACER_INFO "No WMID EC raw mode enable method\n"); + } + + if (wmi_has_guid(ACERWMID_EVENT_GUID)) { + err = acer_wmi_input_setup(); + if (err) + return err; + } + err = platform_driver_register(&acer_platform_driver); if (err) { printk(ACER_ERR "Unable to register platform driver.\n"); @@ -1368,11 +1775,17 @@ error_device_add: error_device_alloc: platform_driver_unregister(&acer_platform_driver); error_platform_register: + if (wmi_has_guid(ACERWMID_EVENT_GUID)) + acer_wmi_input_destroy(); + return err; } static void __exit acer_wmi_exit(void) { + if (wmi_has_guid(ACERWMID_EVENT_GUID)) + acer_wmi_input_destroy(); + remove_sysfs(acer_platform_device); remove_debugfs(); platform_device_unregister(acer_platform_device); diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 341cbfe..9111354 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked) acpi_status status; acpi_handle handle; unsigned long long state; + bool is_blocked; handle = data; status = cmpc_get_rfkill_wlan(handle, &state); if (ACPI_FAILURE(status)) return -ENODEV; - if (blocked) - state &= ~1; - else - state |= 1; - status = cmpc_set_rfkill_wlan(handle, state); - if (ACPI_FAILURE(status)) - return -ENODEV; + /* Check if we really need to call cmpc_set_rfkill_wlan */ + is_blocked = state & 1 ? false : true; + if (is_blocked != blocked) { + state = blocked ? 0 : 1; + status = cmpc_set_rfkill_wlan(handle, state); + if (ACPI_FAILURE(status)) + return -ENODEV; + } return 0; } @@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) code = cmpc_keys_codes[event & 0x0F]; - inputdev = dev_get_drvdata(&dev->dev);; + inputdev = dev_get_drvdata(&dev->dev); input_report_key(inputdev, code, !(event & 0x10)); + input_sync(inputdev); } static void cmpc_keys_idev_init(struct input_dev *inputdev) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 097083c..034572b 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }, .callback = dmi_check_cb_extra }, + { + .ident = "KHLB2", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "KHLB2"), + DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), + }, + .callback = dmi_check_cb_extra + }, { } }; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index c062a65..49d9ad7 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev, queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } +static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) +{ + struct eeepc_laptop *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); + + return get_acpi(eeepc, CM_ASL_TPD); +} + static int eeepc_led_init(struct eeepc_laptop *eeepc) { int rv; @@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) eeepc->tpd_led.name = "eeepc::touchpad"; eeepc->tpd_led.brightness_set = tpd_led_set; + if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ + eeepc->tpd_led.brightness_get = tpd_led_get; eeepc->tpd_led.max_brightness = 1; rv = led_classdev_register(&eeepc->platform_device->dev, diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d50fbb..4d38f98 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -2,6 +2,7 @@ * Eee PC WMI hotkey driver * * Copyright(C) 2010 Intel Corporation. + * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> @@ -34,6 +35,10 @@ #include <linux/input/sparse-keymap.h> #include <linux/fb.h> #include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/rfkill.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <linux/platform_device.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); MODULE_LICENSE("GPL"); +#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ + #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" @@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DEVID_WLAN 0x00010011 +#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 +#define EEEPC_WMI_DEVID_WWAN3G 0x00010019 static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ @@ -83,11 +94,37 @@ struct bios_args { u32 ctrl_param; }; +/* + * eeepc-wmi/ - debugfs root directory + * dev_id - current dev_id + * ctrl_param - current ctrl_param + * devs - call DEVS(dev_id, ctrl_param) and print result + * dsts - call DSTS(dev_id) and print result + */ +struct eeepc_wmi_debug { + struct dentry *root; + u32 dev_id; + u32 ctrl_param; +}; + struct eeepc_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; + struct platform_device *platform_device; + + struct led_classdev tpd_led; + int tpd_led_wk; + struct workqueue_struct *led_workqueue; + struct work_struct tpd_led_work; + + struct rfkill *wlan_rfkill; + struct rfkill *bluetooth_rfkill; + struct rfkill *wwan3g_rfkill; + + struct eeepc_wmi_debug debug; }; +/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ static struct platform_device *platform_device; static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) @@ -101,7 +138,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) eeepc->inputdev->name = "Eee PC WMI hotkeys"; eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; eeepc->inputdev->id.bustype = BUS_HOST; - eeepc->inputdev->dev.parent = &platform_device->dev; + eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); if (err) @@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) eeepc->inputdev = NULL; } -static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) +static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) { struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) else tmp = 0; - if (ctrl_param) - *ctrl_param = tmp; + if (retval) + *retval = tmp; kfree(obj); @@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) } -static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) +static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, + u32 *retval) { struct bios_args args = { .dev_id = dev_id, @@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; acpi_status status; - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); + if (!retval) { + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, + EEEPC_WMI_METHODID_DEVS, + &input, NULL); + } else { + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + u32 tmp; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, + EEEPC_WMI_METHODID_DEVS, + &input, &output); + + if (ACPI_FAILURE(status)) + return status; + + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32)obj->integer.value; + else + tmp = 0; + + *retval = tmp; + + kfree(obj); + } return status; } +/* + * LEDs + */ +/* + * These functions actually update the LED's, and are called from a + * workqueue. By doing this as separate work rather than when the LED + * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a + * potentially bad time, such as a timer interrupt. + */ +static void tpd_led_update(struct work_struct *work) +{ + int ctrl_param; + struct eeepc_wmi *eeepc; + + eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); + + ctrl_param = eeepc->tpd_led_wk; + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL); +} + +static void tpd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + + eeepc->tpd_led_wk = !!value; + queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); +} + +static int read_tpd_state(struct eeepc_wmi *eeepc) +{ + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); + + if (ACPI_FAILURE(status)) + return -1; + else if (!retval || retval == 0x00060000) + /* + * if touchpad led is present, DSTS will set some bits, + * usually 0x00020000. + * 0x00060000 means that the device is not supported + */ + return -ENODEV; + else + /* Status is stored in the first bit */ + return retval & 0x1; +} + +static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + + return read_tpd_state(eeepc); +} + +static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) +{ + int rv; + + if (read_tpd_state(eeepc) < 0) + return 0; + + eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!eeepc->led_workqueue) + return -ENOMEM; + INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); + + eeepc->tpd_led.name = "eeepc::touchpad"; + eeepc->tpd_led.brightness_set = tpd_led_set; + eeepc->tpd_led.brightness_get = tpd_led_get; + eeepc->tpd_led.max_brightness = 1; + + rv = led_classdev_register(&eeepc->platform_device->dev, + &eeepc->tpd_led); + if (rv) { + destroy_workqueue(eeepc->led_workqueue); + return rv; + } + + return 0; +} + +static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->tpd_led.dev) + led_classdev_unregister(&eeepc->tpd_led); + if (eeepc->led_workqueue) + destroy_workqueue(eeepc->led_workqueue); +} + +/* + * Rfkill devices + */ +static int eeepc_rfkill_set(void *data, bool blocked) +{ + int dev_id = (unsigned long)data; + u32 ctrl_param = !blocked; + + return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); +} + +static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) +{ + int dev_id = (unsigned long)data; + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &retval); + + if (ACPI_FAILURE(status)) + return ; + + rfkill_set_sw_state(rfkill, !(retval & 0x1)); +} + +static const struct rfkill_ops eeepc_rfkill_ops = { + .set_block = eeepc_rfkill_set, + .query = eeepc_rfkill_query, +}; + +static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, + struct rfkill **rfkill, + const char *name, + enum rfkill_type type, int dev_id) +{ + int result; + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &retval); + + if (ACPI_FAILURE(status)) + return -1; + + /* If the device is present, DSTS will always set some bits + * 0x00070000 - 1110000000000000000 - device supported + * 0x00060000 - 1100000000000000000 - not supported + * 0x00020000 - 0100000000000000000 - device supported + * 0x00010000 - 0010000000000000000 - not supported / special mode ? + */ + if (!retval || retval == 0x00060000) + return -ENODEV; + + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, (void *)(long)dev_id); + + if (!*rfkill) + return -EINVAL; + + rfkill_init_sw_state(*rfkill, !(retval & 0x1)); + result = rfkill_register(*rfkill); + if (result) { + rfkill_destroy(*rfkill); + *rfkill = NULL; + return result; + } + return 0; +} + +static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->wlan_rfkill) { + rfkill_unregister(eeepc->wlan_rfkill); + rfkill_destroy(eeepc->wlan_rfkill); + eeepc->wlan_rfkill = NULL; + } + if (eeepc->bluetooth_rfkill) { + rfkill_unregister(eeepc->bluetooth_rfkill); + rfkill_destroy(eeepc->bluetooth_rfkill); + eeepc->bluetooth_rfkill = NULL; + } + if (eeepc->wwan3g_rfkill) { + rfkill_unregister(eeepc->wwan3g_rfkill); + rfkill_destroy(eeepc->wwan3g_rfkill); + eeepc->wwan3g_rfkill = NULL; + } +} + +static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) +{ + int result = 0; + + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, + "eeepc-wlan", RFKILL_TYPE_WLAN, + EEEPC_WMI_DEVID_WLAN); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, + "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, + EEEPC_WMI_DEVID_BLUETOOTH); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, + "eeepc-wwan3g", RFKILL_TYPE_WWAN, + EEEPC_WMI_DEVID_WWAN3G); + + if (result && result != -ENODEV) + goto exit; + +exit: + if (result && result != -ENODEV) + eeepc_wmi_rfkill_exit(eeepc); + + if (result == -ENODEV) + result = 0; + + return result; +} + +/* + * Backlight + */ static int read_brightness(struct backlight_device *bd) { - static u32 ctrl_param; + u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); if (ACPI_FAILURE(status)) return -1; else - return ctrl_param & 0xFF; + return retval & 0xFF; } static int update_bl_status(struct backlight_device *bd) { - static u32 ctrl_param; + u32 ctrl_param; acpi_status status; ctrl_param = bd->props.brightness; - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); if (ACPI_FAILURE(status)) return -1; @@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = 15; bd = backlight_device_register(EEEPC_WMI_FILE, - &platform_device->dev, eeepc, + &eeepc->platform_device->dev, eeepc, &eeepc_wmi_bl_ops, &props); if (IS_ERR(bd)) { pr_err("Could not register backlight device\n"); @@ -321,65 +606,240 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); +static struct attribute *platform_attributes[] = { + &dev_attr_cpufv.attr, + NULL +}; + +static struct attribute_group platform_attribute_group = { + .attrs = platform_attributes +}; + static void eeepc_wmi_sysfs_exit(struct platform_device *device) { - device_remove_file(&device->dev, &dev_attr_cpufv); + sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); } static int eeepc_wmi_sysfs_init(struct platform_device *device) { - int retval = -ENOMEM; + return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); +} - retval = device_create_file(&device->dev, &dev_attr_cpufv); - if (retval) - goto error_sysfs; +/* + * Platform device + */ +static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) +{ + int err; + eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); + if (!eeepc->platform_device) + return -ENOMEM; + platform_set_drvdata(eeepc->platform_device, eeepc); + + err = platform_device_add(eeepc->platform_device); + if (err) + goto fail_platform_device; + + err = eeepc_wmi_sysfs_init(eeepc->platform_device); + if (err) + goto fail_sysfs; return 0; -error_sysfs: - eeepc_wmi_sysfs_exit(platform_device); - return retval; +fail_sysfs: + platform_device_del(eeepc->platform_device); +fail_platform_device: + platform_device_put(eeepc->platform_device); + return err; } -static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) +static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) { + eeepc_wmi_sysfs_exit(eeepc->platform_device); + platform_device_unregister(eeepc->platform_device); +} + +/* + * debugfs + */ +struct eeepc_wmi_debugfs_node { struct eeepc_wmi *eeepc; - int err; + char *name; + int (*show)(struct seq_file *m, void *data); +}; + +static int show_dsts(struct seq_file *m, void *data) +{ + struct eeepc_wmi *eeepc = m->private; acpi_status status; + u32 retval = -1; - eeepc = platform_get_drvdata(device); + status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + + seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); + + return 0; +} + +static int show_devs(struct seq_file *m, void *data) +{ + struct eeepc_wmi *eeepc = m->private; + acpi_status status; + u32 retval = -1; + + status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, + eeepc->debug.ctrl_param, &retval); + if (ACPI_FAILURE(status)) + return -EIO; + + seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, + eeepc->debug.ctrl_param, retval); + + return 0; +} + +static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { + { NULL, "devs", show_devs }, + { NULL, "dsts", show_dsts }, +}; + +static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) +{ + struct eeepc_wmi_debugfs_node *node = inode->i_private; + + return single_open(file, node->show, node->eeepc); +} + +static const struct file_operations eeepc_wmi_debugfs_io_ops = { + .owner = THIS_MODULE, + .open = eeepc_wmi_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) +{ + debugfs_remove_recursive(eeepc->debug.root); +} + +static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) +{ + struct dentry *dent; + int i; + + eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); + if (!eeepc->debug.root) { + pr_err("failed to create debugfs directory"); + goto error_debugfs; + } + + dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, + eeepc->debug.root, &eeepc->debug.dev_id); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, + eeepc->debug.root, &eeepc->debug.ctrl_param); + if (!dent) + goto error_debugfs; + + for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { + struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; + + node->eeepc = eeepc; + dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, + eeepc->debug.root, node, + &eeepc_wmi_debugfs_io_ops); + if (!dent) { + pr_err("failed to create debug file: %s\n", node->name); + goto error_debugfs; + } + } + + return 0; + +error_debugfs: + eeepc_wmi_debugfs_exit(eeepc); + return -ENOMEM; +} + +/* + * WMI Driver + */ +static struct platform_device * __init eeepc_wmi_add(void) +{ + struct eeepc_wmi *eeepc; + acpi_status status; + int err; + + eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); + if (!eeepc) + return ERR_PTR(-ENOMEM); + + /* + * Register the platform device first. It is used as a parent for the + * sub-devices below. + */ + err = eeepc_wmi_platform_init(eeepc); + if (err) + goto fail_platform; err = eeepc_wmi_input_init(eeepc); if (err) - goto error_input; + goto fail_input; + + err = eeepc_wmi_led_init(eeepc); + if (err) + goto fail_leds; + + err = eeepc_wmi_rfkill_init(eeepc); + if (err) + goto fail_rfkill; if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); if (err) - goto error_backlight; + goto fail_backlight; } else pr_info("Backlight controlled by ACPI video driver\n"); status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, - eeepc_wmi_notify, eeepc); + eeepc_wmi_notify, eeepc); if (ACPI_FAILURE(status)) { pr_err("Unable to register notify handler - %d\n", status); err = -ENODEV; - goto error_wmi; + goto fail_wmi_handler; } - return 0; + err = eeepc_wmi_debugfs_init(eeepc); + if (err) + goto fail_debugfs; -error_wmi: + return eeepc->platform_device; + +fail_debugfs: + wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); +fail_wmi_handler: eeepc_wmi_backlight_exit(eeepc); -error_backlight: +fail_backlight: + eeepc_wmi_rfkill_exit(eeepc); +fail_rfkill: + eeepc_wmi_led_exit(eeepc); +fail_leds: eeepc_wmi_input_exit(eeepc); -error_input: - return err; +fail_input: + eeepc_wmi_platform_exit(eeepc); +fail_platform: + kfree(eeepc); + return ERR_PTR(err); } -static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) +static int eeepc_wmi_remove(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_input_exit(eeepc); + eeepc_wmi_led_exit(eeepc); + eeepc_wmi_rfkill_exit(eeepc); + eeepc_wmi_debugfs_exit(eeepc); + eeepc_wmi_platform_exit(eeepc); + kfree(eeepc); return 0; } @@ -396,13 +861,31 @@ static struct platform_driver platform_driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, }, - .probe = eeepc_wmi_platform_probe, - .remove = __devexit_p(eeepc_wmi_platform_remove), }; +static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, + void *context, void **retval) +{ + pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); + *(bool *)context = true; + return AE_CTRL_TERMINATE; +} + +static int __init eeepc_wmi_check_atkd(void) +{ + acpi_status status; + bool found = false; + + status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, + &found, NULL); + + if (ACPI_FAILURE(status) || !found) + return 0; + return -1; +} + static int __init eeepc_wmi_init(void) { - struct eeepc_wmi *eeepc; int err; if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || @@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void) return -ENODEV; } - eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); - if (!eeepc) - return -ENOMEM; - - platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); - if (!platform_device) { - pr_warning("Unable to allocate platform device\n"); - err = -ENOMEM; - goto fail_platform; + if (eeepc_wmi_check_atkd()) { + pr_warning("WMI device present, but legacy ATKD device is also " + "present and enabled."); + pr_warning("You probably booted with acpi_osi=\"Linux\" or " + "acpi_osi=\"!Windows 2009\""); + pr_warning("Can't load eeepc-wmi, use default acpi_osi " + "(preferred) or eeepc-laptop"); + return -ENODEV; } - err = platform_device_add(platform_device); - if (err) { - pr_warning("Unable to add platform device\n"); - goto put_dev; + platform_device = eeepc_wmi_add(); + if (IS_ERR(platform_device)) { + err = PTR_ERR(platform_device); + goto fail_eeepc_wmi; } - platform_set_drvdata(platform_device, eeepc); - err = platform_driver_register(&platform_driver); if (err) { pr_warning("Unable to register platform driver\n"); - goto del_dev; + goto fail_platform_driver; } - 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: - platform_device_put(platform_device); -fail_platform: - kfree(eeepc); - +fail_platform_driver: + eeepc_wmi_remove(platform_device); +fail_eeepc_wmi: return err; } static void __exit eeepc_wmi_exit(void) { - struct eeepc_wmi *eeepc; - - eeepc_wmi_sysfs_exit(platform_device); - eeepc = platform_get_drvdata(platform_device); + eeepc_wmi_remove(platform_device); platform_driver_unregister(&platform_driver); - platform_device_unregister(platform_device); - kfree(eeepc); } module_init(eeepc_wmi_init); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 34d0dc5..19e92b2 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); -static struct pnp_device_id pnp_ids[] = { +static struct pnp_device_id pnp_ids[] __used = { {.id = "FUJ02bf"}, {.id = "FUJ02B1"}, {.id = "FUJ02E3"}, diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5ff1220..114d952 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1,5 +1,5 @@ /* - * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras + * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras * * Copyright © 2010 Intel Corporation * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> @@ -27,31 +27,19 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> -#define IDEAPAD_DEV_CAMERA 0 -#define IDEAPAD_DEV_WLAN 1 -#define IDEAPAD_DEV_BLUETOOTH 2 -#define IDEAPAD_DEV_3G 3 -#define IDEAPAD_DEV_KILLSW 4 +#define IDEAPAD_RFKILL_DEV_NUM (3) struct ideapad_private { - acpi_handle handle; - struct rfkill *rfk[5]; -} *ideapad_priv; - -static struct { - char *name; - int cfgbit; - int opcode; - int type; -} ideapad_rfk_data[] = { - { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, - { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, - { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } + struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; + struct platform_device *platform_device; + struct input_dev *inputdev; }; +static acpi_handle ideapad_handle; static bool no_bt_rfkill; module_param(no_bt_rfkill, bool, 0444); MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); @@ -163,17 +151,17 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) pr_err("timeout in write_ec_cmd\n"); return -1; } -/* the above is ACPI helpers */ +/* + * camera power + */ static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; unsigned long result; - if (read_ec_data(handle, 0x1D, &result)) + if (read_ec_data(ideapad_handle, 0x1D, &result)) return sprintf(buf, "-1\n"); return sprintf(buf, "%lu\n", result); } @@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; int ret, state; if (!count) return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = write_ec_cmd(handle, 0x1E, state); + ret = write_ec_cmd(ideapad_handle, 0x1E, state); if (ret < 0) return ret; return count; @@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev, static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); +/* + * Rfkill + */ +struct ideapad_rfk_data { + char *name; + int cfgbit; + int opcode; + int type; +}; + +const struct ideapad_rfk_data ideapad_rfk_data[] = { + { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, +}; + static int ideapad_rfk_set(void *data, bool blocked) { - int device = (unsigned long)data; + unsigned long opcode = (unsigned long)data; - if (device == IDEAPAD_DEV_KILLSW) - return -EINVAL; - - return write_ec_cmd(ideapad_priv->handle, - ideapad_rfk_data[device].opcode, - !blocked); + return write_ec_cmd(ideapad_handle, opcode, !blocked); } static struct rfkill_ops ideapad_rfk_ops = { @@ -217,20 +214,20 @@ 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); - acpi_handle handle = priv->handle; unsigned long hw_blocked; int i; - if (read_ec_data(handle, 0x23, &hw_blocked)) + if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) return; hw_blocked = !hw_blocked; - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) if (priv->rfk[i]) rfkill_set_hw_state(priv->rfk[i], hw_blocked); } -static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) +static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, + int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int ret; @@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) 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, + write_ec_cmd(ideapad_handle, ideapad_rfk_data[dev].opcode, 1); return 0; } @@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) if (!priv->rfk[dev]) return -ENOMEM; - if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, + if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, &sw_blocked)) { rfkill_init_sw_state(priv->rfk[dev], 0); } else { @@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) return 0; } -static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) +static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, + int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); @@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) rfkill_destroy(priv->rfk[dev]); } +/* + * Platform device + */ +static struct attribute *ideapad_attributes[] = { + &dev_attr_camera_power.attr, + NULL +}; + +static struct attribute_group ideapad_attribute_group = { + .attrs = ideapad_attributes +}; + +static int __devinit ideapad_platform_init(struct ideapad_private *priv) +{ + int result; + + priv->platform_device = platform_device_alloc("ideapad", -1); + if (!priv->platform_device) + return -ENOMEM; + platform_set_drvdata(priv->platform_device, priv); + + result = platform_device_add(priv->platform_device); + if (result) + goto fail_platform_device; + + result = sysfs_create_group(&priv->platform_device->dev.kobj, + &ideapad_attribute_group); + if (result) + goto fail_sysfs; + return 0; + +fail_sysfs: + platform_device_del(priv->platform_device); +fail_platform_device: + platform_device_put(priv->platform_device); + return result; +} + +static void ideapad_platform_exit(struct ideapad_private *priv) +{ + sysfs_remove_group(&priv->platform_device->dev.kobj, + &ideapad_attribute_group); + platform_device_unregister(priv->platform_device); +} + +/* + * input device + */ +static const struct key_entry ideapad_keymap[] = { + { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x0D, { KEY_WLAN } }, + { KE_END, 0 }, +}; + +static int __devinit ideapad_input_init(struct ideapad_private *priv) +{ + struct input_dev *inputdev; + int error; + + inputdev = input_allocate_device(); + if (!inputdev) { + pr_info("Unable to allocate input device\n"); + return -ENOMEM; + } + + inputdev->name = "Ideapad extra buttons"; + inputdev->phys = "ideapad/input0"; + inputdev->id.bustype = BUS_HOST; + inputdev->dev.parent = &priv->platform_device->dev; + + error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); + if (error) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; + } + + error = input_register_device(inputdev); + if (error) { + pr_err("Unable to register input device\n"); + goto err_free_keymap; + } + + priv->inputdev = inputdev; + return 0; + +err_free_keymap: + sparse_keymap_free(inputdev); +err_free_dev: + input_free_device(inputdev); + return error; +} + +static void __devexit ideapad_input_exit(struct ideapad_private *priv) +{ + sparse_keymap_free(priv->inputdev); + input_unregister_device(priv->inputdev); + priv->inputdev = NULL; +} + +static void ideapad_input_report(struct ideapad_private *priv, + unsigned long scancode) +{ + sparse_keymap_report_event(priv->inputdev, scancode, 1, true); +} + +/* + * module init/exit + */ static const struct acpi_device_id ideapad_device_ids[] = { { "VPC2004", 0}, { "", 0}, }; MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); -static int ideapad_acpi_add(struct acpi_device *adevice) +static int __devinit ideapad_acpi_add(struct acpi_device *adevice) { - int i, cfg; - int devs_present[5]; + int ret, i, cfg; struct ideapad_private *priv; if (read_method_int(adevice->handle, "_CFG", &cfg)) return -ENODEV; - for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) - devs_present[i] = 1; - else - devs_present[i] = 0; - } - - /* The hardware switch is always present */ - devs_present[IDEAPAD_DEV_KILLSW] = 1; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + dev_set_drvdata(&adevice->dev, priv); + ideapad_handle = adevice->handle; - if (devs_present[IDEAPAD_DEV_CAMERA]) { - int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); - if (ret) { - kfree(priv); - return ret; - } - } + ret = ideapad_platform_init(priv); + if (ret) + goto platform_failed; - 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; + ret = ideapad_input_init(priv); + if (ret) + goto input_failed; - ideapad_register_rfkill(adevice, i); + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { + if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) + ideapad_register_rfkill(adevice, i); + else + priv->rfk[i] = NULL; } ideapad_sync_rfk_state(adevice); + return 0; + +input_failed: + ideapad_platform_exit(priv); +platform_failed: + kfree(priv); + return ret; } -static int ideapad_acpi_remove(struct acpi_device *adevice, int type) +static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int i; - device_remove_file(&adevice->dev, &dev_attr_camera_power); - - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) ideapad_unregister_rfkill(adevice, i); - + ideapad_input_exit(priv); + ideapad_platform_exit(priv); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); + return 0; } static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) { + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); acpi_handle handle = adevice->handle; unsigned long vpc1, vpc2, vpc_bit; @@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); + else + ideapad_input_report(priv, vpc_bit); } } } @@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = { .owner = THIS_MODULE, }; - static int __init ideapad_acpi_module_init(void) { - acpi_bus_register_driver(&ideapad_acpi_driver); - - return 0; + return acpi_bus_register_driver(&ideapad_acpi_driver); } - static void __exit ideapad_acpi_module_exit(void) { acpi_bus_unregister_driver(&ideapad_acpi_driver); - } MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index c44a5e8..1294a39 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -75,6 +75,7 @@ #include <drm/i915_drm.h> #include <asm/msr.h> #include <asm/processor.h> +#include "intel_ips.h" #define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 @@ -245,6 +246,7 @@ #define thm_writel(off, val) writel((val), ips->regmap + (off)) static const int IPS_ADJUST_PERIOD = 5000; /* ms */ +static bool late_i915_load = false; /* For initial average collection */ static const int IPS_SAMPLE_PERIOD = 200; /* ms */ @@ -339,6 +341,9 @@ struct ips_driver { u64 orig_turbo_ratios; }; +static bool +ips_gpu_turbo_enabled(struct ips_driver *ips); + /** * ips_cpu_busy - is CPU busy? * @ips: IPS driver struct @@ -517,7 +522,7 @@ static void ips_disable_cpu_turbo(struct ips_driver *ips) */ static bool ips_gpu_busy(struct ips_driver *ips) { - if (!ips->gpu_turbo_enabled) + if (!ips_gpu_turbo_enabled(ips)) return false; return ips->gpu_busy(); @@ -532,7 +537,7 @@ static bool ips_gpu_busy(struct ips_driver *ips) */ static void ips_gpu_raise(struct ips_driver *ips) { - if (!ips->gpu_turbo_enabled) + if (!ips_gpu_turbo_enabled(ips)) return; if (!ips->gpu_raise()) @@ -549,7 +554,7 @@ static void ips_gpu_raise(struct ips_driver *ips) */ static void ips_gpu_lower(struct ips_driver *ips) { - if (!ips->gpu_turbo_enabled) + if (!ips_gpu_turbo_enabled(ips)) return; if (!ips->gpu_lower()) @@ -1454,6 +1459,31 @@ out_err: return false; } +static bool +ips_gpu_turbo_enabled(struct ips_driver *ips) +{ + if (!ips->gpu_busy && late_i915_load) { + if (ips_get_i915_syms(ips)) { + dev_info(&ips->dev->dev, + "i915 driver attached, reenabling gpu turbo\n"); + ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); + } + } + + return ips->gpu_turbo_enabled; +} + +void +ips_link_to_i915_driver(void) +{ + /* We can't cleanly get at the various ips_driver structs from + * this caller (the i915 driver), so just set a flag saying + * that it's time to try getting the symbols again. + */ + late_i915_load = true; +} +EXPORT_SYMBOL_GPL(ips_link_to_i915_driver); + static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h new file mode 100644 index 0000000..73299be --- /dev/null +++ b/drivers/platform/x86/intel_ips.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + */ + +void ips_link_to_i915_driver(void); diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index e61db9d..930e627 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) generic_handle_irq(pg->irq_base + gpio); } } - desc->chip->eoi(irq); + + if (desc->chip->irq_eoi) + desc->chip->irq_eoi(irq_get_irq_data(irq)); + else + dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq); } static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 41a9e34..1752ef0 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -26,6 +26,7 @@ #include <linux/sfi.h> #include <asm/mrst.h> #include <asm/intel_scu_ipc.h> +#include <asm/mrst.h> /* IPC defines the following message types */ #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ @@ -496,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); mutex_unlock(&ipclock); - return -1; + return -EIO; } mutex_unlock(&ipclock); return 0; @@ -641,7 +642,7 @@ update_end: if (status == IPC_FW_UPDATE_SUCCESS) return 0; - return -1; + return -EIO; } EXPORT_SYMBOL(intel_scu_ipc_fw_update); @@ -699,6 +700,9 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) iounmap(ipcdev.ipc_base); return -ENOMEM; } + + intel_scu_devices_create(); + return 0; } @@ -720,6 +724,7 @@ static void ipc_remove(struct pci_dev *pdev) iounmap(ipcdev.ipc_base); iounmap(ipcdev.i2c_base); ipcdev.pdev = NULL; + intel_scu_devices_destroy(); } static const struct pci_device_id pci_ids[] = { diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c new file mode 100644 index 0000000..ba3231d --- /dev/null +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -0,0 +1,133 @@ +/* + * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * + * (C) Copyright 2008-2010 Intel Corporation + * Author: Sreedhara DS (sreedhara.ds@intel.com) + * + * 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; version 2 + * of the License. + * + * This driver provides ioctl interfaces to call intel scu ipc driver api + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <asm/intel_scu_ipc.h> + +static u32 major; + +#define MAX_FW_SIZE 264192 + +/* ioctl commnds */ +#define INTE_SCU_IPC_REGISTER_READ 0 +#define INTE_SCU_IPC_REGISTER_WRITE 1 +#define INTE_SCU_IPC_REGISTER_UPDATE 2 +#define INTE_SCU_IPC_FW_UPDATE 0xA2 + +struct scu_ipc_data { + u32 count; /* No. of registers */ + u16 addr[5]; /* Register addresses */ + u8 data[5]; /* Register data */ + u8 mask; /* Valid for read-modify-write */ +}; + +/** + * scu_reg_access - implement register access ioctls + * @cmd: command we are doing (read/write/update) + * @data: kernel copy of ioctl data + * + * Allow the user to perform register accesses on the SCU via the + * kernel interface + */ + +static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) +{ + int count = data->count; + + if (count == 0 || count == 3 || count > 4) + return -EINVAL; + + switch (cmd) { + case INTE_SCU_IPC_REGISTER_READ: + return intel_scu_ipc_readv(data->addr, data->data, count); + case INTE_SCU_IPC_REGISTER_WRITE: + return intel_scu_ipc_writev(data->addr, data->data, count); + case INTE_SCU_IPC_REGISTER_UPDATE: + return intel_scu_ipc_update_register(data->addr[0], + data->data[0], data->mask); + default: + return -ENOTTY; + } +} + +/** + * scu_ipc_ioctl - control ioctls for the SCU + * @fp: file handle of the SCU device + * @cmd: ioctl coce + * @arg: pointer to user passed structure + * + * Support the I/O and firmware flashing interfaces of the SCU + */ +static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct scu_ipc_data data; + void __user *argp = (void __user *)arg; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (cmd == INTE_SCU_IPC_FW_UPDATE) { + u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); + if (fwbuf == NULL) + return -ENOMEM; + if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { + kfree(fwbuf); + return -EFAULT; + } + ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE); + kfree(fwbuf); + return ret; + } else { + if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) + return -EFAULT; + ret = scu_reg_access(cmd, &data); + if (ret < 0) + return ret; + if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) + return -EFAULT; + return 0; + } +} + +static const struct file_operations scu_ipc_fops = { + .unlocked_ioctl = scu_ipc_ioctl, +}; + +static int __init ipc_module_init(void) +{ + return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); +} + +static void __exit ipc_module_exit(void) +{ + unregister_chrdev(major, "intel_mid_scu"); +} + +module_init(ipc_module_init); +module_exit(ipc_module_exit); + +MODULE_LICENSE("GPL V2"); +MODULE_DESCRIPTION("Utility driver for intel scu ipc"); +MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>"); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f8b2fc9..5e83370 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = { 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ + 59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = { KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ + KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = { { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, + { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, + { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, + { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa6, SONYPI_EVENT_HELP_PRESSED }, + { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0, 0 }, }; @@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, return err; } -static void sony_nc_rfkill_update() +static void sony_nc_rfkill_update(void) { enum sony_nc_rfkill i; int result; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index ecd503f..dd59958 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle, default: printk(TPACPI_ERR "acpi_evalf() called " "with invalid format character '%c'\n", c); + va_end(ap); return 0; } } @@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "as change notification\n"); tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | TP_ACPI_HKEY_BRGHTUP_MASK - | TP_ACPI_HKEY_BRGHTDWN_MASK);; + | TP_ACPI_HKEY_BRGHTDWN_MASK); return 0; } diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aecd9a9..05cc796 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data) { struct wmi_block *block; - acpi_status status; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid || !handler) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (block->handler && block->handler != wmi_notify_debug) - return AE_ALREADY_ACQUIRED; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - block->handler = handler; - block->handler_data = data; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (block->handler && + block->handler != wmi_notify_debug) + return AE_ALREADY_ACQUIRED; - status = wmi_method_enable(block, 1); + block->handler = handler; + block->handler_data = data; + + wmi_status = wmi_method_enable(block, 1); + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) + status = wmi_status; + } + } return status; } @@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); acpi_status wmi_remove_notify_handler(const char *guid) { struct wmi_block *block; - acpi_status status = AE_OK; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (!block->handler || block->handler == wmi_notify_debug) - return AE_NULL_ENTRY; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - if (debug_event) { - block->handler = wmi_notify_debug; - } else { - status = wmi_method_enable(block, 0); - block->handler = NULL; - block->handler_data = NULL; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (!block->handler || + block->handler == wmi_notify_debug) + return AE_NULL_ENTRY; + + if (debug_event) { + block->handler = wmi_notify_debug; + status = AE_OK; + } else { + wmi_status = wmi_method_enable(block, 0); + block->handler = NULL; + block->handler_data = NULL; + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && + (status == AE_NOT_EXIST))) + status = wmi_status; + } + } } + return status; } EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); @@ -705,22 +734,11 @@ static struct class wmi_class = { .dev_attrs = wmi_dev_attrs, }; -static struct wmi_block *wmi_create_device(const struct guid_block *gblock, - acpi_handle handle) +static int wmi_create_device(const struct guid_block *gblock, + struct wmi_block *wblock, acpi_handle handle) { - struct wmi_block *wblock; - int error; char guid_string[37]; - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - error = -ENOMEM; - goto err_out; - } - - wblock->handle = handle; - wblock->gblock = *gblock; - wblock->dev.class = &wmi_class; wmi_gtoa(gblock->guid, guid_string); @@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, dev_set_drvdata(&wblock->dev, wblock); - error = device_register(&wblock->dev); - if (error) - goto err_free; - - list_add_tail(&wblock->list, &wmi_block_list); - return wblock; - -err_free: - kfree(wblock); -err_out: - return ERR_PTR(error); + return device_register(&wblock->dev); } static void wmi_free_devices(void) @@ -747,7 +755,8 @@ static void wmi_free_devices(void) /* Delete devices for all the GUIDs */ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) - device_unregister(&wblock->dev); + if (wblock->dev.class) + device_unregister(&wblock->dev); } static bool guid_already_parsed(const char *guid_string) @@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) union acpi_object *obj; const struct guid_block *gblock; struct wmi_block *wblock; - char guid_string[37]; acpi_status status; int retval; u32 i, total; @@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle) total = obj->buffer.length / sizeof(struct guid_block); for (i = 0; i < total; i++) { + if (debug_dump_wdg) + wmi_dump_wdg(&gblock[i]); + + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) + return AE_NO_MEMORY; + + wblock->handle = handle; + wblock->gblock = gblock[i]; + /* Some WMI devices, like those for nVidia hooks, have a duplicate GUID. It's not clear what we should do in this - case yet, so for now, we'll just ignore the duplicate. - Anyone who wants to add support for that device can come - up with a better workaround for the mess then. + case yet, so for now, we'll just ignore the duplicate + for device creation. */ - if (guid_already_parsed(gblock[i].guid) == true) { - wmi_gtoa(gblock[i].guid, guid_string); - pr_info("Skipping duplicate GUID %s\n", guid_string); - continue; + if (!guid_already_parsed(gblock[i].guid)) { + retval = wmi_create_device(&gblock[i], wblock, handle); + if (retval) { + wmi_free_devices(); + goto out_free_pointer; + } } - if (debug_dump_wdg) - wmi_dump_wdg(&gblock[i]); - - wblock = wmi_create_device(&gblock[i], handle); - if (IS_ERR(wblock)) { - retval = PTR_ERR(wblock); - wmi_free_devices(); - break; - } + list_add_tail(&wblock->list, &wmi_block_list); if (debug_event) { wblock->handler = wmi_notify_debug; |