From c01cc7df046b5d5777bbc193c0d5bb3e0246f5db Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 16 Feb 2011 08:01:49 -0500 Subject: hwmon: (jc42) fix type mismatch In set_temp_crit_hyst(), make the variable 'val' have the correct type for strict_strtoul(). Signed-off-by: Clemens Ladisch Cc: stable@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 340fc78c8dde..5efe2399e8f8 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -332,7 +332,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct jc42_data *data = i2c_get_clientdata(client); - long val; + unsigned long val; int diff, hyst; int err; int ret = count; -- cgit v1.2.3 From 3c9caab58f9132b1d88750c39211c620030d9363 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 16 Feb 2011 08:02:08 -0500 Subject: hwmon: (jc42) more helpful documentation The documentation lists standard numbers and chip names in excruciating detail, but that's all it does. To help mere mortals in deciding whether to enable this driver, mention what this sensor is for and in which systems it might be found. Also add a link to the actual JC 42.4 specification. Signed-off-by: Clemens Ladisch Cc: stable@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/jc42 | 9 +++++++-- drivers/hwmon/Kconfig | 11 ++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index 0e76ef12e4c6..2a0924003f92 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -51,7 +51,8 @@ Supported chips: * JEDEC JC 42.4 compliant temperature sensor chips Prefix: 'jc42' Addresses scanned: I2C 0x18 - 0x1f - Datasheet: - + Datasheet: + http://www.jedec.org/sites/default/files/docs/4_01_04R19.pdf Author: Guenter Roeck @@ -60,7 +61,11 @@ Author: Description ----------- -This driver implements support for JEDEC JC 42.4 compliant temperature sensors. +This driver implements support for JEDEC JC 42.4 compliant temperature sensors, +which are used on many DDR3 memory modules for mobile devices and servers. Some +systems use the sensor to prevent memory overheating by automatically throttling +the memory controller. + The driver auto-detects the chips listed above, but can be manually instantiated to support other JC 42.4 compliant chips. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 773e484f1646..412339451a32 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -455,13 +455,14 @@ config SENSORS_JZ4740 called jz4740-hwmon. config SENSORS_JC42 - tristate "JEDEC JC42.4 compliant temperature sensors" + tristate "JEDEC JC42.4 compliant memory module temperature sensors" depends on I2C help - If you say yes here you get support for Jedec JC42.4 compliant - temperature sensors. Support will include, but not be limited to, - ADT7408, CAT34TS02,, CAT6095, MAX6604, MCP9805, MCP98242, MCP98243, - MCP9843, SE97, SE98, STTS424, TSE2002B3, and TS3000B3. + If you say yes here, you get support for JEDEC JC42.4 compliant + temperature sensors, which are used on many DDR3 memory modules for + mobile devices and servers. Support will include, but not be limited + to, ADT7408, CAT34TS02, CAT6095, MAX6604, MCP9805, MCP98242, MCP98243, + MCP9843, SE97, SE98, STTS424(E), TSE2002B3, and TS3000B3. This driver can also be built as a module. If so, the module will be called jc42. -- cgit v1.2.3 From 3247246c25907f3ba4707383d394a732b3e3a45b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 16 Feb 2011 08:02:38 -0500 Subject: hwmon: (jc42) do not allow writing to locked registers On systems where the temperature sensor is actually used, the BIOS is likely to have locked the alarm registers. In that case, all writes through the corresponding sysfs files would be silently ignored. To prevent this, detect the locks and make the affected sysfs files read-only. Signed-off-by: Clemens Ladisch Cc: stable@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/jc42 | 12 ++++++++---- drivers/hwmon/jc42.c | 33 +++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index 2a0924003f92..a22ecf48f255 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -86,15 +86,19 @@ limits. The chip supports only a single register to configure the hysteresis, which applies to all limits. This register can be written by writing into temp1_crit_hyst. Other hysteresis attributes are read-only. +If the BIOS has configured the sensor for automatic temperature management, it +is likely that it has locked the registers, i.e., that the temperature limits +cannot be changed. + Sysfs entries ------------- temp1_input Temperature (RO) -temp1_min Minimum temperature (RW) -temp1_max Maximum temperature (RW) -temp1_crit Critical high temperature (RW) +temp1_min Minimum temperature (RO or RW) +temp1_max Maximum temperature (RO or RW) +temp1_crit Critical high temperature (RO or RW) -temp1_crit_hyst Critical hysteresis temperature (RW) +temp1_crit_hyst Critical hysteresis temperature (RO or RW) temp1_max_hyst Maximum hysteresis temperature (RO) temp1_min_alarm Temperature low alarm diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 5efe2399e8f8..934991237061 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -53,6 +53,8 @@ static const unsigned short normal_i2c[] = { /* Configuration register defines */ #define JC42_CFG_CRIT_ONLY (1 << 2) +#define JC42_CFG_TCRIT_LOCK (1 << 6) +#define JC42_CFG_EVENT_LOCK (1 << 7) #define JC42_CFG_SHUTDOWN (1 << 8) #define JC42_CFG_HYST_SHIFT 9 #define JC42_CFG_HYST_MASK 0x03 @@ -380,14 +382,14 @@ static ssize_t show_alarm(struct device *dev, static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, set_temp_crit); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, set_temp_min); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, set_temp_max); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, set_temp_crit_hyst); static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_max_hyst, NULL); @@ -412,8 +414,31 @@ static struct attribute *jc42_attributes[] = { NULL }; +static mode_t jc42_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct jc42_data *data = i2c_get_clientdata(client); + unsigned int config = data->config; + bool readonly; + + if (attr == &dev_attr_temp1_crit.attr) + readonly = config & JC42_CFG_TCRIT_LOCK; + else if (attr == &dev_attr_temp1_min.attr || + attr == &dev_attr_temp1_max.attr) + readonly = config & JC42_CFG_EVENT_LOCK; + else if (attr == &dev_attr_temp1_crit_hyst.attr) + readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK); + else + readonly = true; + + return S_IRUGO | (readonly ? 0 : S_IWUSR); +} + static const struct attribute_group jc42_group = { .attrs = jc42_attributes, + .is_visible = jc42_attribute_mode, }; /* Return 0 if detection is successful, -ENODEV otherwise */ -- cgit v1.2.3 From a01759f3e816aacfe7f61a1975652142fdb56488 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 17 Feb 2011 03:22:40 -0500 Subject: hwmon: (k10temp) add support for AMD Family 12h/14h CPUs Add the PCI ID to support the internal temperature sensor of the AMD "Llano" and "Brazos" processor families. Signed-off-by: Clemens Ladisch Cc: stable@kernel.org # ca86828: x86, AMD, PCI: Add AMD northbridge PCI device Cc: stable@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/k10temp | 8 +++++++- drivers/hwmon/Kconfig | 6 +++--- drivers/hwmon/k10temp.c | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp index 6526eee525a6..d2b56a4fd1f5 100644 --- a/Documentation/hwmon/k10temp +++ b/Documentation/hwmon/k10temp @@ -9,6 +9,8 @@ Supported chips: Socket S1G3: Athlon II, Sempron, Turion II * AMD Family 11h processors: Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra) +* AMD Family 12h processors: "Llano" +* AMD Family 14h processors: "Brazos" (C/E/G-Series) Prefix: 'k10temp' Addresses scanned: PCI space @@ -17,10 +19,14 @@ Supported chips: http://support.amd.com/us/Processor_TechDocs/31116.pdf BIOS and Kernel Developer's Guide (BKDG) for AMD Family 11h Processors: http://support.amd.com/us/Processor_TechDocs/41256.pdf + BIOS and Kernel Developer's Guide (BKDG) for AMD Family 14h Models 00h-0Fh Processors: + http://support.amd.com/us/Processor_TechDocs/43170.pdf Revision Guide for AMD Family 10h Processors: http://support.amd.com/us/Processor_TechDocs/41322.pdf Revision Guide for AMD Family 11h Processors: http://support.amd.com/us/Processor_TechDocs/41788.pdf + Revision Guide for AMD Family 14h Models 00h-0Fh Processors: + http://support.amd.com/us/Processor_TechDocs/47534.pdf AMD Family 11h Processor Power and Thermal Data Sheet for Notebooks: http://support.amd.com/us/Processor_TechDocs/43373.pdf AMD Family 10h Server and Workstation Processor Power and Thermal Data Sheet: @@ -34,7 +40,7 @@ Description ----------- This driver permits reading of the internal temperature sensor of AMD -Family 10h and 11h processors. +Family 10h/11h/12h/14h processors. All these processors have a sensor, but on those for Socket F or AM2+, the sensor may return inconsistent values (erratum 319). The driver diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 412339451a32..5eadb007e542 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -238,13 +238,13 @@ config SENSORS_K8TEMP will be called k8temp. config SENSORS_K10TEMP - tristate "AMD Phenom/Sempron/Turion/Opteron temperature sensor" + tristate "AMD Family 10h/11h/12h/14h temperature sensor" depends on X86 && PCI help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of - the AMD Family 10h and all revisions of the AMD Family 11h - microarchitectures. + the AMD Family 10h and all revisions of the AMD Family 11h, + 12h (Llano), and 14h (Brazos) microarchitectures. This driver can also be built as a module. If so, the module will be called k10temp. diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index da5a2404cd3e..82bf65aa2968 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,5 +1,5 @@ /* - * k10temp.c - AMD Family 10h/11h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch * @@ -25,7 +25,7 @@ #include #include -MODULE_DESCRIPTION("AMD Family 10h/11h CPU core temperature monitor"); +MODULE_DESCRIPTION("AMD Family 10h/11h/12h/14h CPU core temperature monitor"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL"); @@ -208,6 +208,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev) static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); -- cgit v1.2.3 From 3c5ccd9ad3a7b66aa40e336eb5e021dc4c4a9e14 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 18 Feb 2011 03:18:26 -0500 Subject: hwmon: (lm85) extend to support EMC6D103 chips The interface is identical EMC6D102, so all that needs to be added are some definitions and their uses. Registers apparently missing in EMC6D103S/EMC6D103:A2 compared to EMC6D103:A0, EMC6D103:A1, and EMC6D102 (according to the data sheets), but used unconditionally in the driver: 62[5:7], 6D[0:7], and 6E[0:7]. For that reason, EMC6D103S chips don't get enabled for the time being. Signed-off-by: Jan Beulich (Guenter Roeck: Replaced EMC6D103_A2 with EMC6D103S per EMC6D103S datasheet) Signed-off-by: Guenter Roeck Cc: stable@kernel.org --- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm85.c | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5eadb007e542..297bc9a7d6e6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -575,7 +575,7 @@ config SENSORS_LM85 help If you say yes here you get support for National Semiconductor LM85 sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100, - EMC6D101 and EMC6D102. + EMC6D101, EMC6D102, and EMC6D103. This driver can also be built as a module. If so, the module will be called lm85. diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 1e229847f37a..d2cc28660816 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -41,7 +41,7 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { any_chip, lm85b, lm85c, adm1027, adt7463, adt7468, - emc6d100, emc6d102 + emc6d100, emc6d102, emc6d103 }; /* The LM85 registers */ @@ -90,6 +90,9 @@ enum chips { #define LM85_VERSTEP_EMC6D100_A0 0x60 #define LM85_VERSTEP_EMC6D100_A1 0x61 #define LM85_VERSTEP_EMC6D102 0x65 +#define LM85_VERSTEP_EMC6D103_A0 0x68 +#define LM85_VERSTEP_EMC6D103_A1 0x69 +#define LM85_VERSTEP_EMC6D103S 0x6A /* Also known as EMC6D103:A2 */ #define LM85_REG_CONFIG 0x40 @@ -348,6 +351,7 @@ static const struct i2c_device_id lm85_id[] = { { "emc6d100", emc6d100 }, { "emc6d101", emc6d100 }, { "emc6d102", emc6d102 }, + { "emc6d103", emc6d103 }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); @@ -1250,6 +1254,20 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) case LM85_VERSTEP_EMC6D102: type_name = "emc6d102"; break; + case LM85_VERSTEP_EMC6D103_A0: + case LM85_VERSTEP_EMC6D103_A1: + type_name = "emc6d103"; + break; + /* + * Registers apparently missing in EMC6D103S/EMC6D103:A2 + * compared to EMC6D103:A0, EMC6D103:A1, and EMC6D102 + * (according to the data sheets), but used unconditionally + * in the driver: 62[5:7], 6D[0:7], and 6E[0:7]. + * So skip EMC6D103S for now. + case LM85_VERSTEP_EMC6D103S: + type_name = "emc6d103s"; + break; + */ } } else { dev_dbg(&adapter->dev, @@ -1283,6 +1301,7 @@ static int lm85_probe(struct i2c_client *client, case adt7468: case emc6d100: case emc6d102: + case emc6d103: data->freq_map = adm1027_freq_map; break; default: @@ -1468,7 +1487,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) /* More alarm bits */ data->alarms |= lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; - } else if (data->type == emc6d102) { + } else if (data->type == emc6d102 || data->type == emc6d103) { /* Have to read LSB bits after the MSB ones because the reading of the MSB bits has frozen the LSBs (backward from the ADM1027). -- cgit v1.2.3 From 90d7e902a041234399b726b5a95a3317aa49c2cc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 20 Jan 2011 12:48:36 -0800 Subject: platform/x86: ideapad-laptop depends on INPUT Most platform/x86 drivers that use INPUT_SPARSEKMAP also depend on INPUT, so do the same for ideapad-laptop. This fixes a kconfig warning and subsequent build errors when CONFIG_INPUT is disabled. warning: (ACER_WMI && ASUS_LAPTOP && DELL_WMI && HP_WMI && PANASONIC_LAPTOP && IDEAPAD_LAPTOP && EEEPC_LAPTOP && EEEPC_WMI && MSI_WMI && TOPSTAR_LAPTOP && ACPI_TOSHIBA) selects INPUT_SPARSEKMAP which has unmet direct dependencies (!S390 && INPUT) ERROR: "input_free_device" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "input_register_device" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "sparse_keymap_setup" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "input_allocate_device" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "input_unregister_device" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "sparse_keymap_free" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "sparse_keymap_report_event" [drivers/platform/x86/ideapad-laptop.ko] undefined! Signed-off-by: Randy Dunlap Cc: David Woodhouse Cc: Matthew Garrett Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index d163bc2e2b9e..a59af5b24f0a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -227,7 +227,7 @@ config SONYPI_COMPAT config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI - depends on RFKILL + depends on RFKILL && INPUT select INPUT_SPARSEKMAP help This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. -- cgit v1.2.3 From 276af0fb1519559688d07661539fd719d7096440 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Thu, 3 Feb 2011 16:27:55 +0000 Subject: OLPC XO-1.5 ebook switch driver The OLPC XO-1.5 has an ebook switch, triggered when the laptop screen is rotated then folding down, converting the device into ebook form. This switch is exposed through ACPI. Add a driver that exposes it to userspace as an input device. Signed-off-by: Daniel Drake Acked-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 9 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/xo15-ebook.c | 181 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 drivers/platform/x86/xo15-ebook.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a59af5b24f0a..211b48d695ed 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -654,4 +654,13 @@ config XO1_RFKILL Support for enabling/disabling the WLAN interface on the OLPC XO-1 laptop. +config XO15_EBOOK + tristate "OLPC XO-1.5 ebook switch" + depends on ACPI && INPUT + ---help--- + Support for the ebook switch on the OLPC XO-1.5 laptop. + + This switch is triggered as the screen is rotated and folded down to + convert the device into ebook form. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 4ec4ff8f9182..7452ffcf840e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -33,4 +33,5 @@ obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o +obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c new file mode 100644 index 000000000000..2343bb36e563 --- /dev/null +++ b/drivers/platform/x86/xo15-ebook.c @@ -0,0 +1,181 @@ +/* + * OLPC XO-1.5 ebook switch driver + * (based on generic ACPI button driver) + * + * Copyright (C) 2009 Paul Fox + * 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 +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "xo15-ebook" +#define PREFIX MODULE_NAME ": " + +#define XO15_EBOOK_CLASS MODULE_NAME +#define XO15_EBOOK_TYPE_UNKNOWN 0x00 +#define XO15_EBOOK_NOTIFY_STATUS 0x80 + +#define XO15_EBOOK_SUBCLASS "ebook" +#define XO15_EBOOK_HID "XO15EBK" +#define XO15_EBOOK_DEVICE_NAME "EBook Switch" + +ACPI_MODULE_NAME(MODULE_NAME); + +MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); +MODULE_LICENSE("GPL"); + +static const struct acpi_device_id ebook_device_ids[] = { + { XO15_EBOOK_HID, 0 }, + { "", 0 }, +}; +MODULE_DEVICE_TABLE(acpi, ebook_device_ids); + +struct ebook_switch { + struct input_dev *input; + char phys[32]; /* for input device */ +}; + +static int ebook_send_state(struct acpi_device *device) +{ + struct ebook_switch *button = acpi_driver_data(device); + unsigned long long state; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); + if (ACPI_FAILURE(status)) + return -EIO; + + /* input layer checks if event is redundant */ + input_report_switch(button->input, SW_TABLET_MODE, !state); + input_sync(button->input); + return 0; +} + +static void ebook_switch_notify(struct acpi_device *device, u32 event) +{ + switch (event) { + case ACPI_FIXED_HARDWARE_EVENT: + case XO15_EBOOK_NOTIFY_STATUS: + ebook_send_state(device); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } +} + +static int ebook_switch_resume(struct acpi_device *device) +{ + return ebook_send_state(device); +} + +static int ebook_switch_add(struct acpi_device *device) +{ + struct ebook_switch *button; + struct input_dev *input; + const char *hid = acpi_device_hid(device); + char *name, *class; + int error; + + button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_button; + } + + name = acpi_device_name(device); + class = acpi_device_class(device); + + if (strcmp(hid, XO15_EBOOK_HID)) { + printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); + error = -ENODEV; + goto err_free_input; + } + + strcpy(name, XO15_EBOOK_DEVICE_NAME); + sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); + + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + + input->name = name; + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->dev.parent = &device->dev; + + input->evbit[0] = BIT_MASK(EV_SW); + set_bit(SW_TABLET_MODE, input->swbit); + + error = input_register_device(input); + if (error) + goto err_free_input; + + ebook_send_state(device); + + if (device->wakeup.flags.valid) { + /* Button's GPE is run-wake GPE */ + acpi_enable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + device->wakeup.run_wake_count++; + device_set_wakeup_enable(&device->dev, true); + } + + return 0; + + err_free_input: + input_free_device(input); + err_free_button: + kfree(button); + return error; +} + +static int ebook_switch_remove(struct acpi_device *device, int type) +{ + struct ebook_switch *button = acpi_driver_data(device); + + input_unregister_device(button->input); + kfree(button); + return 0; +} + +static struct acpi_driver xo15_ebook_driver = { + .name = MODULE_NAME, + .class = XO15_EBOOK_CLASS, + .ids = ebook_device_ids, + .ops = { + .add = ebook_switch_add, + .resume = ebook_switch_resume, + .remove = ebook_switch_remove, + .notify = ebook_switch_notify, + }, +}; + +static int __init xo15_ebook_init(void) +{ + return acpi_bus_register_driver(&xo15_ebook_driver); +} + +static void __exit xo15_ebook_exit(void) +{ + acpi_bus_unregister_driver(&xo15_ebook_driver); +} + +module_init(xo15_ebook_init); +module_exit(xo15_ebook_exit); -- cgit v1.2.3 From 0f8d4d6c6cc709d3f5328d9184898f0a46f94c7d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:26 +0100 Subject: eeepc-wmi: reorder keymap Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 4d38f98aa976..350b7bcfc326 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -74,18 +74,18 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ - { KE_KEY, 0x5d, { KEY_WLAN } }, - { KE_KEY, 0x32, { KEY_MUTE } }, - { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, - { KE_KEY, 0x30, { KEY_VOLUMEUP } }, { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x5c, { KEY_F15 } }, + { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, - { KE_KEY, 0x5c, { KEY_F15 } }, { KE_END, 0}, }; -- cgit v1.2.3 From 3e9153bad40cb40c8f91d9d2915ee28240c9200e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:27 +0100 Subject: eeepc-wmi: add wlan key found on 1015P Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 350b7bcfc326..0d8217d14780 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -82,6 +82,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x5c, { KEY_F15 } }, { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, -- cgit v1.2.3 From 2f1f8bf24c7923e69c8bf033afa6b03ade70b7b3 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:28 +0100 Subject: eeepc-wmi: add hotplug code for Eeepc 1000H Implement wireless like hotplug handling (code stolen from eeepc-laptop). Reminder: on some models rfkill is implemented by logically unplugging the wireless card from the PCI bus. Despite sending ACPI notifications, this does not appear to be implemented using standard ACPI hotplug - nor does the firmware provide the _OSC method required to support native PCIe hotplug. The only sensible choice appears to be to handle the hotplugging directly in the platform driver. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 274 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 273 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d8217d14780..01bc2b3da98a 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -37,9 +37,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -72,6 +75,14 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 +static bool hotplug_wireless; + +module_param(hotplug_wireless, bool, 0444); +MODULE_PARM_DESC(hotplug_wireless, + "Enable hotplug for wireless device. " + "If your laptop needs that, please report to " + "acpi4asus-user@lists.sourceforge.net."); + static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, @@ -109,6 +120,8 @@ struct eeepc_wmi_debug { }; struct eeepc_wmi { + bool hotplug_wireless; + struct input_dev *inputdev; struct backlight_device *backlight_device; struct platform_device *platform_device; @@ -122,6 +135,9 @@ struct eeepc_wmi { struct rfkill *bluetooth_rfkill; struct rfkill *wwan3g_rfkill; + struct hotplug_slot *hotplug_slot; + struct mutex hotplug_lock; + struct eeepc_wmi_debug debug; }; @@ -177,7 +193,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) u32 tmp; status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_DSTS, &input, &output); + 1, EEEPC_WMI_METHODID_DSTS, + &input, &output); if (ACPI_FAILURE(status)) return status; @@ -333,6 +350,206 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) destroy_workqueue(eeepc->led_workqueue); } +/* + * PCI hotplug (for wlan rfkill) + */ +static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) +{ + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); + + if (ACPI_FAILURE(status)) + return false; + + return !(retval & 0x1); +} + +static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) +{ + struct pci_dev *dev; + struct pci_bus *bus; + bool blocked = eeepc_wlan_rfkill_blocked(eeepc); + bool absent; + u32 l; + + if (eeepc->wlan_rfkill) + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + + mutex_lock(&eeepc->hotplug_lock); + + if (eeepc->hotplug_slot) { + bus = pci_find_bus(0, 1); + if (!bus) { + pr_warning("Unable to find PCI bus 1?\n"); + goto out_unlock; + } + + if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { + pr_err("Unable to read PCI config space?\n"); + goto out_unlock; + } + absent = (l == 0xffffffff); + + if (blocked != absent) { + pr_warning("BIOS says wireless lan is %s, " + "but the pci device is %s\n", + blocked ? "blocked" : "unblocked", + absent ? "absent" : "present"); + pr_warning("skipped wireless hotplug as probably " + "inappropriate for this model\n"); + goto out_unlock; + } + + if (!blocked) { + dev = pci_get_slot(bus, 0); + if (dev) { + /* Device already present */ + pci_dev_put(dev); + goto out_unlock; + } + dev = pci_scan_single_device(bus, 0); + if (dev) { + pci_bus_assign_resources(bus); + if (pci_bus_add_device(dev)) + pr_err("Unable to hotplug wifi\n"); + } + } else { + dev = pci_get_slot(bus, 0); + if (dev) { + pci_remove_bus_device(dev); + pci_dev_put(dev); + } + } + } + +out_unlock: + mutex_unlock(&eeepc->hotplug_lock); +} + +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +{ + struct eeepc_wmi *eeepc = data; + + if (event != ACPI_NOTIFY_BUS_CHECK) + return; + + eeepc_rfkill_hotplug(eeepc); +} + +static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, + char *node) +{ + acpi_status status; + acpi_handle handle; + + status = acpi_get_handle(NULL, node, &handle); + + if (ACPI_SUCCESS(status)) { + status = acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify, + eeepc); + if (ACPI_FAILURE(status)) + pr_warning("Failed to register notify on %s\n", node); + } else + return -ENODEV; + + return 0; +} + +static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, + char *node) +{ + acpi_status status = AE_OK; + acpi_handle handle; + + status = acpi_get_handle(NULL, node, &handle); + + if (ACPI_SUCCESS(status)) { + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify); + if (ACPI_FAILURE(status)) + pr_err("Error removing rfkill notify handler %s\n", + node); + } +} + +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, + u8 *value) +{ + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (!retval || retval == 0x00060000) + return -ENODEV; + else + *value = (retval & 0x1); + + return 0; +} + +static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) +{ + kfree(hotplug_slot->info); + kfree(hotplug_slot); +} + +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { + .owner = THIS_MODULE, + .get_adapter_status = eeepc_get_adapter_status, + .get_power_status = eeepc_get_adapter_status, +}; + +static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) +{ + int ret = -ENOMEM; + struct pci_bus *bus = pci_find_bus(0, 1); + + if (!bus) { + pr_err("Unable to find wifi PCI bus\n"); + return -ENODEV; + } + + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!eeepc->hotplug_slot) + goto error_slot; + + eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), + GFP_KERNEL); + if (!eeepc->hotplug_slot->info) + goto error_info; + + eeepc->hotplug_slot->private = eeepc; + eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; + eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; + eeepc_get_adapter_status(eeepc->hotplug_slot, + &eeepc->hotplug_slot->info->adapter_status); + + ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); + if (ret) { + pr_err("Unable to register hotplug slot - %d\n", ret); + goto error_register; + } + + return 0; + +error_register: + kfree(eeepc->hotplug_slot->info); +error_info: + kfree(eeepc->hotplug_slot); + eeepc->hotplug_slot = NULL; +error_slot: + return ret; +} + /* * Rfkill devices */ @@ -404,11 +621,22 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) { + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); if (eeepc->wlan_rfkill) { rfkill_unregister(eeepc->wlan_rfkill); rfkill_destroy(eeepc->wlan_rfkill); eeepc->wlan_rfkill = NULL; } + /* + * Refresh pci hotplug in case the rfkill state was changed after + * eeepc_unregister_rfkill_notifier() + */ + eeepc_rfkill_hotplug(eeepc); + if (eeepc->hotplug_slot) + pci_hp_deregister(eeepc->hotplug_slot); + if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); rfkill_destroy(eeepc->bluetooth_rfkill); @@ -425,6 +653,8 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) { int result = 0; + mutex_init(&eeepc->hotplug_lock); + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, "eeepc-wlan", RFKILL_TYPE_WLAN, EEEPC_WMI_DEVID_WLAN); @@ -446,6 +676,23 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + result = eeepc_setup_pci_hotplug(eeepc); + /* + * If we get -EBUSY then something else is handling the PCI hotplug - + * don't fail in this case + */ + if (result == -EBUSY) + result = 0; + + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); + /* + * Refresh pci hotplug in case the rfkill state was changed during + * setup. + */ + eeepc_rfkill_hotplug(eeepc); + exit: if (result && result != -ENODEV) eeepc_wmi_rfkill_exit(eeepc); @@ -771,6 +1018,28 @@ error_debugfs: /* * WMI Driver */ +static void eeepc_dmi_check(struct eeepc_wmi *eeepc) +{ + const char *model; + + model = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!model) + return; + + /* + * Whitelist for wlan hotplug + * + * Eeepc 1000H needs the current hotplug code to handle + * Fn+F2 correctly. We may add other Eeepc here later, but + * it seems that most of the laptops supported by eeepc-wmi + * don't need to be on this list + */ + if (strcmp(model, "1000H") == 0) { + eeepc->hotplug_wireless = true; + pr_info("wlan hotplug enabled\n"); + } +} + static struct platform_device * __init eeepc_wmi_add(void) { struct eeepc_wmi *eeepc; @@ -781,6 +1050,9 @@ static struct platform_device * __init eeepc_wmi_add(void) if (!eeepc) return ERR_PTR(-ENOMEM); + eeepc->hotplug_wireless = hotplug_wireless; + eeepc_dmi_check(eeepc); + /* * Register the platform device first. It is used as a parent for the * sub-devices below. -- cgit v1.2.3 From b7787083b1e44149d13843d299a3041ffbef2a7a Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:29 +0100 Subject: eeepc-wmi: serialize access to wmi method \AMW0.WMBC, which is the main method that we use, is not reentrant. When wireless hotpluging is enabled, toggling the status of the wireless device using WMBC will trigger a notification and the notification handler need to call WMBC again to get the new status of the device, this will trigger the following error: ACPI Error (dswload-0802): [_T_0] Namespace lookup failure, AE_ALREADY_EXISTS ACPI Exception: AE_ALREADY_EXISTS, During name lookup/catalog (20100428/psloop-231) ACPI Error (psparse-0537): Method parse/execution failed [\AMW0.WMBC] (Node f7023b88), AE_ALREADY_EXISTS ACPI: Marking method WMBC as Serialized because of AE_ALREADY_EXISTS error Since there is currently no way to tell the acpi subsystem to mark a method as serialized, we do it in eeepc-wmi. Of course, we could let the first call fail, and then it would work, but it doesn't seems really clean, and it will make the first WMBC call return a random value. This patch was tested on EeePc 1000H with a RaLink RT2860 wireless card using the rt2800pci driver. rt2860sta driver seems to deadlock when we remove the pci device... Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 78 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 01bc2b3da98a..eb4c0ce88ac1 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -137,6 +137,9 @@ struct eeepc_wmi { struct hotplug_slot *hotplug_slot; struct mutex hotplug_lock; + struct mutex wmi_lock; + struct workqueue_struct *hotplug_workqueue; + struct work_struct hotplug_work; struct eeepc_wmi_debug debug; }; @@ -370,15 +373,19 @@ static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) { struct pci_dev *dev; struct pci_bus *bus; - bool blocked = eeepc_wlan_rfkill_blocked(eeepc); + bool blocked; bool absent; u32 l; - if (eeepc->wlan_rfkill) - rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + mutex_lock(&eeepc->wmi_lock); + blocked = eeepc_wlan_rfkill_blocked(eeepc); + mutex_unlock(&eeepc->wmi_lock); mutex_lock(&eeepc->hotplug_lock); + if (eeepc->wlan_rfkill) + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + if (eeepc->hotplug_slot) { bus = pci_find_bus(0, 1); if (!bus) { @@ -435,7 +442,14 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) if (event != ACPI_NOTIFY_BUS_CHECK) return; - eeepc_rfkill_hotplug(eeepc); + /* + * We can't call directly eeepc_rfkill_hotplug because most + * of the time WMBC is still being executed and not reetrant. + * There is currently no way to tell ACPICA that we want this + * method to be serialized, we schedule a eeepc_rfkill_hotplug + * call later, in a safer context. + */ + queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work); } static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, @@ -508,6 +522,14 @@ static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { .get_power_status = eeepc_get_adapter_status, }; +static void eeepc_hotplug_work(struct work_struct *work) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(work, struct eeepc_wmi, hotplug_work); + eeepc_rfkill_hotplug(eeepc); +} + static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) { int ret = -ENOMEM; @@ -518,6 +540,13 @@ static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) return -ENODEV; } + eeepc->hotplug_workqueue = + create_singlethread_workqueue("hotplug_workqueue"); + if (!eeepc->hotplug_workqueue) + goto error_workqueue; + + INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work); + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); if (!eeepc->hotplug_slot) goto error_slot; @@ -547,6 +576,8 @@ error_info: kfree(eeepc->hotplug_slot); eeepc->hotplug_slot = NULL; error_slot: + destroy_workqueue(eeepc->hotplug_workqueue); +error_workqueue: return ret; } @@ -575,6 +606,34 @@ static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) rfkill_set_sw_state(rfkill, !(retval & 0x1)); } +static int eeepc_rfkill_wlan_set(void *data, bool blocked) +{ + struct eeepc_wmi *eeepc = data; + int ret; + + /* + * This handler is enabled only if hotplug is enabled. + * In this case, the eeepc_wmi_set_devstate() will + * trigger a wmi notification and we need to wait + * this call to finish before being able to call + * any wmi method + */ + mutex_lock(&eeepc->wmi_lock); + ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked); + mutex_unlock(&eeepc->wmi_lock); + return ret; +} + +static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data) +{ + eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN); +} + +static const struct rfkill_ops eeepc_rfkill_wlan_ops = { + .set_block = eeepc_rfkill_wlan_set, + .query = eeepc_rfkill_wlan_query, +}; + static const struct rfkill_ops eeepc_rfkill_ops = { .set_block = eeepc_rfkill_set, .query = eeepc_rfkill_query, @@ -603,8 +662,12 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, if (!retval || retval == 0x00060000) return -ENODEV; - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, - &eeepc_rfkill_ops, (void *)(long)dev_id); + if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_wlan_ops, eeepc); + else + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, (void *)(long)dev_id); if (!*rfkill) return -EINVAL; @@ -636,6 +699,8 @@ static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) eeepc_rfkill_hotplug(eeepc); if (eeepc->hotplug_slot) pci_hp_deregister(eeepc->hotplug_slot); + if (eeepc->hotplug_workqueue) + destroy_workqueue(eeepc->hotplug_workqueue); if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); @@ -654,6 +719,7 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) int result = 0; mutex_init(&eeepc->hotplug_lock); + mutex_init(&eeepc->wmi_lock); result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, "eeepc-wlan", RFKILL_TYPE_WLAN, -- cgit v1.2.3 From 08225efe82699da973911bd42113ecfeb686f465 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:30 +0100 Subject: eeepc-wmi: return proper error code in eeepc_rfkill_set() Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index eb4c0ce88ac1..d8234582b541 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -588,8 +588,14 @@ static int eeepc_rfkill_set(void *data, bool blocked) { int dev_id = (unsigned long)data; u32 ctrl_param = !blocked; + acpi_status status; + + status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; - return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); + return 0; } static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) -- cgit v1.2.3 From 9a1668d12f7da03884522d576cc9a7f4860f7f5a Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:31 +0100 Subject: eeepc-wmi: add an helper using simple return codes eeepc_wmi_get_devstate returns an acpi_status, so each call need extra logic to handle the return code. This patch add a simple getter, returning a boolean (or a negative error code). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 96 ++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index d8234582b541..de501fb3d1bb 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -257,6 +257,29 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, return status; } +/* Helper for special devices with magic return codes */ +static int eeepc_wmi_get_devstate_simple(u32 dev_id) +{ + u32 retval = 0; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &retval); + + if (ACPI_FAILURE(status)) + return -EINVAL; + + /* 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; + + return retval & 0x1; +} + /* * LEDs */ @@ -290,23 +313,7 @@ static void tpd_led_set(struct led_classdev *led_cdev, 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; + return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TPDLED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -358,15 +365,11 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) */ static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) { - u32 retval; - acpi_status status; - - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); - if (ACPI_FAILURE(status)) + if (result < 0) return false; - - return !(retval & 0x1); + return !result; } static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) @@ -494,19 +497,12 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - u32 retval; - acpi_status status; - - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); - - if (ACPI_FAILURE(status)) - return -EIO; + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); - if (!retval || retval == 0x00060000) - return -ENODEV; - else - *value = (retval & 0x1); + if (result < 0) + return result; + *value = !!result; return 0; } @@ -601,15 +597,14 @@ static int eeepc_rfkill_set(void *data, bool blocked) static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) { int dev_id = (unsigned long)data; - u32 retval; - acpi_status status; + int result; - status = eeepc_wmi_get_devstate(dev_id, &retval); + result = eeepc_wmi_get_devstate_simple(dev_id); - if (ACPI_FAILURE(status)) + if (result < 0) return ; - rfkill_set_sw_state(rfkill, !(retval & 0x1)); + rfkill_set_sw_state(rfkill, !result); } static int eeepc_rfkill_wlan_set(void *data, bool blocked) @@ -650,23 +645,10 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, 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); + int result = eeepc_wmi_get_devstate_simple(dev_id); - 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; + if (result < 0) + return result; if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, @@ -678,7 +660,7 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, if (!*rfkill) return -EINVAL; - rfkill_init_sw_state(*rfkill, !(retval & 0x1)); + rfkill_init_sw_state(*rfkill, !result); result = rfkill_register(*rfkill); if (result) { rfkill_destroy(*rfkill); -- cgit v1.2.3 From 7779d675648efbc5531229fe0bfd22c2c250522d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:32 +0100 Subject: eeepc-wmi: add hibernate/resume callbacks Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index de501fb3d1bb..583ba78a7f7d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -1183,10 +1183,59 @@ static int eeepc_wmi_remove(struct platform_device *device) return 0; } +/* + * Platform driver - hibernate/resume callbacks + */ +static int eeepc_hotk_thaw(struct device *device) +{ + struct eeepc_wmi *eeepc = dev_get_drvdata(device); + + if (eeepc->wlan_rfkill) { + bool wlan; + + /* + * Work around bios bug - acpi _PTS turns off the wireless led + * during suspend. Normally it restores it on resume, but + * we should kick it ourselves in case hibernation is aborted. + */ + wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL); + } + + return 0; +} + +static int eeepc_hotk_restore(struct device *device) +{ + struct eeepc_wmi *eeepc = dev_get_drvdata(device); + int bl; + + /* Refresh both wlan rfkill state and pci hotplug */ + if (eeepc->wlan_rfkill) + eeepc_rfkill_hotplug(eeepc); + + if (eeepc->bluetooth_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); + rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); +} + if (eeepc->wwan3g_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); + rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); + } + + return 0; +} + +static const struct dev_pm_ops eeepc_pm_ops = { + .thaw = eeepc_hotk_thaw, + .restore = eeepc_hotk_restore, +}; + static struct platform_driver platform_driver = { .driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, + .pm = &eeepc_pm_ops, }, }; -- cgit v1.2.3 From adf7894d154db0e15368cf246553e4c85e8297ba Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:33 +0100 Subject: eeepc-wmi: switch to platform_create_bundle() This allow to remove ~30 lines of code. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 75 ++++++++++++---------------------------- 1 file changed, 22 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 583ba78a7f7d..1fc191bc0a3f 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -144,9 +144,6 @@ struct eeepc_wmi { 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) { int err; @@ -932,33 +929,12 @@ static int eeepc_wmi_sysfs_init(struct platform_device *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; - -fail_sysfs: - platform_device_del(eeepc->platform_device); -fail_platform_device: - platform_device_put(eeepc->platform_device); - return err; + return eeepc_wmi_sysfs_init(eeepc->platform_device); } static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) { eeepc_wmi_sysfs_exit(eeepc->platform_device); - platform_device_unregister(eeepc->platform_device); } /* @@ -1094,7 +1070,7 @@ static void eeepc_dmi_check(struct eeepc_wmi *eeepc) } } -static struct platform_device * __init eeepc_wmi_add(void) +static int __init eeepc_wmi_add(struct platform_device *pdev) { struct eeepc_wmi *eeepc; acpi_status status; @@ -1102,15 +1078,14 @@ static struct platform_device * __init eeepc_wmi_add(void) eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); if (!eeepc) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + + eeepc->platform_device = pdev; + platform_set_drvdata(eeepc->platform_device, eeepc); eeepc->hotplug_wireless = hotplug_wireless; eeepc_dmi_check(eeepc); - /* - * 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; @@ -1147,7 +1122,7 @@ static struct platform_device * __init eeepc_wmi_add(void) if (err) goto fail_debugfs; - return eeepc->platform_device; + return 0; fail_debugfs: wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); @@ -1163,10 +1138,10 @@ fail_input: eeepc_wmi_platform_exit(eeepc); fail_platform: kfree(eeepc); - return ERR_PTR(err); + return err; } -static int eeepc_wmi_remove(struct platform_device *device) +static int __exit eeepc_wmi_remove(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -1232,6 +1207,7 @@ static const struct dev_pm_ops eeepc_pm_ops = { }; static struct platform_driver platform_driver = { + .remove = __exit_p(eeepc_wmi_remove), .driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, @@ -1260,10 +1236,8 @@ static int __init eeepc_wmi_check_atkd(void) return -1; } -static int __init eeepc_wmi_init(void) +static int __init eeepc_wmi_probe(struct platform_device *pdev) { - int err; - if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { pr_warning("No known WMI GUID found\n"); @@ -1280,29 +1254,24 @@ static int __init eeepc_wmi_init(void) return -ENODEV; } - platform_device = eeepc_wmi_add(); - if (IS_ERR(platform_device)) { - err = PTR_ERR(platform_device); - goto fail_eeepc_wmi; - } + return eeepc_wmi_add(pdev); +} - err = platform_driver_register(&platform_driver); - if (err) { - pr_warning("Unable to register platform driver\n"); - goto fail_platform_driver; - } +static struct platform_device *platform_device; +static int __init eeepc_wmi_init(void) +{ + platform_device = platform_create_bundle(&platform_driver, + eeepc_wmi_probe, + NULL, 0, NULL, 0); + if (IS_ERR(platform_device)) + return PTR_ERR(platform_device); return 0; - -fail_platform_driver: - eeepc_wmi_remove(platform_device); -fail_eeepc_wmi: - return err; } static void __exit eeepc_wmi_exit(void) { - eeepc_wmi_remove(platform_device); + platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); } -- cgit v1.2.3 From 8b6ff93c8520b7b48fa365e392b81fa97005f576 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:34 +0100 Subject: eeepc-wmi: reorder defines Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 1fc191bc0a3f..aa9e1d1719da 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -60,20 +60,20 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); -#define NOTIFY_BRNUP_MIN 0x11 -#define NOTIFY_BRNUP_MAX 0x1f -#define NOTIFY_BRNDOWN_MIN 0x20 -#define NOTIFY_BRNDOWN_MAX 0x2e +#define NOTIFY_BRNUP_MIN 0x11 +#define NOTIFY_BRNUP_MAX 0x1f +#define NOTIFY_BRNDOWN_MIN 0x20 +#define NOTIFY_BRNDOWN_MAX 0x2e -#define EEEPC_WMI_METHODID_DEVS 0x53564544 -#define EEEPC_WMI_METHODID_DSTS 0x53544344 -#define EEEPC_WMI_METHODID_CFVS 0x53564643 +#define EEEPC_WMI_METHODID_DSTS 0x53544344 +#define EEEPC_WMI_METHODID_DEVS 0x53564544 +#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 +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_TPDLED 0x00100011 static bool hotplug_wireless; -- cgit v1.2.3 From 78719cb645be3f079e547e61e0573bb36c1b9c2b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:35 +0100 Subject: eeepc-wmi: use the presence bit correctly I checked some more DSDT, and it seems that I wasn't totally right about the meaning of DSTS return value. Bit 0 is clearly the status of the device, and I discovered that bit 16 is set when the device is present. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index aa9e1d1719da..391c32bd703e 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -75,6 +75,9 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 +#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 + static bool hotplug_wireless; module_param(hotplug_wireless, bool, 0444); @@ -265,16 +268,10 @@ static int eeepc_wmi_get_devstate_simple(u32 dev_id) if (ACPI_FAILURE(status)) return -EINVAL; - /* 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) + if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; - return retval & 0x1; + return retval & EEEPC_WMI_DSTS_STATUS_BIT; } /* -- cgit v1.2.3 From d21faa29d027f58afe4d29864137092136e24389 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:36 +0100 Subject: eeepc-wmi: add camera and card reader support Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- Documentation/ABI/testing/sysfs-platform-eeepc-wmi | 14 ++++ drivers/platform/x86/eeepc-wmi.c | 89 +++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi index e4b5fef5fadd..9fc8d33e280d 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi @@ -8,3 +8,17 @@ Description: * 0 -> Super Performance Mode * 1 -> High Performance Mode * 2 -> Power Saving Mode + +What: /sys/devices/platform/eeepc-wmi/camera +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the camera. 1 means on, 0 means off. + +What: /sys/devices/platform/eeepc-wmi/cardr +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the card reader. 1 means on, 0 means off. diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 391c32bd703e..83415dd42d0d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -73,6 +73,8 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_CAMERA 0x00060013 +#define EEEPC_WMI_DEVID_CARDREADER 0x00080013 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 @@ -879,6 +881,70 @@ static void eeepc_wmi_notify(u32 value, void *context) kfree(obj); } +/* + * Sys helpers + */ +static int parse_arg(const char *buf, unsigned long count, int *val) +{ + if (!count) + return 0; + if (sscanf(buf, "%i", val) != 1) + return -EINVAL; + return count; +} + +static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) +{ + acpi_status status; + u32 retval; + int rv, value; + + value = eeepc_wmi_get_devstate_simple(devid); + if (value == -ENODEV) /* Check device presence */ + return value; + + rv = parse_arg(buf, count, &value); + status = eeepc_wmi_set_devstate(devid, value, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + return rv; +} + +static ssize_t show_sys_wmi(int devid, char *buf) +{ + int value = eeepc_wmi_get_devstate_simple(devid); + + if (value < 0) + return value; + + return sprintf(buf, "%d\n", value); +} + +#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ + static ssize_t show_##_name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return show_sys_wmi(_cm, buf); \ + } \ + static ssize_t store_##_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return store_sys_wmi(_cm, buf, count); \ + } \ + static struct device_attribute dev_attr_##_name = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = _mode }, \ + .show = show_##_name, \ + .store = store_##_name, \ + } + +EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); +EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); + static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -904,11 +970,32 @@ static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, + &dev_attr_camera.attr, + &dev_attr_cardr.attr, NULL }; +static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, + int idx) +{ + bool supported = true; + int devid = -1; + + if (attr == &dev_attr_camera.attr) + devid = EEEPC_WMI_DEVID_CAMERA; + else if (attr == &dev_attr_cardr.attr) + devid = EEEPC_WMI_DEVID_CARDREADER; + + if (devid != -1) + supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; + + return supported ? attr->mode : 0; +} + static struct attribute_group platform_attribute_group = { - .attrs = platform_attributes + .is_visible = eeepc_sysfs_is_visible, + .attrs = platform_attributes }; static void eeepc_wmi_sysfs_exit(struct platform_device *device) -- cgit v1.2.3 From 388fcbd0a988c9290a2d2252ca7108c54aa26e0f Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:37 +0100 Subject: eeepc-wmi: add wimax support Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 83415dd42d0d..0db700939433 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -71,6 +71,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_WLAN 0x00010011 #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 +#define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 @@ -138,6 +139,7 @@ struct eeepc_wmi { struct rfkill *wlan_rfkill; struct rfkill *bluetooth_rfkill; + struct rfkill *wimax_rfkill; struct rfkill *wwan3g_rfkill; struct hotplug_slot *hotplug_slot; @@ -691,6 +693,11 @@ static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) rfkill_destroy(eeepc->bluetooth_rfkill); eeepc->bluetooth_rfkill = NULL; } + if (eeepc->wimax_rfkill) { + rfkill_unregister(eeepc->wimax_rfkill); + rfkill_destroy(eeepc->wimax_rfkill); + eeepc->wimax_rfkill = NULL; + } if (eeepc->wwan3g_rfkill) { rfkill_unregister(eeepc->wwan3g_rfkill); rfkill_destroy(eeepc->wwan3g_rfkill); @@ -719,6 +726,13 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, + "eeepc-wimax", RFKILL_TYPE_WIMAX, + EEEPC_WMI_DEVID_WIMAX); + + if (result && result != -ENODEV) + goto exit; + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, "eeepc-wwan3g", RFKILL_TYPE_WWAN, EEEPC_WMI_DEVID_WWAN3G); @@ -1276,7 +1290,11 @@ static int eeepc_hotk_restore(struct device *device) if (eeepc->bluetooth_rfkill) { bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); -} + } + if (eeepc->wimax_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX); + rfkill_set_sw_state(eeepc->wimax_rfkill, bl); + } if (eeepc->wwan3g_rfkill) { bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); -- cgit v1.2.3 From e56befa899df9c5ceeffef236e6e3ac81983c217 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:38 +0100 Subject: eeepc-wmi: set the right key code for 0xe9 This key should power off the backlight, not the display, it is also used in acpi/video.c to do the same thing. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0db700939433..75dd692d31ee 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -103,7 +103,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, - { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, + { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, { KE_END, 0}, }; -- cgit v1.2.3 From 5af94b3ae71d41ff494e26b445bb454873bbe81b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:39 +0100 Subject: eeepc-wmi: support backlight power (bl_power) attribute Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 75 ++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 75dd692d31ee..910eb00fb13e 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -73,13 +73,16 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 -#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 +#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 +#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF +#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 static bool hotplug_wireless; @@ -262,7 +265,7 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, } /* Helper for special devices with magic return codes */ -static int eeepc_wmi_get_devstate_simple(u32 dev_id) +static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask) { u32 retval = 0; acpi_status status; @@ -275,7 +278,12 @@ static int eeepc_wmi_get_devstate_simple(u32 dev_id) if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; - return retval & EEEPC_WMI_DSTS_STATUS_BIT; + return retval & mask; +} + +static int eeepc_wmi_get_devstate_simple(u32 dev_id) +{ + return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT); } /* @@ -770,34 +778,53 @@ exit: /* * Backlight */ +static int read_backlight_power(void) +{ + int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT); + + if (ret < 0) + return ret; + + return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; +} + static int read_brightness(struct backlight_device *bd) { u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval); if (ACPI_FAILURE(status)) - return -1; + return -EIO; else - return retval & 0xFF; + return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK; } static int update_bl_status(struct backlight_device *bd) { - u32 ctrl_param; acpi_status status; + int power; ctrl_param = bd->props.brightness; - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, ctrl_param, NULL); if (ACPI_FAILURE(status)) - return -1; - else - return 0; + return -EIO; + + power = read_backlight_power(); + if (power != -ENODEV && bd->props.power != power) { + ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + } + return 0; } static const struct backlight_ops eeepc_wmi_bl_ops = { @@ -827,9 +854,29 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) { struct backlight_device *bd; struct backlight_properties props; + int max; + int power; + + max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS, + EEEPC_WMI_DSTS_MAX_BRIGTH_MASK); + power = read_backlight_power(); + + if (max < 0 && power < 0) { + /* Try to keep the original error */ + if (max == -ENODEV && power == -ENODEV) + return -ENODEV; + if (max != -ENODEV) + return max; + else + return power; + } + if (max == -ENODEV) + max = 0; + if (power == -ENODEV) + power = FB_BLANK_UNBLANK; memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 15; + props.max_brightness = max; bd = backlight_device_register(EEEPC_WMI_FILE, &eeepc->platform_device->dev, eeepc, &eeepc_wmi_bl_ops, &props); @@ -841,7 +888,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) eeepc->backlight_device = bd; bd->props.brightness = read_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = power; backlight_update_status(bd); return 0; @@ -1202,7 +1249,7 @@ static int __init eeepc_wmi_add(struct platform_device *pdev) if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); - if (err) + if (err && err != -ENODEV) goto fail_backlight; } else pr_info("Backlight controlled by ACPI video driver\n"); -- cgit v1.2.3 From 97bcd144bd077d61f2641db979953cd09e1c3c17 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:40 +0100 Subject: eeepc-wmi: respect wireless_hotplug setting Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 910eb00fb13e..bc7133345674 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -748,6 +748,9 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + if (!eeepc->hotplug_wireless) + goto exit; + result = eeepc_setup_pci_hotplug(eeepc); /* * If we get -EBUSY then something else is handling the PCI hotplug - -- cgit v1.2.3 From b6a2de2053dbbd4439dcb46f16ddc6e683258d17 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:41 +0100 Subject: eeepc-wmi: real touchpad led device id is 0x001000012 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index bc7133345674..898476716246 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -77,7 +77,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 -#define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 @@ -303,7 +303,7 @@ static void tpd_led_update(struct work_struct *work) 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); + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); } static void tpd_led_set(struct led_classdev *led_cdev, @@ -317,9 +317,9 @@ static void tpd_led_set(struct led_classdev *led_cdev, queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } -static int read_tpd_state(struct eeepc_wmi *eeepc) +static int read_tpd_led_state(struct eeepc_wmi *eeepc) { - return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TPDLED); + return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -328,14 +328,14 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); - return read_tpd_state(eeepc); + return read_tpd_led_state(eeepc); } static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) { int rv; - if (read_tpd_state(eeepc) < 0) + if (read_tpd_led_state(eeepc) < 0) return 0; eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); -- cgit v1.2.3 From db34bb9575004dc55931ef67b0fcd1fc5ebfa26b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:30:48 +0100 Subject: eeepc-wmi: comments keymap to clarify the meaning of some keys Found while checking PDF manuals... Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 898476716246..a78a90d886da 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -99,13 +99,13 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x30, { KEY_VOLUMEUP } }, { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, { KE_KEY, 0x32, { KEY_MUTE } }, - { KE_KEY, 0x5c, { KEY_F15 } }, + { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, - { KE_KEY, 0xe1, { KEY_F14 } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ + { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, { KE_END, 0}, }; -- cgit v1.2.3 From 51d296304ffe00eecd6195233966d250685665df Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:56 +0300 Subject: platform: x86: acer-wmi: world-writable sysfs threeg file Don't allow everybody to write to hardware registers. Signed-off-by: Vasiliy Kulikov Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c5c4b8c32eb8..a7bcad7eb093 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1280,7 +1280,7 @@ static ssize_t set_bool_threeg(struct device *dev, return -EINVAL; return count; } -static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, +static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, set_bool_threeg); static ssize_t show_interface(struct device *dev, struct device_attribute *attr, -- cgit v1.2.3 From 257065491ae88f1b86ddf4324bea77687c3d7403 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:59 +0300 Subject: platform: x86: asus_acpi: world-writable procfs files Don't allow everybody to change ACPI settings. The comment says that it is done deliberatelly, however, the comment before disp_proc_write() says that at least one of these setting is experimental. Signed-off-by: Vasiliy Kulikov Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus_acpi.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 4633fd8532cc..fe495939c307 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -1081,14 +1081,8 @@ static int asus_hotk_add_fs(struct acpi_device *device) struct proc_dir_entry *proc; mode_t mode; - /* - * If parameter uid or gid is not changed, keep the default setting for - * our proc entries (-rw-rw-rw-) else, it means we care about security, - * and then set to -rw-rw---- - */ - if ((asus_uid == 0) && (asus_gid == 0)) { - mode = S_IFREG | S_IRUGO | S_IWUGO; + mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP; } else { mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; printk(KERN_WARNING " asus_uid and asus_gid parameters are " -- cgit v1.2.3 From 42e2ae10d8c1926e34e23b6aadf89b29ca6cfe59 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:24:03 +0300 Subject: platform: x86: tc1100-wmi: world-writable sysfs wireless and jogdial files Don't allow everybody to change WMI settings. Signed-off-by: Vasiliy Kulikov Signed-off-by: Matthew Garrett --- drivers/platform/x86/tc1100-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index 1fe0f1feff71..865ef78d6f1a 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -162,7 +162,7 @@ set_bool_##value(struct device *dev, struct device_attribute *attr, \ return -EINVAL; \ return count; \ } \ -static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ +static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ show_bool_##value, set_bool_##value); show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); -- cgit v1.2.3 From 41d4aaf09df95ce513ab8b6438b3bf0750c39c50 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:27:30 +0100 Subject: asus-laptop: let WLED alone on L1400B Asus took the DSDT from another model (L84F), made some change to make it work, but forgot to remove WLED method (the laptop doesn't have a wireless card). They even didn't change the model name. ref: https://bugzilla.kernel.org/show_bug.cgi?id=25712 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index f3aa6a7fdab6..7be2a09636c3 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -1556,6 +1557,20 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) return result; } +static void __devinit asus_dmi_check(void) +{ + const char *model; + + model = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!model) + return; + + /* On L1400B WLED control the sound card, don't mess with it ... */ + if (strncmp(model, "L1400B", 6) == 0) { + wlan_status = -1; + } +} + static bool asus_device_present; static int __devinit asus_acpi_add(struct acpi_device *device) @@ -1574,6 +1589,8 @@ static int __devinit asus_acpi_add(struct acpi_device *device) device->driver_data = asus; asus->device = device; + asus_dmi_check(); + result = asus_acpi_init(asus); if (result) goto fail_platform; -- cgit v1.2.3 From 1f6c542ee9c4dc8fce29fe946c1a8a43baea09a2 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:27:31 +0100 Subject: asus-laptop: remove deprecated interfaces (lcd_switch and display_get) I should have done that one year ago, so it's more than time to do it. These two features use non-standard interfaces. There are the only features that really need multiple path to guess what's the right method name on a specific laptop. Removing them allow to remove a lot of code an significantly clean the driver. This will affect the backlight code which won't be able to know if the backlight is on or off. The platform display file will also be write only (like the one in eeepc-laptop). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 163 +------------------------------------ 1 file changed, 3 insertions(+), 160 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 7be2a09636c3..92e2a0b16328 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -158,46 +158,9 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " #define METHOD_BRIGHTNESS_SET "SPLV" #define METHOD_BRIGHTNESS_GET "GPLV" -/* Backlight */ -static acpi_handle lcd_switch_handle; -static char *lcd_switch_paths[] = { - "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ - "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ - "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ - "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ - "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ - "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ - "\\_SB.PCI0.PX40.Q10", /* S1x */ - "\\Q10"}; /* A2x, L2D, L3D, M2E */ - /* Display */ #define METHOD_SWITCH_DISPLAY "SDSP" -static acpi_handle display_get_handle; -static char *display_get_paths[] = { - /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ - "\\_SB.PCI0.P0P1.VGA.GETD", - /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ - "\\_SB.PCI0.P0P2.VGA.GETD", - /* A6V A6Q */ - "\\_SB.PCI0.P0P3.VGA.GETD", - /* A6T, A6M */ - "\\_SB.PCI0.P0PA.VGA.GETD", - /* L3C */ - "\\_SB.PCI0.PCI1.VGAC.NMAP", - /* Z96F */ - "\\_SB.PCI0.VGA.GETD", - /* A2D */ - "\\ACTD", - /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ - "\\ADVG", - /* P30 */ - "\\DNXT", - /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ - "\\INFB", - /* A3F A6F A3N A3L M6N W3N W6A */ - "\\SSTE"}; - #define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ #define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ @@ -247,7 +210,6 @@ struct asus_laptop { int wireless_status; bool have_rsts; - int lcd_state; struct rfkill *gps_rfkill; @@ -560,48 +522,6 @@ error: /* * Backlight device */ -static int asus_lcd_status(struct asus_laptop *asus) -{ - return asus->lcd_state; -} - -static int asus_lcd_set(struct asus_laptop *asus, int value) -{ - int lcd = 0; - acpi_status status = 0; - - lcd = !!value; - - if (lcd == asus_lcd_status(asus)) - return 0; - - if (!lcd_switch_handle) - return -ENODEV; - - status = acpi_evaluate_object(lcd_switch_handle, - NULL, NULL, NULL); - - if (ACPI_FAILURE(status)) { - pr_warning("Error switching LCD\n"); - return -ENODEV; - } - - asus->lcd_state = lcd; - return 0; -} - -static void lcd_blank(struct asus_laptop *asus, int blank) -{ - struct backlight_device *bd = asus->backlight_device; - - asus->lcd_state = (blank == FB_BLANK_UNBLANK); - - if (bd) { - bd->props.power = blank; - backlight_update_status(bd); - } -} - static int asus_read_brightness(struct backlight_device *bd) { struct asus_laptop *asus = bl_get_data(bd); @@ -629,16 +549,9 @@ static int asus_set_brightness(struct backlight_device *bd, int value) static int update_bl_status(struct backlight_device *bd) { - struct asus_laptop *asus = bl_get_data(bd); - int rv; int value = bd->props.brightness; - rv = asus_set_brightness(bd, value); - if (rv) - return rv; - - value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; - return asus_lcd_set(asus, value); + return asus_set_brightness(bd, value); } static const struct backlight_ops asusbl_ops = { @@ -662,8 +575,7 @@ static int asus_backlight_init(struct asus_laptop *asus) 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) + acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL)) return 0; memset(&props, 0, sizeof(struct backlight_properties)); @@ -971,41 +883,6 @@ static void asus_set_display(struct asus_laptop *asus, int value) return; } -static int read_display(struct asus_laptop *asus) -{ - unsigned long long value = 0; - acpi_status rv = AE_OK; - - /* - * In most of the case, we know how to set the display, but sometime - * we can't read it - */ - if (display_get_handle) { - rv = acpi_evaluate_integer(display_get_handle, NULL, - NULL, &value); - if (ACPI_FAILURE(rv)) - pr_warning("Error reading display status\n"); - } - - value &= 0x0F; /* needed for some models, shouldn't hurt others */ - - return value; -} - -/* - * Now, *this* one could be more user-friendly, but so far, no-one has - * complained. The significance of bits is the same as in store_disp() - */ -static ssize_t show_disp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct asus_laptop *asus = dev_get_drvdata(dev); - - if (!display_get_handle) - return -ENODEV; - return sprintf(buf, "%d\n", read_display(asus)); -} - /* * Experimental support for display switching. As of now: 1 should activate * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI. @@ -1247,15 +1124,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) struct asus_laptop *asus = acpi_driver_data(device); u16 count; - /* - * We need to tell the backlight device when the backlight power is - * switched - */ - if (event == ATKD_LCD_ON) - lcd_blank(asus, FB_BLANK_UNBLANK); - else if (event == ATKD_LCD_OFF) - lcd_blank(asus, FB_BLANK_POWERDOWN); - /* TODO Find a better way to handle events count. */ count = asus->event_count[event % 128]++; acpi_bus_generate_proc_event(asus->device, event, count); @@ -1282,7 +1150,7 @@ static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, store_bluetooth); static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); -static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); +static DEVICE_ATTR(display, S_IWUSR, NULL, 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); @@ -1393,26 +1261,6 @@ static struct platform_driver platform_driver = { } }; -static int asus_handle_init(char *name, acpi_handle * handle, - char **paths, int num_paths) -{ - int i; - acpi_status status; - - for (i = 0; i < num_paths; i++) { - status = acpi_get_handle(NULL, paths[i], handle); - if (ACPI_SUCCESS(status)) - return 0; - } - - *handle = NULL; - return -ENODEV; -} - -#define ASUS_HANDLE_INIT(object) \ - asus_handle_init(#object, &object##_handle, object##_paths, \ - ARRAY_SIZE(object##_paths)) - /* * This function is used to initialize the context with right values. In this * method, we can make all the detection we want, and modify the asus_laptop @@ -1498,10 +1346,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus) if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) asus->have_rsts = true; - /* Scheduled for removal */ - ASUS_HANDLE_INIT(lcd_switch); - ASUS_HANDLE_INIT(display_get); - kfree(model); return AE_OK; @@ -1553,7 +1397,6 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) asus_als_level(asus, asus->light_level); } - asus->lcd_state = 1; /* LCD should be on when the module load */ return result; } -- cgit v1.2.3 From b8a667095b6bba29b028bc9b29844efb44c06d79 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:42 +0100 Subject: eeepc-wmi: add touchpad sysfs file Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- Documentation/ABI/testing/sysfs-platform-eeepc-wmi | 7 +++++++ drivers/platform/x86/eeepc-wmi.c | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi index 9fc8d33e280d..26acb6842c44 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi @@ -22,3 +22,10 @@ KernelVersion: 2.6.39 Contact: "Corentin Chary" Description: Control the card reader. 1 means on, 0 means off. + +What: /sys/devices/platform/eeepc-wmi/touchpad +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the card touchpad. 1 means on, 0 means off. diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index a78a90d886da..16c7f2d62515 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -77,6 +77,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 +#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 #define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 @@ -1006,6 +1007,7 @@ static ssize_t show_sys_wmi(int devid, char *buf) .store = store_##_name, \ } +EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD); EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); @@ -1036,6 +1038,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, &dev_attr_camera.attr, &dev_attr_cardr.attr, + &dev_attr_touchpad.attr, NULL }; @@ -1050,6 +1053,8 @@ static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, devid = EEEPC_WMI_DEVID_CAMERA; else if (attr == &dev_attr_cardr.attr) devid = EEEPC_WMI_DEVID_CARDREADER; + else if (attr == &dev_attr_touchpad.attr) + devid = EEEPC_WMI_DEVID_TOUCHPAD; if (devid != -1) supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; -- cgit v1.2.3 From 03bbb28a9de42acd9436b54b53b690fad8119cc5 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:43 +0100 Subject: eeepc-wmi: reorder device ids Each device seems to be in a "group" (devid >> 16 & 0xFF). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 16c7f2d62515..eea8c942653b 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -65,21 +65,32 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define NOTIFY_BRNDOWN_MIN 0x20 #define NOTIFY_BRNDOWN_MAX 0x2e +/* WMI Methods */ #define EEEPC_WMI_METHODID_DSTS 0x53544344 #define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_CFVS 0x53564643 +/* Wireless */ #define EEEPC_WMI_DEVID_WLAN 0x00010011 #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 + +/* Backlight and Brightness */ #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 + +/* Misc */ #define EEEPC_WMI_DEVID_CAMERA 0x00060013 + +/* Storage */ #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 + +/* Input */ #define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 #define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 +/* DSTS masks */ #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 #define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF -- cgit v1.2.3 From c6c14f9e88bf44e0595a15be4f1fb671d2bf5484 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:30:47 +0100 Subject: eeepc-wmi: add camera keys These keys are supposed to be handled by any software using the camera (like webKam or cheese...). They can also be used to actually move the camera when possible. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 7 +++++++ include/linux/input.h | 7 +++++++ 2 files changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index eea8c942653b..d3997757e790 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -114,11 +114,18 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, + { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, + { KE_KEY, 0xec, { KEY_CAMERA_UP } }, + { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, + { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, + { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, { KE_END, 0}, }; diff --git a/include/linux/input.h b/include/linux/input.h index e428382ca28a..050504260259 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -664,6 +664,13 @@ struct input_keymap_entry { #define KEY_TOUCHPAD_ON 0x213 #define KEY_TOUCHPAD_OFF 0x214 +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 -- cgit v1.2.3 From 8441fd8335a194fc119cd275c056bee9f66dc6d6 Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Mon, 7 Feb 2011 14:45:55 -0500 Subject: intel_mid_powerbtn: add power button driver for Medfield platform (#3) The power button is connected to MSIC on Medfield, we will get two interrupts from IOAPIC when pressing or releasing the power button. Signed-off-by: Hong Liu [Minor fixes as noted by Dmitry] Signed-off-by: Alan Cox Acked-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 8 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_mid_powerbtn.c | 148 ++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 drivers/platform/x86/intel_mid_powerbtn.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 211b48d695ed..a857090fa00a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -598,6 +598,14 @@ config GPIO_INTEL_PMIC Say Y here to support GPIO via the SCU IPC interface on Intel MID platforms. +config INTEL_MID_POWER_BUTTON + tristate "power button driver for Intel MID platforms" + depends on INTEL_SCU_IPC + help + This driver handles the power button on the Intel MID platforms. + + If unsure, say N. + config RAR_REGISTER bool "Restricted Access Region Register Driver" depends on PCI && X86_MRST diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 7452ffcf840e..5cbecda48c3d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o +obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c new file mode 100644 index 000000000000..213e79ba68d5 --- /dev/null +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -0,0 +1,148 @@ +/* + * Power button driver for Medfield. + * + * Copyright (C) 2010 Intel Corp + * + * 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "msic_power_btn" + +#define MSIC_IRQ_STAT 0x02 + #define MSIC_IRQ_PB (1 << 0) +#define MSIC_PB_CONFIG 0x3e +#define MSIC_PB_STATUS 0x3f + #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ + +struct mfld_pb_priv { + struct input_dev *input; + unsigned int irq; +}; + +static irqreturn_t mfld_pb_isr(int irq, void *dev_id) +{ + struct mfld_pb_priv *priv = dev_id; + int ret; + u8 pbstat; + + ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat); + if (ret < 0) + return IRQ_HANDLED; + + input_event(priv->input, EV_KEY, KEY_POWER, !(pbstat & MSIC_PB_LEVEL)); + input_sync(priv->input); + + return IRQ_HANDLED; +} + +static int __devinit mfld_pb_probe(struct platform_device *pdev) +{ + struct mfld_pb_priv *priv; + struct input_dev *input; + int irq; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + priv = kzalloc(sizeof(struct mfld_pb_priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->input = input; + priv->irq = irq; + + input->name = pdev->name; + input->phys = "power-button/input0"; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = request_threaded_irq(priv->irq, NULL, mfld_pb_isr, + 0, DRIVER_NAME, priv); + if (error) { + dev_err(&pdev->dev, + "unable to request irq %d for mfld power button\n", + irq); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input dev, error %d\n", error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, priv); + return 0; + +err_free_irq: + free_irq(priv->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit mfld_pb_remove(struct platform_device *pdev) +{ + struct mfld_pb_priv *priv = platform_get_drvdata(pdev); + + free_irq(priv->irq, priv); + input_unregister_device(priv->input); + kfree(priv); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver mfld_pb_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mfld_pb_probe, + .remove = __devexit_p(mfld_pb_remove), +}; + +static int __init mfld_pb_init(void) +{ + return platform_driver_register(&mfld_pb_driver); +} +module_init(mfld_pb_init); + +static void __exit mfld_pb_exit(void) +{ + platform_driver_unregister(&mfld_pb_driver); +} +module_exit(mfld_pb_exit); + +MODULE_AUTHOR("Hong Liu "); +MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v1.2.3 From 3ea2def229d6fd2d0cb1770d3091643889663e72 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 5 Feb 2011 10:46:28 +0000 Subject: platform-drivers: x86: pmic: Fix up bogus irq hackery commit 456dc301([PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem) changes - 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); With the following explanation: "Latest kernel has many changes in IRQ subsystem and its interfaces, like adding irq_eoi" for struct irq_chip, this patch will make it support both the new and old interface." This is completely bogus. #1) The changelog does not match the patch at all #2) This driver relies on the assumption that it sits behind an eoi capable interrupt line. If the implementation of the underlying chip changes from eoi to irq_eoi then this driver has to follow that change and not add a total bogosity. Remove the sillyness and retrieve the interrupt data from irq_desc directly. No need to got through circles to look it up. Signed-off-by: Thomas Gleixner Cc: Feng Tang Cc: Matthew Garrett Cc: Alan Cox Cc: Alek Du Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_pmic_gpio.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 930e62762365..4eed130e7c18 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -244,11 +244,7 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) generic_handle_irq(pg->irq_base + gpio); } } - - 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); + desc->chip->irq_eoi(get_irq_desc_chip_data(desc)); } static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) -- cgit v1.2.3 From e3ddd619ba054acda88b2c524523333ae01e5f5a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 5 Feb 2011 10:46:30 +0000 Subject: platform-drivers: x86: Convert pmic to new irq_chip functions Old functions will go away soon. Remove the stray semicolons while at it. Signed-off-by: Thomas Gleixner Cc: Feng Tang Cc: Matthew Garrett Cc: Alan Cox Cc: Alek Du Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_pmic_gpio.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 4eed130e7c18..9134d9d07569 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -190,10 +190,10 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1 << (offset - 16)); } -static int pmic_irq_type(unsigned irq, unsigned type) +static int pmic_irq_type(struct irq_data *data, unsigned type) { - struct pmic_gpio *pg = get_irq_chip_data(irq); - u32 gpio = irq - pg->irq_base; + struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); + u32 gpio = data->irq - pg->irq_base; unsigned long flags; if (gpio >= pg->chip.ngpio) @@ -207,8 +207,6 @@ static int pmic_irq_type(unsigned irq, unsigned type) return 0; } - - static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); @@ -217,19 +215,15 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) } /* the gpiointr register is read-clear, so just do nothing. */ -static void pmic_irq_unmask(unsigned irq) -{ -}; +static void pmic_irq_unmask(struct irq_data *data) { } -static void pmic_irq_mask(unsigned irq) -{ -}; +static void pmic_irq_mask(struct irq_data *data) { } static struct irq_chip pmic_irqchip = { .name = "PMIC-GPIO", - .mask = pmic_irq_mask, - .unmask = pmic_irq_unmask, - .set_type = pmic_irq_type, + .irq_mask = pmic_irq_mask, + .irq_unmask = pmic_irq_unmask, + .irq_set_type = pmic_irq_type, }; static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) -- cgit v1.2.3 From 299046ddc4de330a7e35bb7670a8e227b6d4d842 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 5 Feb 2011 10:46:32 +0000 Subject: platform-drivers: x86: pmic: Use irq_chip buslock mechanism The set_type function of the pmic irq chip is a horrible hack. It schedules work because it cannot access the scu chip from the set_type function. That breaks the assumption, that the type is set after set_type has returned. irq_chips provide buslock functions to avoid the above. Convert the driver to use the proper model. Signed-off-by: Thomas Gleixner Cc: Feng Tang Cc: Matthew Garrett Cc: Alan Cox Cc: Alek Du Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_pmic_gpio.c | 80 ++++++++++++++-------------------- 1 file changed, 32 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 9134d9d07569..df244c83681d 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -60,23 +60,18 @@ enum pmic_gpio_register { #define GPOSW_DOU 0x08 #define GPOSW_RDRV 0x30 +#define GPIO_UPDATE_TYPE 0x80000000 #define NUM_GPIO 24 -struct pmic_gpio_irq { - spinlock_t lock; - u32 trigger[NUM_GPIO]; - u32 dirty; - struct work_struct work; -}; - - struct pmic_gpio { + struct mutex buslock; struct gpio_chip chip; - struct pmic_gpio_irq irqtypes; void *gpiointr; int irq; unsigned irq_base; + unsigned int update_type; + u32 trigger_type; }; static void pmic_program_irqtype(int gpio, int type) @@ -92,37 +87,6 @@ static void pmic_program_irqtype(int gpio, int type) intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); }; -static void pmic_irqtype_work(struct work_struct *work) -{ - struct pmic_gpio_irq *t = - container_of(work, struct pmic_gpio_irq, work); - unsigned long flags; - int i; - u16 type; - - spin_lock_irqsave(&t->lock, flags); - /* As we drop the lock, we may need multiple scans if we race the - pmic_irq_type function */ - while (t->dirty) { - /* - * For each pin that has the dirty bit set send an IPC - * message to configure the hardware via the PMIC - */ - for (i = 0; i < NUM_GPIO; i++) { - if (!(t->dirty & (1 << i))) - continue; - t->dirty &= ~(1 << i); - /* We can't trust the array entry or dirty - once the lock is dropped */ - type = t->trigger[i]; - spin_unlock_irqrestore(&t->lock, flags); - pmic_program_irqtype(i, type); - spin_lock_irqsave(&t->lock, flags); - } - } - spin_unlock_irqrestore(&t->lock, flags); -} - static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { if (offset > 8) { @@ -190,20 +154,21 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1 << (offset - 16)); } +/* + * This is called from genirq with pg->buslock locked and + * irq_desc->lock held. We can not access the scu bus here, so we + * store the change and update in the bus_sync_unlock() function below + */ static int pmic_irq_type(struct irq_data *data, unsigned type) { struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); u32 gpio = data->irq - pg->irq_base; - unsigned long flags; if (gpio >= pg->chip.ngpio) return -EINVAL; - spin_lock_irqsave(&pg->irqtypes.lock, flags); - pg->irqtypes.trigger[gpio] = type; - pg->irqtypes.dirty |= (1 << gpio); - spin_unlock_irqrestore(&pg->irqtypes.lock, flags); - schedule_work(&pg->irqtypes.work); + pg->trigger_type = type; + pg->update_type = gpio | GPIO_UPDATE_TYPE; return 0; } @@ -214,6 +179,26 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return pg->irq_base + offset; } +static void pmic_bus_lock(struct irq_data *data) +{ + struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); + + mutex_lock(&pg->buslock); +} + +static void pmic_bus_sync_unlock(struct irq_data *data) +{ + struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); + + if (pg->update_type) { + unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE; + + pmic_program_irqtype(gpio, pg->trigger_type); + pg->update_type = 0; + } + mutex_unlock(&pg->buslock); +} + /* the gpiointr register is read-clear, so just do nothing. */ static void pmic_irq_unmask(struct irq_data *data) { } @@ -287,8 +272,7 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) pg->chip.can_sleep = 1; pg->chip.dev = dev; - INIT_WORK(&pg->irqtypes.work, pmic_irqtype_work); - spin_lock_init(&pg->irqtypes.lock); + mutex_init(&pg->buslock); pg->chip.dev = dev; retval = gpiochip_add(&pg->chip); -- cgit v1.2.3 From eb9a394e31cb88066805f29b363590d95a92b857 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 21:41:30 +0100 Subject: platform-drivers: x86: pmic: Use request_irq instead of chained handler There is no need to install a chained handler for this hardware. This is a plain x86 IOAPIC interrupt which is handled by the core code perfectly fine. There is nothing special about demultiplexing these gpio interrupts which justifies a custom hack. Replace it by a plain old interrupt handler installed with request_irq. That makes the code agnostic about the underlying primary interrupt hardware. The overhead for this is minimal, but it gives us the advantage of accounting, balancing and to detect interrupt storms. gpio interrupts are not really that performance critical. Patch fixups from akpm Signed-off-by: Thomas Gleixner Signed-off-by: Matthew Garrett Signed-off-by: Andrew Morton --- drivers/platform/x86/intel_pmic_gpio.c | 50 ++++++++-------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index df244c83681d..61433d492862 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -74,19 +74,6 @@ struct pmic_gpio { u32 trigger_type; }; -static void pmic_program_irqtype(int gpio, int type) -{ - if (type & IRQ_TYPE_EDGE_RISING) - intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20); - else - intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20); - - if (type & IRQ_TYPE_EDGE_FALLING) - intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10); - else - intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); -}; - static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { if (offset > 8) { @@ -179,26 +166,6 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return pg->irq_base + offset; } -static void pmic_bus_lock(struct irq_data *data) -{ - struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); - - mutex_lock(&pg->buslock); -} - -static void pmic_bus_sync_unlock(struct irq_data *data) -{ - struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); - - if (pg->update_type) { - unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE; - - pmic_program_irqtype(gpio, pg->trigger_type); - pg->update_type = 0; - } - mutex_unlock(&pg->buslock); -} - /* the gpiointr register is read-clear, so just do nothing. */ static void pmic_irq_unmask(struct irq_data *data) { } @@ -211,19 +178,21 @@ static struct irq_chip pmic_irqchip = { .irq_set_type = pmic_irq_type, }; -static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) +static irqreturn_t pmic_irq_handler(int irq, void *data) { - struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq); + struct pmic_gpio *pg = data; u8 intsts = *((u8 *)pg->gpiointr + 4); int gpio; + irqreturn_t ret = IRQ_NONE; for (gpio = 0; gpio < 8; gpio++) { if (intsts & (1 << gpio)) { pr_debug("pmic pin %d triggered\n", gpio); generic_handle_irq(pg->irq_base + gpio); + ret = IRQ_HANDLED; } } - desc->chip->irq_eoi(get_irq_desc_chip_data(desc)); + return ret; } static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) @@ -280,8 +249,13 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__); goto err; } - set_irq_data(pg->irq, pg); - set_irq_chained_handler(pg->irq, pmic_irq_handler); + + retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg); + if (retval) { + printk(KERN_WARNING "pmic: Interrupt request failed\n"); + goto err; + } + for (i = 0; i < 8; i++) { set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip, handle_simple_irq, "demux"); -- cgit v1.2.3 From a0a726d452cfde2742c4910484c1f9509efcb55d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 9 Feb 2011 16:39:40 -0500 Subject: acer-wmi: Fix capitalisation of GUID 6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3 needs to be 6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3 to match the hardware alias. Signed-off-by: Matthew Garrett Cc: Carlos Corbacho Cc: stable@kernel.org --- drivers/platform/x86/acer-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a7bcad7eb093..38b34a73866a 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -84,7 +84,7 @@ MODULE_LICENSE("GPL"); */ #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" -#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" +#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3" #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" -- cgit v1.2.3 From 46546d1562a7097fb195c6414c13f4e1f50873a6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 17 Feb 2011 18:44:09 +0000 Subject: Enable Dell All-In-One volume up/down keys Enable volume up and down hotkeys on WMI events GUID 284A0E6B-380E-472A-921F-E52786257FB4 and GUID 02314822-307C-4F66-bf0E-48AEAEB26CC8. Also works around a firmware bug where the _WED method should return an integer containing the key code and in fact the method returns the key code in element zero of a buffer. BugLink: http://bugs.launchpad.net/bugs/701530 BugLink: http://bugs.launchpad.net/bugs/676997 Signed-off-by: Colin Ian King Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 13 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/dell-wmi-aio.c | 171 ++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 drivers/platform/x86/dell-wmi-aio.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a857090fa00a..e24f4ac061af 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -101,6 +101,19 @@ config DELL_WMI To compile this driver as a module, choose M here: the module will be called dell-wmi. +config DELL_WMI_AIO + tristate "WMI Hotkeys for Dell All-In-One series" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + ---help--- + Say Y here if you want to support WMI-based hotkeys on Dell + All-In-One machines. + + To compile this driver as a module, choose M here: the module will + be called dell-wmi. + + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5cbecda48c3d..08bb8e682141 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o +obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c new file mode 100644 index 000000000000..0ed84573ae1f --- /dev/null +++ b/drivers/platform/x86/dell-wmi-aio.c @@ -0,0 +1,171 @@ +/* + * WMI hotkeys support for Dell All-In-One series + * + * 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 + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series"); +MODULE_LICENSE("GPL"); + +#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4" +#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8" + +static const char *dell_wmi_aio_guids[] = { + EVENT_GUID1, + EVENT_GUID2, + NULL +}; + +MODULE_ALIAS("wmi:"EVENT_GUID1); +MODULE_ALIAS("wmi:"EVENT_GUID2); + +static const struct key_entry dell_wmi_aio_keymap[] = { + { KE_KEY, 0xc0, { KEY_VOLUMEUP } }, + { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } }, + { KE_END, 0 } +}; + +static struct input_dev *dell_wmi_aio_input_dev; + +static void dell_wmi_aio_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_info("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + if (obj) { + unsigned int scancode; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + /* Most All-In-One correctly return integer scancode */ + scancode = obj->integer.value; + sparse_keymap_report_event(dell_wmi_aio_input_dev, + scancode, 1, true); + break; + case ACPI_TYPE_BUFFER: + /* Broken machines return the scancode in a buffer */ + if (obj->buffer.pointer && obj->buffer.length > 0) { + scancode = obj->buffer.pointer[0]; + sparse_keymap_report_event( + dell_wmi_aio_input_dev, + scancode, 1, true); + } + break; + } + } + kfree(obj); +} + +static int __init dell_wmi_aio_input_setup(void) +{ + int err; + + dell_wmi_aio_input_dev = input_allocate_device(); + + if (!dell_wmi_aio_input_dev) + return -ENOMEM; + + dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys"; + dell_wmi_aio_input_dev->phys = "wmi/input0"; + dell_wmi_aio_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(dell_wmi_aio_input_dev, + dell_wmi_aio_keymap, NULL); + if (err) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; + } + err = input_register_device(dell_wmi_aio_input_dev); + if (err) { + pr_info("Unable to register input device\n"); + goto err_free_keymap; + } + return 0; + +err_free_keymap: + sparse_keymap_free(dell_wmi_aio_input_dev); +err_free_dev: + input_free_device(dell_wmi_aio_input_dev); + return err; +} + +static const char *dell_wmi_aio_find(void) +{ + int i; + + for (i = 0; dell_wmi_aio_guids[i] != NULL; i++) + if (wmi_has_guid(dell_wmi_aio_guids[i])) + return dell_wmi_aio_guids[i]; + + return NULL; +} + +static int __init dell_wmi_aio_init(void) +{ + int err; + const char *guid; + + guid = dell_wmi_aio_find(); + if (!guid) { + pr_warning("No known WMI GUID found\n"); + return -ENXIO; + } + + err = dell_wmi_aio_input_setup(); + if (err) + return err; + + err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); + if (err) { + pr_err("Unable to register notify handler - %d\n", err); + sparse_keymap_free(dell_wmi_aio_input_dev); + input_unregister_device(dell_wmi_aio_input_dev); + return err; + } + + return 0; +} + +static void __exit dell_wmi_aio_exit(void) +{ + const char *guid; + + guid = dell_wmi_aio_find(); + wmi_remove_notify_handler(guid); + sparse_keymap_free(dell_wmi_aio_input_dev); + input_unregister_device(dell_wmi_aio_input_dev); +} + +module_init(dell_wmi_aio_init); +module_exit(dell_wmi_aio_exit); -- cgit v1.2.3 From 0dc409b2c58183025e89cb365ee654063ea6e364 Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Tue, 15 Feb 2011 17:36:07 +0800 Subject: dell-laptop: Toggle the unsupported hardware killswitch It is found on Dell Inspiron 1018 that the firmware reports that the hardware killswitch is not supported. This makes the rfkill key not functional. This patch forces the driver to toggle the firmware rfkill status in the case that the hardware killswitch is indicated as unsupported by the firmware. Signed-off-by: Keng-Yu Lin Tested-by: Alessio Igor Bogani Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 34657f96b5a5..ad24ef36f9f7 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -290,9 +290,12 @@ static int dell_rfkill_set(void *data, bool blocked) dell_send_request(buffer, 17, 11); /* If the hardware switch controls this radio, and the hardware - switch is disabled, don't allow changing the software state */ + switch is disabled, don't allow changing the software state. + If the hardware switch is reported as not supported, always + fire the SMI to toggle the killswitch. */ if ((hwswitch_state & BIT(hwswitch_bit)) && - !(buffer->output[1] & BIT(16))) { + !(buffer->output[1] & BIT(16)) && + (buffer->output[1] & BIT(0))) { ret = -EINVAL; goto out; } @@ -398,6 +401,23 @@ static const struct file_operations dell_debugfs_fops = { static void dell_update_rfkill(struct work_struct *ignored) { + int status; + + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); + + /* if hardware rfkill is not supported, set it explicitly */ + if (!(status & BIT(0))) { + if (wifi_rfkill) + dell_rfkill_set((void *)1, !((status & BIT(17)) >> 17)); + if (bluetooth_rfkill) + dell_rfkill_set((void *)2, !((status & BIT(18)) >> 18)); + if (wwan_rfkill) + dell_rfkill_set((void *)3, !((status & BIT(19)) >> 19)); + } + if (wifi_rfkill) dell_rfkill_query(wifi_rfkill, (void *)1); if (bluetooth_rfkill) -- cgit v1.2.3 From 910a44bf4588dc2c97cabaffe2775a05636d56da Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 16 Feb 2011 14:31:02 -0800 Subject: platform/x86: intel_mid_powerbutton needs INPUT intel_mid_powerbtn.c uses input interfaces, so it should depend on INPUT to fix build errors when CONFIG_INPUT is not enabled: intel_mid_powerbtn.c:(.text+0x56ca8f): undefined reference to `input_event' intel_mid_powerbtn.c:(.devinit.text+0x2e7b4): undefined reference to `input_allocate_device' intel_mid_powerbtn.c:(.devinit.text+0x2e7ff): undefined reference to `input_set_capability' intel_mid_powerbtn.c:(.devinit.text+0x2e84a): undefined reference to `input_register_device' intel_mid_powerbtn.c:(.devinit.text+0x2e88b): undefined reference to `input_free_device' intel_mid_powerbtn.c:(.devexit.text+0x42f0): undefined reference to `input_unregister_device' Signed-off-by: Randy Dunlap Cc: Hong Liu Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index e24f4ac061af..53f85037cc3c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -613,7 +613,7 @@ config GPIO_INTEL_PMIC config INTEL_MID_POWER_BUTTON tristate "power button driver for Intel MID platforms" - depends on INTEL_SCU_IPC + depends on INTEL_SCU_IPC && INPUT help This driver handles the power button on the Intel MID platforms. -- cgit v1.2.3 From d68590a1c1f54794533d3aa786a8743d51e32f85 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:25 +0900 Subject: sony-laptop: add some debug printk useful for bug reports Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5e83370b0812..3aaa92ee1d53 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -733,22 +733,30 @@ static int sony_find_snc_handle(int handle) for (i = 0x20; i < 0x30; i++) { acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); - if (result == handle) + if (result == handle) { + dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", + handle, i - 0x20); return i-0x20; + } } + dprintk("handle 0x%.4x not found\n", handle); return -1; } static int sony_call_snc_handle(int handle, int argument, int *result) { + int ret = 0; int offset = sony_find_snc_handle(handle); if (offset < 0) return -1; - return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, - result); + ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, + result); + dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, + *result); + return ret; } /* -- cgit v1.2.3 From 0471537edc4d0fde29a9241ddaaee3e8f0a04eed Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:27 +0900 Subject: sony-laptop: ignore hard switch rfkill events (SPIC) There is not much use for these events in userspace and handling the events themselves seems to get in the way of the actual activation of the rf devices. The SNC device doesn't expose them already. https://bugzilla.kernel.org/show_bug.cgi?id=15303 Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 8 +++++--- include/linux/sonypi.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3aaa92ee1d53..ad7c496691eb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1591,8 +1591,8 @@ static struct sonypi_event sonypi_blueev[] = { /* The set of possible wireless events */ static struct sonypi_event sonypi_wlessev[] = { - { 0x59, SONYPI_EVENT_WIRELESS_ON }, - { 0x5a, SONYPI_EVENT_WIRELESS_OFF }, + { 0x59, SONYPI_EVENT_IGNORE }, + { 0x5a, SONYPI_EVENT_IGNORE }, { 0, 0 } }; @@ -2732,6 +2732,9 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) if (ev == dev->event_types[i].events[j].data) { device_event = dev->event_types[i].events[j].event; + /* some events may require ignoring */ + if (!device_event) + return IRQ_HANDLED; goto found; } } @@ -2751,7 +2754,6 @@ found: sony_laptop_report_input_event(device_event); acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); sonypi_compat_report_event(device_event); - return IRQ_HANDLED; } diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 0e6dc3891942..c0f87da78f8a 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -40,6 +40,7 @@ /* events the user application reading /dev/sonypi can use */ +#define SONYPI_EVENT_IGNORE 0 #define SONYPI_EVENT_JOGDIAL_DOWN 1 #define SONYPI_EVENT_JOGDIAL_UP 2 #define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED 3 -- cgit v1.2.3 From da049f06502fdb797af9723df26f7687f86cb1a6 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:28 +0900 Subject: sony-laptop: use pr_ for messages Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 102 +++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index ad7c496691eb..1662b05a47af 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -71,8 +71,9 @@ #endif #define DRV_PFX "sony-laptop: " -#define dprintk(msg...) do { \ - if (debug) printk(KERN_WARNING DRV_PFX msg); \ +#define dprintk(msg...) do { \ + if (debug) \ + pr_warn(DRV_PFX msg); \ } while (0) #define SONY_LAPTOP_DRIVER_VERSION "0.6" @@ -402,7 +403,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) error = kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { - printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + pr_err(DRV_PFX "kfifo_alloc failed\n"); goto err_dec_users; } @@ -686,7 +687,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) return 0; } - printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n"); + pr_warn(DRV_PFX "acpi_callreadfunc failed\n"); return -1; } @@ -712,7 +713,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, if (status == AE_OK) { if (result != NULL) { if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad " + pr_warn(DRV_PFX "acpi_evaluate_object bad " "return type\n"); return -1; } @@ -721,7 +722,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, return 0; } - printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n"); + pr_warn(DRV_PFX "acpi_evaluate_object failed\n"); return -1; } @@ -980,7 +981,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) } if (!key_event->data) - printk(KERN_INFO DRV_PFX + pr_info(DRV_PFX "Unknown event: 0x%x 0x%x\n", key_handle, ev); @@ -1004,7 +1005,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, struct acpi_device_info *info; if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { - printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", + pr_warn(DRV_PFX "method: name: %4.4s, args %X\n", (char *)&info->name, info->param_count); kfree(info); @@ -1045,7 +1046,7 @@ static int sony_nc_resume(struct acpi_device *device) ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, item->value, NULL); if (ret < 0) { - printk("%s: %d\n", __func__, ret); + pr_err(DRV_PFX "%s: %d\n", __func__, ret); break; } } @@ -1065,7 +1066,7 @@ static int sony_nc_resume(struct acpi_device *device) /* set the last requested brightness level */ if (sony_backlight_device && sony_backlight_update_status(sony_backlight_device) < 0) - printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); + pr_warn(DRV_PFX "unable to restore brightness level\n"); /* re-read rfkill state */ sony_nc_rfkill_update(); @@ -1213,13 +1214,9 @@ static void sony_nc_rfkill_setup(struct acpi_device *device) } device_enum = (union acpi_object *) buffer.pointer; - if (!device_enum) { - pr_err("Invalid SN06 return object\n"); - goto out_no_enum; - } - if (device_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object type 0x%.2x\n", - device_enum->type); + if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) { + pr_err(DRV_PFX "Invalid SN06 return object 0x%.2x\n", + device_enum->type); goto out_no_enum; } @@ -1260,8 +1257,8 @@ static int sony_nc_add(struct acpi_device *device) acpi_handle handle; struct sony_nc_value *item; - printk(KERN_INFO DRV_PFX "%s v%s.\n", - SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + pr_info(DRV_PFX "%s v%s.\n", SONY_NC_DRIVER_NAME, + SONY_LAPTOP_DRIVER_VERSION); sony_nc_acpi_device = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -1278,10 +1275,11 @@ static int sony_nc_add(struct acpi_device *device) } if (debug) { - status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, - 1, sony_walk_callback, NULL, NULL, NULL); + status = acpi_walk_namespace(ACPI_TYPE_METHOD, + sony_nc_acpi_handle, 1, sony_walk_callback, + NULL, NULL, NULL); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); + pr_warn(DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; goto outwalk; } @@ -1303,17 +1301,16 @@ static int sony_nc_add(struct acpi_device *device) /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to create input devices.\n"); + pr_err(DRV_PFX "Unable to create input devices.\n"); goto outwalk; } if (acpi_video_backlight_support()) { - printk(KERN_INFO DRV_PFX "brightness ignored, must be " + pr_info(DRV_PFX "brightness ignored, must be " "controlled by ACPI video driver\n"); } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { - struct backlight_properties props; + struct backlight_properties props; memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = SONY_MAX_BRIGHTNESS - 1; sony_backlight_device = backlight_device_register("sony", NULL, @@ -1322,7 +1319,7 @@ static int sony_nc_add(struct acpi_device *device) &props); if (IS_ERR(sony_backlight_device)) { - printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); + pr_warning(DRV_PFX "unable to register backlight device\n"); sony_backlight_device = NULL; } else { sony_backlight_device->props.brightness = @@ -1849,7 +1846,7 @@ out: if (pcidev) pci_dev_put(pcidev); - printk(KERN_INFO DRV_PFX "detected Type%d model\n", + pr_info(DRV_PFX "detected Type%d model\n", dev->model == SONYPI_DEVICE_TYPE1 ? 1 : dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); } @@ -1897,7 +1894,7 @@ static int __sony_pic_camera_ready(void) static int __sony_pic_camera_off(void) { if (!camera) { - printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + pr_warn(DRV_PFX "camera control not enabled\n"); return -ENODEV; } @@ -1917,7 +1914,7 @@ static int __sony_pic_camera_on(void) int i, j, x; if (!camera) { - printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + pr_warn(DRV_PFX "camera control not enabled\n"); return -ENODEV; } @@ -1940,7 +1937,7 @@ static int __sony_pic_camera_on(void) } if (j == 0) { - printk(KERN_WARNING DRV_PFX "failed to power on camera\n"); + pr_warn(DRV_PFX "failed to power on camera\n"); return -ENODEV; } @@ -1996,7 +1993,7 @@ int sony_pic_camera_command(int command, u8 value) ITERATIONS_SHORT); break; default: - printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n", + pr_err(DRV_PFX "sony_pic_camera_command invalid: %d\n", command); break; } @@ -2403,7 +2400,7 @@ static int sonypi_compat_init(void) error = kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { - printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + pr_err(DRV_PFX "kfifo_alloc failed\n"); return error; } @@ -2413,11 +2410,11 @@ static int sonypi_compat_init(void) sonypi_misc_device.minor = minor; error = misc_register(&sonypi_misc_device); if (error) { - printk(KERN_ERR DRV_PFX "misc_register failed\n"); + pr_err(DRV_PFX "misc_register failed\n"); goto err_free_kfifo; } if (minor == -1) - printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", + pr_info(DRV_PFX "device allocated minor is %d\n", sonypi_misc_device.minor); return 0; @@ -2477,8 +2474,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) } for (i = 0; i < p->interrupt_count; i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING DRV_PFX - "Invalid IRQ %d\n", + pr_warn(DRV_PFX "Invalid IRQ %d\n", p->interrupts[i]); continue; } @@ -2517,7 +2513,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) ioport->io2.address_length); } else { - printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); + pr_err(DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); return AE_ERROR; } return AE_OK; @@ -2545,7 +2541,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) dprintk("Evaluating _STA\n"); result = acpi_bus_get_status(device); if (result) { - printk(KERN_WARNING DRV_PFX "Unable to read status\n"); + pr_warn(DRV_PFX "Unable to read status\n"); goto end; } @@ -2561,8 +2557,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, sony_pic_read_possible_resource, &spic_dev); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING DRV_PFX - "Failure evaluating %s\n", + pr_warn(DRV_PFX "Failure evaluating %s\n", METHOD_NAME__PRS); result = -ENODEV; } @@ -2676,7 +2671,7 @@ static int sony_pic_enable(struct acpi_device *device, /* check for total failure */ if (ACPI_FAILURE(status)) { - printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); + pr_err(DRV_PFX "Error evaluating _SRS\n"); result = -ENODEV; goto end; } @@ -2768,7 +2763,7 @@ static int sony_pic_remove(struct acpi_device *device, int type) struct sony_pic_irq *irq, *tmp_irq; if (sony_pic_disable(device)) { - printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); + pr_err(DRV_PFX "Couldn't disable device.\n"); return -ENXIO; } @@ -2808,8 +2803,8 @@ static int sony_pic_add(struct acpi_device *device) struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - printk(KERN_INFO DRV_PFX "%s v%s.\n", - SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + pr_info(DRV_PFX "%s v%s.\n", SONY_PIC_DRIVER_NAME, + SONY_LAPTOP_DRIVER_VERSION); spic_dev.acpi_dev = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -2819,16 +2814,14 @@ static int sony_pic_add(struct acpi_device *device) /* read _PRS resources */ result = sony_pic_possible_resources(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to read possible resources.\n"); + pr_err(DRV_PFX "Unable to read possible resources.\n"); goto err_free_resources; } /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to create input devices.\n"); + pr_err(DRV_PFX "Unable to create input devices.\n"); goto err_free_resources; } @@ -2869,7 +2862,7 @@ static int sony_pic_add(struct acpi_device *device) } } if (!spic_dev.cur_ioport) { - printk(KERN_ERR DRV_PFX "Failed to request_region.\n"); + pr_err(DRV_PFX "Failed to request_region.\n"); result = -ENODEV; goto err_remove_compat; } @@ -2889,7 +2882,7 @@ static int sony_pic_add(struct acpi_device *device) } } if (!spic_dev.cur_irq) { - printk(KERN_ERR DRV_PFX "Failed to request_irq.\n"); + pr_err(DRV_PFX "Failed to request_irq.\n"); result = -ENODEV; goto err_release_region; } @@ -2897,7 +2890,7 @@ static int sony_pic_add(struct acpi_device *device) /* set resource status _SRS */ result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); if (result) { - printk(KERN_ERR DRV_PFX "Couldn't enable device.\n"); + pr_err(DRV_PFX "Couldn't enable device.\n"); goto err_free_irq; } @@ -3006,8 +2999,7 @@ static int __init sony_laptop_init(void) if (!no_spic && dmi_check_system(sonypi_dmi_table)) { result = acpi_bus_register_driver(&sony_pic_driver); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to register SPIC driver."); + pr_err(DRV_PFX "Unable to register SPIC driver."); goto out; } spic_drv_registered = 1; @@ -3015,7 +3007,7 @@ static int __init sony_laptop_init(void) result = acpi_bus_register_driver(&sony_nc_driver); if (result) { - printk(KERN_ERR DRV_PFX "Unable to register SNC driver."); + pr_err(DRV_PFX "Unable to register SNC driver."); goto out_unregister_pic; } -- cgit v1.2.3 From d6b35d5cf16927d6ceefc322c6e332e272e362ee Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:29 +0900 Subject: sony-laptop: remove unused Type4 define Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 1662b05a47af..22519a45e270 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1442,7 +1442,6 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_DEVICE_TYPE1 0x00000001 #define SONYPI_DEVICE_TYPE2 0x00000002 #define SONYPI_DEVICE_TYPE3 0x00000004 -#define SONYPI_DEVICE_TYPE4 0x00000008 #define SONYPI_TYPE1_OFFSET 0x04 #define SONYPI_TYPE2_OFFSET 0x12 -- cgit v1.2.3 From d631b17fb5b8f1d745f93c4788b6ed485f337749 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:30 +0900 Subject: sony-laptop: cache handles and report them via sysfs Avoid calling into acpi each time we need to lookup a method handle and report the available handles to ease collection of information when debugging issues. Also move initialization of the platform driver earlier to allow adding files from other setup functions. Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 97 +++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 22519a45e270..7163392dbead 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -727,20 +727,79 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, return -1; } -static int sony_find_snc_handle(int handle) +struct sony_nc_handles { + u16 cap[0x10]; + struct device_attribute devattr; +}; + +struct sony_nc_handles *handles; + +static ssize_t sony_nc_handles_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ", + handles->cap[i]); + } + len += snprintf(buffer + len, PAGE_SIZE - len, "\n"); + + return len; +} + +static int sony_nc_handles_setup(struct platform_device *pd) { int i; int result; - for (i = 0x20; i < 0x30; i++) { - acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); - if (result == handle) { - dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", - handle, i - 0x20); - return i-0x20; + handles = kzalloc(sizeof(*handles), GFP_KERNEL); + + sysfs_attr_init(&handles->devattr.attr); + handles->devattr.attr.name = "handles"; + handles->devattr.attr.mode = S_IRUGO; + handles->devattr.show = sony_nc_handles_show; + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + if (!acpi_callsetfunc(sony_nc_acpi_handle, + "SN00", i + 0x20, &result)) { + dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", + result, i); + handles->cap[i] = result; } } + /* allow reading capabilities via sysfs */ + if (device_create_file(&pd->dev, &handles->devattr)) { + kfree(handles); + handles = NULL; + return -1; + } + + return 0; +} + +static int sony_nc_handles_cleanup(struct platform_device *pd) +{ + if (handles) { + device_remove_file(&pd->dev, &handles->devattr); + kfree(handles); + handles = NULL; + } + return 0; +} + +static int sony_find_snc_handle(int handle) +{ + int i; + for (i = 0; i < 0x10; i++) { + if (handles->cap[i] == handle) { + dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", + handle, i); + return i; + } + } dprintk("handle 0x%.4x not found\n", handle); return -1; } @@ -1274,6 +1333,10 @@ static int sony_nc_add(struct acpi_device *device) goto outwalk; } + result = sony_pf_add(); + if (result) + goto outpresent; + if (debug) { status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, 1, sony_walk_callback, @@ -1281,7 +1344,7 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_FAILURE(status)) { pr_warn(DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; - goto outwalk; + goto outpresent; } } @@ -1294,6 +1357,8 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); + if (sony_nc_handles_setup(sony_pf_device)) + goto outpresent; sony_nc_function_setup(device); sony_nc_rfkill_setup(device); } @@ -1302,7 +1367,7 @@ static int sony_nc_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { pr_err(DRV_PFX "Unable to create input devices.\n"); - goto outwalk; + goto outsnc; } if (acpi_video_backlight_support()) { @@ -1329,10 +1394,6 @@ static int sony_nc_add(struct acpi_device *device) } - result = sony_pf_add(); - if (result) - goto outbacklight; - /* create sony_pf sysfs attributes related to the SNC device */ for (item = sony_nc_values; item->name; ++item) { @@ -1378,14 +1439,17 @@ static int sony_nc_add(struct acpi_device *device) for (item = sony_nc_values; item->name; ++item) { device_remove_file(&sony_pf_device->dev, &item->devattr); } - sony_pf_remove(); - - outbacklight: if (sony_backlight_device) backlight_device_unregister(sony_backlight_device); sony_laptop_remove_input(); + outsnc: + sony_nc_handles_cleanup(sony_pf_device); + + outpresent: + sony_pf_remove(); + outwalk: sony_nc_rfkill_cleanup(); return result; @@ -1404,6 +1468,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } + sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); sony_nc_rfkill_cleanup(); -- cgit v1.2.3 From 1030062435a835cf3eec7ef5a9dfe0ce2cecb41a Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:31 +0900 Subject: sony-laptop: implement keyboard backlight support Recent Vaios have the opportunity to control the keyboard backlight via ACPI calls to the SNC device. Introduce two module parameters to control how keyboard backlight should be set at module loading (default to on and with 10 seconds timeout). Tested-by: Marco Chiappero Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 176 ++++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 7163392dbead..231f647c1bb6 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -125,6 +125,19 @@ MODULE_PARM_DESC(minor, "default is -1 (automatic)"); #endif +static int kbd_backlight; /* = 1 */ +module_param(kbd_backlight, int, 0444); +MODULE_PARM_DESC(kbd_backlight, + "set this to 0 to disable keyboard backlight, " + "1 to enable it (default: 0)"); + +static int kbd_backlight_timeout; /* = 0 */ +module_param(kbd_backlight_timeout, int, 0444); +MODULE_PARM_DESC(kbd_backlight_timeout, + "set this to 0 to set the default 10 seconds timeout, " + "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " + "(default: 0)"); + enum sony_nc_rfkill { SONY_WIFI, SONY_BLUETOOTH, @@ -1309,6 +1322,161 @@ out_no_enum: return; } +/* Keyboard backlight feature */ +#define KBDBL_HANDLER 0x137 +#define KBDBL_PRESENT 0xB00 +#define SET_MODE 0xC00 +#define SET_TIMEOUT 0xE00 + +struct kbd_backlight { + int mode; + int timeout; + struct device_attribute mode_attr; + struct device_attribute timeout_attr; +}; + +struct kbd_backlight *kbdbl_handle; + +static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) +{ + int result; + + if (value > 1) + return -EINVAL; + + if (sony_call_snc_handle(KBDBL_HANDLER, + (value << 0x10) | SET_MODE, &result)) + return -EIO; + + kbdbl_handle->mode = value; + + return 0; +} + +static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + int ret = 0; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (strict_strtoul(buffer, 10, &value)) + return -EINVAL; + + ret = __sony_nc_kbd_backlight_mode_set(value); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); + return count; +} + +static int __sony_nc_kbd_backlight_timeout_set(u8 value) +{ + int result; + + if (value > 3) + return -EINVAL; + + if (sony_call_snc_handle(KBDBL_HANDLER, + (value << 0x10) | SET_TIMEOUT, &result)) + return -EIO; + + kbdbl_handle->timeout = value; + + return 0; +} + +static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + int ret = 0; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (strict_strtoul(buffer, 10, &value)) + return -EINVAL; + + ret = __sony_nc_kbd_backlight_timeout_set(value); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); + return count; +} + +static int sony_nc_kbd_backlight_setup(struct platform_device *pd) +{ + int result; + + if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result)) + return 0; + if (!(result & 0x02)) + return 0; + + kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); + + sysfs_attr_init(&kbdbl_handle->mode_attr.attr); + kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; + kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; + kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; + + sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); + kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; + kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; + kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; + + if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) + goto outkzalloc; + + if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) + goto outmode; + + __sony_nc_kbd_backlight_mode_set(kbd_backlight); + __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout); + + return 0; + +outmode: + device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); +outkzalloc: + kfree(kbdbl_handle); + kbdbl_handle = NULL; + return -1; +} + +static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) +{ + if (kbdbl_handle) { + device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); + device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); + kfree(kbdbl_handle); + } + return 0; +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1359,6 +1527,8 @@ static int sony_nc_add(struct acpi_device *device) dprintk("Doing SNC setup\n"); if (sony_nc_handles_setup(sony_pf_device)) goto outpresent; + if (sony_nc_kbd_backlight_setup(sony_pf_device)) + goto outsnc; sony_nc_function_setup(device); sony_nc_rfkill_setup(device); } @@ -1367,7 +1537,7 @@ static int sony_nc_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { pr_err(DRV_PFX "Unable to create input devices.\n"); - goto outsnc; + goto outkbdbacklight; } if (acpi_video_backlight_support()) { @@ -1444,6 +1614,9 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); + outkbdbacklight: + sony_nc_kbd_backlight_cleanup(sony_pf_device); + outsnc: sony_nc_handles_cleanup(sony_pf_device); @@ -1468,6 +1641,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } + sony_nc_kbd_backlight_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); -- cgit v1.2.3 From 38b7480c2b6fc0b41eaa82c588053ddec4e69e59 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:32 +0900 Subject: sony-laptop: implement new backlight control method Reasonably recent Vaios have a 0x12f or 0x137 handler that exposes a fine lid backlight regulation with values ranging from 0 to 255. The patch is based on findings and code from Javier Achirica and Marco Chiappero Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 108 ++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 231f647c1bb6..a0ba0e795b67 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -938,11 +938,39 @@ static int sony_backlight_get_brightness(struct backlight_device *bd) return value - 1; } -static struct backlight_device *sony_backlight_device; +static int sony_nc_get_brightness_ng(struct backlight_device *bd) +{ + int result; + int *handle = (int *)bl_get_data(bd); + + sony_call_snc_handle(*handle, 0x0200, &result); + + return result & 0xff; +} + +static int sony_nc_update_status_ng(struct backlight_device *bd) +{ + int value, result; + int *handle = (int *)bl_get_data(bd); + + value = bd->props.brightness; + sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result); + + return sony_nc_get_brightness_ng(bd); +} + static const struct backlight_ops sony_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, .update_status = sony_backlight_update_status, .get_brightness = sony_backlight_get_brightness, }; +static const struct backlight_ops sony_backlight_ng_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = sony_nc_update_status_ng, + .get_brightness = sony_nc_get_brightness_ng, +}; +static int backlight_ng_handle; +static struct backlight_device *sony_backlight_device; /* * New SNC-only Vaios event mapping to driver known keys @@ -1135,11 +1163,6 @@ static int sony_nc_resume(struct acpi_device *device) sony_nc_function_setup(device); } - /* set the last requested brightness level */ - if (sony_backlight_device && - sony_backlight_update_status(sony_backlight_device) < 0) - pr_warn(DRV_PFX "unable to restore brightness level\n"); - /* re-read rfkill state */ sony_nc_rfkill_update(); @@ -1477,6 +1500,51 @@ static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) return 0; } +static void sony_nc_backlight_setup(void) +{ + acpi_handle unused; + int max_brightness = 0; + const struct backlight_ops *ops = NULL; + struct backlight_properties props; + + if (sony_find_snc_handle(0x12f) != -1) { + backlight_ng_handle = 0x12f; + ops = &sony_backlight_ng_ops; + max_brightness = 0xff; + + } else if (sony_find_snc_handle(0x137) != -1) { + backlight_ng_handle = 0x137; + ops = &sony_backlight_ng_ops; + max_brightness = 0xff; + + } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", + &unused))) { + ops = &sony_backlight_ops; + max_brightness = SONY_MAX_BRIGHTNESS - 1; + + } else + return; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = max_brightness; + sony_backlight_device = backlight_device_register("sony", NULL, + &backlight_ng_handle, + ops, &props); + + if (IS_ERR(sony_backlight_device)) { + pr_warning(DRV_PFX "unable to register backlight device\n"); + sony_backlight_device = NULL; + } else + sony_backlight_device->props.brightness = + ops->get_brightness(sony_backlight_device); +} + +static void sony_nc_backlight_cleanup(void) +{ + if (sony_backlight_device) + backlight_device_unregister(sony_backlight_device); +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1543,26 +1611,8 @@ static int sony_nc_add(struct acpi_device *device) if (acpi_video_backlight_support()) { pr_info(DRV_PFX "brightness ignored, must be " "controlled by ACPI video driver\n"); - } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", - &handle))) { - struct backlight_properties props; - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = SONY_MAX_BRIGHTNESS - 1; - sony_backlight_device = backlight_device_register("sony", NULL, - NULL, - &sony_backlight_ops, - &props); - - if (IS_ERR(sony_backlight_device)) { - pr_warning(DRV_PFX "unable to register backlight device\n"); - sony_backlight_device = NULL; - } else { - sony_backlight_device->props.brightness = - sony_backlight_get_brightness - (sony_backlight_device); - } - - } + } else + sony_nc_backlight_setup(); /* create sony_pf sysfs attributes related to the SNC device */ for (item = sony_nc_values; item->name; ++item) { @@ -1609,8 +1659,7 @@ static int sony_nc_add(struct acpi_device *device) for (item = sony_nc_values; item->name; ++item) { device_remove_file(&sony_pf_device->dev, &item->devattr); } - if (sony_backlight_device) - backlight_device_unregister(sony_backlight_device); + sony_nc_backlight_cleanup(); sony_laptop_remove_input(); @@ -1632,8 +1681,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) { struct sony_nc_value *item; - if (sony_backlight_device) - backlight_device_unregister(sony_backlight_device); + sony_nc_backlight_cleanup(); sony_nc_acpi_device = NULL; -- cgit v1.2.3 From a94d939db743d46b32cce7b45c8eabd287183f54 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:20 +0200 Subject: hp-wmi: check query return value in hp_wmi_perform_query Check BIOS provided return value code in hp_wmi_perform_query and print a warning on error. Printing is suppressed for HPWMI_RET_UNKNOWN_CMDTYPE which is returned when the command type is unsupported. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 9e05af9c41cb..87dfb2489762 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -89,6 +89,13 @@ struct bios_return { u32 value; }; +enum hp_return_value { + HPWMI_RET_WRONG_SIGNATURE = 0x02, + HPWMI_RET_UNKNOWN_COMMAND = 0x03, + HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, + HPWMI_RET_INVALID_PARAMETERS = 0x05, +}; + static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, @@ -171,6 +178,15 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, bios_return = *((struct bios_return *)obj->buffer.pointer); + if (bios_return.return_code) { + if (bios_return.return_code != HPWMI_RET_UNKNOWN_CMDTYPE) + printk(KERN_WARNING PREFIX "query 0x%x returned " + "error 0x%x\n", + query, bios_return.return_code); + kfree(obj); + return bios_return.return_code; + } + memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); kfree(obj); -- cgit v1.2.3 From 3bc65169a33f7582d4efd9013f6e879de08c161b Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:21 +0200 Subject: hp-wmi: remove a variable that is never read Remove the status variable from hp_wmi_perform_query which holds the return value from wmi_evaluate_method(). It is never checked as the function bails out if the output buffer hasn't been allocated which indicates the call failed. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 87dfb2489762..8e27c27900a3 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -153,7 +153,6 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, int buffersize) { struct bios_return bios_return; - acpi_status status; union acpi_object *obj; struct bios_args args = { .signature = 0x55434553, @@ -165,7 +164,7 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, struct acpi_buffer input = { sizeof(struct bios_args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); + wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); obj = output.pointer; -- cgit v1.2.3 From c3eee0cc13be3c14d2c11375af6a3bffdd86a561 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:22 +0200 Subject: hp-wmi: allow setting input and output buffer sizes separately Split buffersize parameter of hp_wmi_perform_query to insize and outsize. Existing callers are changed to use the same value for insize and outsize to avoid any regressions, with the exception of hp_wmi_set_block where the output buffer is unused and therefore outsize is set to 0 (this change is not seen by BIOS code). The maximum input buffer size is kept at 4 bytes as per struct bios_args. Some commands exist that take longer buffers, but they haven't been implemented. The data portion of bios_args can be trivially made dynamically allocated later when such larger buffers become needed. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 63 ++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 8e27c27900a3..5c8ae65950d4 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -86,7 +86,6 @@ struct bios_args { struct bios_return { u32 sigpass; u32 return_code; - u32 value; }; enum hp_return_value { @@ -136,7 +135,8 @@ static struct platform_driver hp_wmi_driver = { * query: The commandtype -> What should be queried * write: The command -> 0 read, 1 write, 3 ODM specific * buffer: Buffer used as input and/or output - * buffersize: Size of buffer + * insize: Size of input buffer + * outsize: Size of output buffer * * returns zero on success * an HP WMI query specific error code (which is positive) @@ -147,23 +147,28 @@ static struct platform_driver hp_wmi_driver = { * size. E.g. Battery info query (0x7) is defined to have 1 byte input * and 128 byte output. The caller would do: * buffer = kzalloc(128, GFP_KERNEL); - * ret = hp_wmi_perform_query(0x7, 0, buffer, 128) + * ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128) */ -static int hp_wmi_perform_query(int query, int write, u32 *buffer, - int buffersize) +static int hp_wmi_perform_query(int query, int write, void *buffer, + int insize, int outsize) { - struct bios_return bios_return; + struct bios_return *bios_return; + int actual_outsize; union acpi_object *obj; struct bios_args args = { .signature = 0x55434553, .command = write ? 0x2 : 0x1, .commandtype = query, - .datasize = buffersize, - .data = *buffer, + .datasize = insize, + .data = 0, }; struct acpi_buffer input = { sizeof(struct bios_args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + if (WARN_ON(insize > sizeof(args.data))) + return -EINVAL; + memcpy(&args.data, buffer, insize); + wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); obj = output.pointer; @@ -175,19 +180,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, return -EINVAL; } - bios_return = *((struct bios_return *)obj->buffer.pointer); + bios_return = (struct bios_return *)obj->buffer.pointer; - if (bios_return.return_code) { - if (bios_return.return_code != HPWMI_RET_UNKNOWN_CMDTYPE) + if (bios_return->return_code) { + if (bios_return->return_code != HPWMI_RET_UNKNOWN_CMDTYPE) printk(KERN_WARNING PREFIX "query 0x%x returned " "error 0x%x\n", - query, bios_return.return_code); + query, bios_return->return_code); kfree(obj); - return bios_return.return_code; + return bios_return->return_code; } - memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); + if (!outsize) { + /* ignore output data */ + kfree(obj); + return 0; + } + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); + memset(buffer + actual_outsize, 0, outsize - actual_outsize); kfree(obj); return 0; } @@ -196,7 +208,7 @@ static int hp_wmi_display_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -206,7 +218,7 @@ static int hp_wmi_hddtemp_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -216,7 +228,7 @@ static int hp_wmi_als_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -226,7 +238,7 @@ static int hp_wmi_dock_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; @@ -238,7 +250,7 @@ static int hp_wmi_tablet_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return ret; @@ -252,7 +264,7 @@ static int hp_wmi_set_block(void *data, bool blocked) int ret; ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, - &query, sizeof(query)); + &query, sizeof(query), 0); if (ret) return -EINVAL; return 0; @@ -267,7 +279,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) int wireless = 0; int mask; hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, - &wireless, sizeof(wireless)); + &wireless, sizeof(wireless), + sizeof(wireless)); /* TBD: Pass error */ mask = 0x200 << (r * 8); @@ -283,7 +296,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) int wireless = 0; int mask; hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, - &wireless, sizeof(wireless)); + &wireless, sizeof(wireless), + sizeof(wireless)); /* TBD: Pass error */ mask = 0x800 << (r * 8); @@ -344,7 +358,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr, { u32 tmp = simple_strtoul(buf, NULL, 10); int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, - sizeof(tmp)); + sizeof(tmp), sizeof(tmp)); if (ret) return -EINVAL; @@ -417,6 +431,7 @@ static void hp_wmi_notify(u32 value, void *context) case HPWMI_BEZEL_BUTTON: ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, &key_code, + sizeof(key_code), sizeof(key_code)); if (ret) break; @@ -523,7 +538,7 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) int wireless = 0; err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, - sizeof(wireless)); + sizeof(wireless), sizeof(wireless)); if (err) return err; -- cgit v1.2.3 From 3fcc8400dcfa33bac9ce8adb08040f3922131268 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:23 +0200 Subject: hp-wmi: split rfkill initialization out of hp_wmi_bios_setup Split initialization of rfkill devices from hp_wmi_bios_setup() to hp_wmi_rfkill_setup(). This makes the code somewhat cleaner, especially with the future command 0x1b rfkill support. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 46 +++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 5c8ae65950d4..f6a1c37af9cc 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -532,7 +532,7 @@ static void cleanup_sysfs(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_tablet); } -static int __devinit hp_wmi_bios_setup(struct platform_device *device) +static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) { int err; int wireless = 0; @@ -542,22 +542,6 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) if (err) return err; - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_hddtemp); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_als); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_dock); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_tablet); - if (err) - goto add_sysfs_error; - if (wireless & 0x1) { wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, RFKILL_TYPE_WLAN, @@ -611,6 +595,34 @@ register_bluetooth_error: rfkill_unregister(wifi_rfkill); register_wifi_error: rfkill_destroy(wifi_rfkill); + return err; +} + +static int __devinit hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + + err = hp_wmi_rfkill_setup(device); + if (err) + return err; + + err = device_create_file(&device->dev, &dev_attr_display); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_hddtemp); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_als); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_dock); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_tablet); + if (err) + goto add_sysfs_error; + return 0; + add_sysfs_error: cleanup_sysfs(device); return err; -- cgit v1.2.3 From ef6a0dc3689fc86d3e07872ad4590e22065b2f44 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:24 +0200 Subject: hp-wmi: clear rfkill device pointers when appropriate NULLify rfkill pointers during initialization. This prevents dereference of invalid pointer in case the driver is rebound and some rfkill device isn't detected anymore. Clear them also in hp_wmi_rfkill_setup failure path so that an rfkill initialization failure doesn't need to be fatal for the whole driver. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index f6a1c37af9cc..45b2bbe6d835 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -587,14 +587,17 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) return 0; register_wwan_err: rfkill_destroy(wwan_rfkill); + wwan_rfkill = NULL; if (bluetooth_rfkill) rfkill_unregister(bluetooth_rfkill); register_bluetooth_error: rfkill_destroy(bluetooth_rfkill); + bluetooth_rfkill = NULL; if (wifi_rfkill) rfkill_unregister(wifi_rfkill); register_wifi_error: rfkill_destroy(wifi_rfkill); + wifi_rfkill = NULL; return err; } @@ -602,6 +605,11 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) { int err; + /* clear detected rfkill devices */ + wifi_rfkill = NULL; + bluetooth_rfkill = NULL; + wwan_rfkill = NULL; + err = hp_wmi_rfkill_setup(device); if (err) return err; -- cgit v1.2.3 From 7ea55dbb6b0979ca2966c1ab7d82c6454915480f Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:25 +0200 Subject: hp-wmi: make rfkill initialization failure non-fatal hp_wmi_rfkill_setup cleans up after itself now, so failing completely is no longer necessary. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 45b2bbe6d835..524ffabc2866 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -610,9 +610,7 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) bluetooth_rfkill = NULL; wwan_rfkill = NULL; - err = hp_wmi_rfkill_setup(device); - if (err) - return err; + hp_wmi_rfkill_setup(device); err = device_create_file(&device->dev, &dev_attr_display); if (err) -- cgit v1.2.3 From 0511454dbf7b3fcded4448e37f214ddbd423a157 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:26 +0200 Subject: hp-wmi: add rfkill support for wireless query 0x1b Some recent HP laptops use a new wireless query command type 0x1b. Add support for it. Tested on HP Mini 5102. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 188 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 524ffabc2866..1bc4a7539ba9 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -2,6 +2,7 @@ * HP WMI hotkeys * * Copyright (C) 2008 Red Hat + * Copyright (C) 2010, 2011 Anssi Hannula * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac @@ -51,6 +52,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_HOTKEY_QUERY 0xc +#define HPWMI_WIRELESS2_QUERY 0x1b #define PREFIX "HP WMI: " #define UNIMP "Unimplemented " @@ -95,6 +97,39 @@ enum hp_return_value { HPWMI_RET_INVALID_PARAMETERS = 0x05, }; +enum hp_wireless2_bits { + HPWMI_POWER_STATE = 0x01, + HPWMI_POWER_SOFT = 0x02, + HPWMI_POWER_BIOS = 0x04, + HPWMI_POWER_HARD = 0x08, +}; + +#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \ + != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) + +struct bios_rfkill2_device_state { + u8 radio_type; + u8 bus_type; + u16 vendor_id; + u16 product_id; + u16 subsys_vendor_id; + u16 subsys_product_id; + u8 rfkill_id; + u8 power; + u8 unknown[4]; +}; + +/* 7 devices fit into the 128 byte buffer */ +#define HPWMI_MAX_RFKILL2_DEVICES 7 + +struct bios_rfkill2_state { + u8 unknown[7]; + u8 count; + u8 pad[8]; + struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; +}; + static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, @@ -114,6 +149,15 @@ static struct rfkill *wifi_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *wwan_rfkill; +struct rfkill2_device { + u8 id; + int num; + struct rfkill *rfkill; +}; + +static int rfkill2_count; +static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; + static const struct dev_pm_ops hp_wmi_pm_ops = { .resume = hp_wmi_resume_handler, .restore = hp_wmi_resume_handler, @@ -308,6 +352,51 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) return true; } +static int hp_wmi_rfkill2_set_block(void *data, bool blocked) +{ + int rfkill_id = (int)(long)data; + char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; + + if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1, + buffer, sizeof(buffer), 0)) + return -EINVAL; + return 0; +} + +static const struct rfkill_ops hp_wmi_rfkill2_ops = { + .set_block = hp_wmi_rfkill2_set_block, +}; + +static int hp_wmi_rfkill2_refresh(void) +{ + int err, i; + struct bios_rfkill2_state state; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, + 0, sizeof(state)); + if (err) + return err; + + for (i = 0; i < rfkill2_count; i++) { + int num = rfkill2[i].num; + struct bios_rfkill2_device_state *devstate; + devstate = &state.device[num]; + + if (num >= state.count || + devstate->rfkill_id != rfkill2[i].id) { + printk(KERN_WARNING PREFIX "power configuration of " + "the wireless devices unexpectedly changed\n"); + continue; + } + + rfkill_set_states(rfkill2[i].rfkill, + IS_SWBLOCKED(devstate->power), + IS_HWBLOCKED(devstate->power)); + } + + return 0; +} + static ssize_t show_display(struct device *dev, struct device_attribute *attr, char *buf) { @@ -442,6 +531,11 @@ static void hp_wmi_notify(u32 value, void *context) key_code); break; case HPWMI_WIRELESS: + if (rfkill2_count) { + hp_wmi_rfkill2_refresh(); + break; + } + if (wifi_rfkill) rfkill_set_states(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI), @@ -601,6 +695,87 @@ register_wifi_error: return err; } +static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device) +{ + int err, i; + struct bios_rfkill2_state state; + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, + 0, sizeof(state)); + if (err) + return err; + + if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { + printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n"); + return -EINVAL; + } + + for (i = 0; i < state.count; i++) { + struct rfkill *rfkill; + enum rfkill_type type; + char *name; + switch (state.device[i].radio_type) { + case HPWMI_WIFI: + type = RFKILL_TYPE_WLAN; + name = "hp-wifi"; + break; + case HPWMI_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "hp-bluetooth"; + break; + case HPWMI_WWAN: + type = RFKILL_TYPE_WWAN; + name = "hp-wwan"; + break; + default: + printk(KERN_WARNING PREFIX "unknown device type 0x%x\n", + state.device[i].radio_type); + continue; + } + + if (!state.device[i].vendor_id) { + printk(KERN_WARNING PREFIX "zero device %d while %d " + "reported\n", i, state.count); + continue; + } + + rfkill = rfkill_alloc(name, &device->dev, type, + &hp_wmi_rfkill2_ops, (void *)(long)i); + if (!rfkill) { + err = -ENOMEM; + goto fail; + } + + rfkill2[rfkill2_count].id = state.device[i].rfkill_id; + rfkill2[rfkill2_count].num = i; + rfkill2[rfkill2_count].rfkill = rfkill; + + rfkill_init_sw_state(rfkill, + IS_SWBLOCKED(state.device[i].power)); + rfkill_set_hw_state(rfkill, + IS_HWBLOCKED(state.device[i].power)); + + if (!(state.device[i].power & HPWMI_POWER_BIOS)) + printk(KERN_INFO PREFIX "device %s blocked by BIOS\n", + name); + + err = rfkill_register(rfkill); + if (err) { + rfkill_destroy(rfkill); + goto fail; + } + + rfkill2_count++; + } + + return 0; +fail: + for (; rfkill2_count > 0; rfkill2_count--) { + rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); + rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); + } + return err; +} + static int __devinit hp_wmi_bios_setup(struct platform_device *device) { int err; @@ -609,8 +784,10 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) wifi_rfkill = NULL; bluetooth_rfkill = NULL; wwan_rfkill = NULL; + rfkill2_count = 0; - hp_wmi_rfkill_setup(device); + if (hp_wmi_rfkill_setup(device)) + hp_wmi_rfkill2_setup(device); err = device_create_file(&device->dev, &dev_attr_display); if (err) @@ -636,8 +813,14 @@ add_sysfs_error: static int __exit hp_wmi_bios_remove(struct platform_device *device) { + int i; cleanup_sysfs(device); + for (i = 0; i < rfkill2_count; i++) { + rfkill_unregister(rfkill2[i].rfkill); + rfkill_destroy(rfkill2[i].rfkill); + } + if (wifi_rfkill) { rfkill_unregister(wifi_rfkill); rfkill_destroy(wifi_rfkill); @@ -670,6 +853,9 @@ static int hp_wmi_resume_handler(struct device *device) input_sync(hp_wmi_input_dev); } + if (rfkill2_count) + hp_wmi_rfkill2_refresh(); + if (wifi_rfkill) rfkill_set_states(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI), -- cgit v1.2.3 From 2466a492698fe3b15c32658c223a7de381025b93 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 14 Jan 2011 15:54:39 -0600 Subject: thinkpad_acpi: Always report scancodes for hotkeys Some thinkpad hotkeys report key codes like KEY_FN_F8 when something like KEY_VOLUMEDOWN is desired. Always provide the scan codes in addition to the key codes to assist with debugging these issues. Also send the scan code before the key code to match what other drivers do, as some userspace utilities expect this ordering. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index dd599585c6a9..eb9922385ef8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2275,16 +2275,12 @@ static void tpacpi_input_send_key(const unsigned int scancode) if (keycode != KEY_RESERVED) { mutex_lock(&tpacpi_inputdev_send_mutex); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 1); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 0); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); mutex_unlock(&tpacpi_inputdev_send_mutex); -- cgit v1.2.3 From c80297acbb55237adc128b3b2e0b74f0cf34764e Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Sun, 20 Feb 2011 23:05:43 +0530 Subject: medfield: Add Thermal Driver This is the basic thermal sensor driver for Intel MID platform using the Medfield chipset. It plugs in via the thermal drivers and provides sensor readings for the device sensors. Signed-off-by: Durgadoss R Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 7 + drivers/platform/x86/Makefile | 3 +- drivers/platform/x86/intel_mid_thermal.c | 575 +++++++++++++++++++++++++++++++ 3 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/intel_mid_thermal.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 53f85037cc3c..906e1fb421e7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -619,6 +619,13 @@ config INTEL_MID_POWER_BUTTON If unsure, say N. +config INTEL_MFLD_THERMAL + tristate "Thermal driver for Intel Medfield platform" + depends on INTEL_SCU_IPC && THERMAL + help + Say Y here to enable thermal driver support for the Intel Medfield + platform. + config RAR_REGISTER bool "Restricted Access Region Register Driver" depends on PCI && X86_MRST diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 08bb8e682141..951b5163c7e1 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -29,7 +29,8 @@ 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_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o +obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.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/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c new file mode 100644 index 000000000000..1215c3096868 --- /dev/null +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -0,0 +1,575 @@ +/* + * intel_mid_thermal.c - Intel MID platform thermal driver + * + * Copyright (C) 2011 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Author: Durgadoss R + */ + +#define pr_fmt(fmt) "intel_mid_thermal: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Number of thermal sensors */ +#define MSIC_THERMAL_SENSORS 4 + +/* ADC1 - thermal registers */ +#define MSIC_THERM_ADC1CNTL1 0x1C0 +#define MSIC_ADC_ENBL 0x10 +#define MSIC_ADC_START 0x08 + +#define MSIC_THERM_ADC1CNTL3 0x1C2 +#define MSIC_ADCTHERM_ENBL 0x04 +#define MSIC_ADCRRDATA_ENBL 0x05 +#define MSIC_CHANL_MASK_VAL 0x0F + +#define MSIC_STOPBIT_MASK 16 +#define MSIC_ADCTHERM_MASK 4 +#define ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS) + +/* ADC channel code values */ +#define SKIN_SENSOR0_CODE 0x08 +#define SKIN_SENSOR1_CODE 0x09 +#define SYS_SENSOR_CODE 0x0A +#define MSIC_DIE_SENSOR_CODE 0x03 + +#define SKIN_THERM_SENSOR0 0 +#define SKIN_THERM_SENSOR1 1 +#define SYS_THERM_SENSOR2 2 +#define MSIC_DIE_THERM_SENSOR3 3 + +/* ADC code range */ +#define ADC_MAX 977 +#define ADC_MIN 162 +#define ADC_VAL0C 887 +#define ADC_VAL20C 720 +#define ADC_VAL40C 508 +#define ADC_VAL60C 315 + +/* ADC base addresses */ +#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ + +/* MSIC die attributes */ +#define MSIC_DIE_ADC_MIN 488 +#define MSIC_DIE_ADC_MAX 1004 + +/* This holds the address of the first free ADC channel, + * among the 15 channels + */ +static int channel_index; + +struct platform_info { + struct platform_device *pdev; + struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS]; +}; + +struct thermal_device_info { + unsigned int chnl_addr; + int direct; + /* This holds the current temperature in millidegree celsius */ + long curr_temp; +}; + +/** + * to_msic_die_temp - converts adc_val to msic_die temperature + * @adc_val: ADC value to be converted + * + * Can sleep + */ +static int to_msic_die_temp(uint16_t adc_val) +{ + return (368 * (adc_val) / 1000) - 220; +} + +/** + * is_valid_adc - checks whether the adc code is within the defined range + * @min: minimum value for the sensor + * @max: maximum value for the sensor + * + * Can sleep + */ +static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max) +{ + return (adc_val >= min) && (adc_val <= max); +} + +/** + * adc_to_temp - converts the ADC code to temperature in C + * @direct: true if ths channel is direct index + * @adc_val: the adc_val that needs to be converted + * @tp: temperature return value + * + * Linear approximation is used to covert the skin adc value into temperature. + * This technique is used to avoid very long look-up table to get + * the appropriate temp value from ADC value. + * The adc code vs sensor temp curve is split into five parts + * to achieve very close approximate temp value with less than + * 0.5C error + */ +static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp) +{ + int temp; + + /* Direct conversion for die temperature */ + if (direct) { + if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) { + *tp = to_msic_die_temp(adc_val) * 1000; + return 0; + } + return -ERANGE; + } + + if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX)) + return -ERANGE; + + /* Linear approximation for skin temperature */ + if (adc_val > ADC_VAL0C) + temp = 177 - (adc_val/5); + else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C)) + temp = 111 - (adc_val/8); + else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C)) + temp = 92 - (adc_val/10); + else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C)) + temp = 91 - (adc_val/10); + else + temp = 112 - (adc_val/6); + + /* Convert temperature in celsius to milli degree celsius */ + *tp = temp * 1000; + return 0; +} + +/** + * mid_read_temp - read sensors for temperature + * @temp: holds the current temperature for the sensor after reading + * + * reads the adc_code from the channel and converts it to real + * temperature. The converted value is stored in temp. + * + * Can sleep + */ +static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) +{ + struct thermal_device_info *td_info = tzd->devdata; + uint16_t adc_val, addr; + uint8_t data = 0; + int ret; + unsigned long curr_temp; + + + addr = td_info->chnl_addr; + + /* Enable the msic for conversion before reading */ + ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); + if (ret) + return ret; + + /* Re-toggle the RRDATARD bit (temporary workaround) */ + ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL); + if (ret) + return ret; + + /* Read the higher bits of data */ + ret = intel_scu_ipc_ioread8(addr, &data); + if (ret) + return ret; + + /* Shift bits to accomodate the lower two data bits */ + adc_val = (data << 2); + addr++; + + ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */ + if (ret) + return ret; + + /* Adding lower two bits to the higher bits */ + data &= 03; + adc_val += data; + + /* Convert ADC value to temperature */ + ret = adc_to_temp(td_info->direct, adc_val, &curr_temp); + if (ret == 0) + *temp = td_info->curr_temp = curr_temp; + return ret; +} + +/** + * configure_adc - enables/disables the ADC for conversion + * @val: zero: disables the ADC non-zero:enables the ADC + * + * Enable/Disable the ADC depending on the argument + * + * Can sleep + */ +static int configure_adc(int val) +{ + int ret; + uint8_t data; + + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + if (ret) + return ret; + + if (val) { + /* Enable and start the ADC */ + data |= (MSIC_ADC_ENBL | MSIC_ADC_START); + } else { + /* Just stop the ADC */ + data &= (~MSIC_ADC_START); + } + + return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data); +} + +/** + * set_up_therm_channel - enable thermal channel for conversion + * @base_addr: index of free msic ADC channel + * + * Enable all the three channels for conversion + * + * Can sleep + */ +static int set_up_therm_channel(u16 base_addr) +{ + int ret; + + /* Enable all the sensor channels */ + ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE); + if (ret) + return ret; + + ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE); + if (ret) + return ret; + + ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE); + if (ret) + return ret; + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + ret = intel_scu_ipc_iowrite8(base_addr + 3, + (MSIC_DIE_SENSOR_CODE | 0x10)); + if (ret) + return ret; + + /* Enable ADC and start it */ + return configure_adc(1); +} + +/** + * reset_stopbit - sets the stop bit to 0 on the given channel + * @addr: address of the channel + * + * Can sleep + */ +static int reset_stopbit(uint16_t addr) +{ + int ret; + uint8_t data; + ret = intel_scu_ipc_ioread8(addr, &data); + if (ret) + return ret; + /* Set the stop bit to zero */ + return intel_scu_ipc_iowrite8(addr, (data & 0xEF)); +} + +/** + * find_free_channel - finds an empty channel for conversion + * + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * + * FIXME: Ultimately the channel allocator will move into the intel_scu_ipc + * code. + */ +static int find_free_channel(void) +{ + int ret; + int i; + uint8_t data; + + /* check whether ADC is enabled */ + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + if (ret) + return ret; + + if ((data & MSIC_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < ADC_CHANLS_MAX; i++) { + ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data); + if (ret) + return ret; + + if (data & MSIC_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/** + * mid_initialize_adc - initializing the ADC + * @dev: our device structure + * + * Initialize the ADC for reading thermistor values. Can sleep. + */ +static int mid_initialize_adc(struct device *dev) +{ + u8 data; + u16 base_addr; + int ret; + + /* + * Ensure that adctherm is disabled before we + * initialize the ADC + */ + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data); + if (ret) + return ret; + + if (data & MSIC_ADCTHERM_MASK) + dev_warn(dev, "ADCTHERM already set"); + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(); + if (channel_index < 0) { + dev_err(dev, "No free ADC channels"); + return channel_index; + } + + base_addr = ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + ret = reset_stopbit(base_addr); + if (ret) + return ret; + + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + ret = set_up_therm_channel(base_addr); + if (ret) { + dev_err(dev, "unable to enable ADC"); + return ret; + } + dev_dbg(dev, "ADC initialization successful"); + return ret; +} + +/** + * initialize_sensor - sets default temp and timer ranges + * @index: index of the sensor + * + * Context: can sleep + */ +static struct thermal_device_info *initialize_sensor(int index) +{ + struct thermal_device_info *td_info = + kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL); + + if (!td_info) + return NULL; + + /* Set the base addr of the channel for this sensor */ + td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index); + /* Sensor 3 is direct conversion */ + if (index == 3) + td_info->direct = 1; + return td_info; +} + +/** + * mid_thermal_resume - resume routine + * @pdev: platform device structure + * + * mid thermal resume: re-initializes the adc. Can sleep. + */ +static int mid_thermal_resume(struct platform_device *pdev) +{ + return mid_initialize_adc(&pdev->dev); +} + +/** + * mid_thermal_suspend - suspend routine + * @pdev: platform device structure + * + * mid thermal suspend implements the suspend functionality + * by stopping the ADC. Can sleep. + */ +static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + /* + * This just stops the ADC and does not disable it. + * temporary workaround until we have a generic ADC driver. + * If 0 is passed, it disables the ADC. + */ + return configure_adc(0); +} + +/** + * read_curr_temp - reads the current temperature and stores in temp + * @temp: holds the current temperature value after reading + * + * Can sleep + */ +static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) +{ + WARN_ON(tzd == NULL); + return mid_read_temp(tzd, temp); +} + +/* Can't be const */ +static struct thermal_zone_device_ops tzd_ops = { + .get_temp = read_curr_temp, +}; + + +/** + * mid_thermal_probe - mfld thermal initialize + * @pdev: platform device structure + * + * mid thermal probe initializes the hardware and registers + * all the sensors with the generic thermal framework. Can sleep. + */ +static int mid_thermal_probe(struct platform_device *pdev) +{ + static char *name[MSIC_THERMAL_SENSORS] = { + "skin0", "skin1", "sys", "msicdie" + }; + + int ret; + int i; + struct platform_info *pinfo; + + pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + /* Initializing the hardware */ + ret = mid_initialize_adc(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "ADC init failed"); + kfree(pinfo); + return ret; + } + + /* Register each sensor with the generic thermal framework*/ + for (i = 0; i < MSIC_THERMAL_SENSORS; i++) { + pinfo->tzd[i] = thermal_zone_device_register(name[i], + 0, initialize_sensor(i), + &tzd_ops, 0, 0, 0, 0); + if (IS_ERR(pinfo->tzd[i])) + goto reg_fail; + } + + pinfo->pdev = pdev; + platform_set_drvdata(pdev, pinfo); + return 0; + +reg_fail: + ret = PTR_ERR(pinfo->tzd[i]); + while (--i >= 0) + thermal_zone_device_unregister(pinfo->tzd[i]); + configure_adc(0); + kfree(pinfo); + return ret; +} + +/** + * mid_thermal_remove - mfld thermal finalize + * @dev: platform device structure + * + * MLFD thermal remove unregisters all the sensors from the generic + * thermal framework. Can sleep. + */ +static int mid_thermal_remove(struct platform_device *pdev) +{ + int i; + struct platform_info *pinfo = platform_get_drvdata(pdev); + + for (i = 0; i < MSIC_THERMAL_SENSORS; i++) + thermal_zone_device_unregister(pinfo->tzd[i]); + + platform_set_drvdata(pdev, NULL); + + /* Stop the ADC */ + return configure_adc(0); +} + +/********************************************************************* + * Driver initialisation and finalization + *********************************************************************/ + +#define DRIVER_NAME "msic_sensor" + +static const struct platform_device_id therm_id_table[] = { + { DRIVER_NAME, 1 }, +}; + +static struct platform_driver mid_thermal_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mid_thermal_probe, + .suspend = mid_thermal_suspend, + .resume = mid_thermal_resume, + .remove = __devexit_p(mid_thermal_remove), + .id_table = therm_id_table, +}; + +static int __init mid_thermal_module_init(void) +{ + return platform_driver_register(&mid_thermal_driver); +} + +static void __exit mid_thermal_module_exit(void) +{ + platform_driver_unregister(&mid_thermal_driver); +} + +module_init(mid_thermal_module_init); +module_exit(mid_thermal_module_exit); + +MODULE_AUTHOR("Durgadoss R "); +MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3