From 8acb025085aa88c41063bfa0f2c3b4d0a3f2ef11 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:28 -0700 Subject: ACPI: ibm_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. The patch keeps the procfs brightness handling for backward compatibility. Add two generic functions brightness_get and brightness_set to be used both by the procfs related and the sysfs related methods. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 1 + drivers/acpi/ibm_acpi.c | 75 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0f9d4be7ed75..bc58a3b18e06 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -200,6 +200,7 @@ config ACPI_ASUS config ACPI_IBM tristate "IBM ThinkPad Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 003a9876c968..cd8722e4157e 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include @@ -243,6 +244,8 @@ struct ibm_struct { static struct proc_dir_entry *proc_dir = NULL; +static struct backlight_device *ibm_backlight_device; + #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) @@ -1381,12 +1384,22 @@ static int ecdump_write(char *buf) static int brightness_offset = 0x31; +static int brightness_get(struct backlight_device *bd) +{ + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; + + level &= 0x7; + return level; +} + static int brightness_read(char *p) { int len = 0; - u8 level; + int level; - if (!acpi_ec_read(brightness_offset, &level)) { + if ((level = brightness_get(NULL)) < 0) { len += sprintf(p + len, "level:\t\tunreadable\n"); } else { len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); @@ -1401,16 +1414,34 @@ static int brightness_read(char *p) #define BRIGHTNESS_UP 4 #define BRIGHTNESS_DOWN 5 -static int brightness_write(char *buf) +static int brightness_set(int value) { int cmos_cmd, inc, i; - u8 level; + int current_value = brightness_get(NULL); + + value &= 7; + + cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; + inc = value > current_value ? 1 : -1; + for (i = current_value; i != value; i += inc) { + if (!cmos_eval(cmos_cmd)) + return -EIO; + if (!acpi_ec_write(brightness_offset, i + inc)) + return -EIO; + } + + return 0; +} + +static int brightness_write(char *buf) +{ + int level; int new_level; char *cmd; while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; + if ((level = brightness_get(NULL)) < 0) + return level; level &= 7; if (strlencmp(cmd, "up") == 0) { @@ -1423,19 +1454,17 @@ static int brightness_write(char *buf) } else return -EINVAL; - cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; - inc = new_level > level ? 1 : -1; - for (i = level; i != new_level; i += inc) { - if (!cmos_eval(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; - } + brightness_set(new_level); } return 0; } +static int brightness_update_status(struct backlight_device *bd) +{ + return brightness_set(bd->props->brightness); +} + static int volume_offset = 0x30; static int volume_read(char *p) @@ -1963,10 +1992,20 @@ IBM_PARAM(brightness); IBM_PARAM(volume); IBM_PARAM(fan); +static struct backlight_properties ibm_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, +}; + static void acpi_ibm_exit(void) { int i; + if (ibm_backlight_device) + backlight_device_unregister(ibm_backlight_device); + for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) ibm_exit(&ibms[i]); @@ -2034,6 +2073,14 @@ static int __init acpi_ibm_init(void) } } + ibm_backlight_device = backlight_device_register("ibm", NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register ibm backlight device\n"); + ibm_backlight_device = NULL; + acpi_ibm_exit(); + } + return 0; } -- cgit v1.2.2 From 2039a6eb72d4b5d0dd71de5c4dff5db129848c44 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:29 -0700 Subject: ACPI: asus_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. Keep the procfs brightness handling for backward compatibility. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 1 + drivers/acpi/asus_acpi.c | 62 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index bc58a3b18e06..9b64c4e6c63e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -172,6 +172,7 @@ config ACPI_NUMA config ACPI_ASUS tristate "ASUS/Medion Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This driver provides support for extra features of ACPI-compatible ASUS laptops. As some of Medion laptops are made by ASUS, it may also diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index c7ac9297a204..bf7bc25e680e 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,8 @@ static struct model_data model_conf[END_MODEL] = { /* procdir we use */ static struct proc_dir_entry *asus_proc_dir; +static struct backlight_device *asus_backlight_device; + /* * This header is made available to allow proper configuration given model, * revision number , ... this info cannot go in struct asus_hotk because it is @@ -779,7 +782,7 @@ proc_write_lcd(struct file *file, const char __user * buffer, return rv; } -static int read_brightness(void) +static int read_brightness(struct backlight_device *bd) { int value; @@ -801,9 +804,10 @@ static int read_brightness(void) /* * Change the brightness level */ -static void set_brightness(int value) +static int set_brightness(int value) { acpi_status status = 0; + int ret = 0; /* SPLV laptop */ if (hotk->methods->brightness_set) { @@ -811,11 +815,12 @@ static void set_brightness(int value) value, NULL)) printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); - return; + ret = -EIO; + goto out; } /* No SPLV method if we are here, act as appropriate */ - value -= read_brightness(); + value -= read_brightness(NULL); while (value != 0) { status = acpi_evaluate_object(NULL, (value > 0) ? hotk->methods->brightness_up : @@ -825,15 +830,22 @@ static void set_brightness(int value) if (ACPI_FAILURE(status)) printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); + ret = -EIO; } - return; +out: + return ret; +} + +static int set_brightness_status(struct backlight_device *bd) +{ + return set_brightness(bd->props->brightness); } static int proc_read_brn(char *page, char **start, off_t off, int count, int *eof, void *data) { - return sprintf(page, "%d\n", read_brightness()); + return sprintf(page, "%d\n", read_brightness(NULL)); } static int @@ -1333,6 +1345,26 @@ static int asus_hotk_remove(struct acpi_device *device, int type) return 0; } +static struct backlight_properties asus_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = read_brightness, + .update_status = set_brightness_status, + .max_brightness = 15, +}; + +static void __exit asus_acpi_exit(void) +{ + if (asus_backlight_device) + backlight_device_unregister(asus_backlight_device); + + acpi_bus_unregister_driver(&asus_hotk_driver); + remove_proc_entry(PROC_ASUS, acpi_root_dir); + + kfree(asus_info); + + return; +} + static int __init asus_acpi_init(void) { int result; @@ -1370,17 +1402,15 @@ static int __init asus_acpi_init(void) return result; } - return 0; -} - -static void __exit asus_acpi_exit(void) -{ - acpi_bus_unregister_driver(&asus_hotk_driver); - remove_proc_entry(PROC_ASUS, acpi_root_dir); - - kfree(asus_info); + asus_backlight_device = backlight_device_register("asus", NULL, + &asus_backlight_data); + if (IS_ERR(asus_backlight_device)) { + printk(KERN_ERR "Could not register asus backlight device\n"); + asus_backlight_device = NULL; + asus_acpi_exit(); + } - return; + return 0; } module_init(asus_acpi_init); -- cgit v1.2.2 From c92635572489b810d03acdf03f61bf6dd1af5433 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Fri, 20 Oct 2006 14:30:29 -0700 Subject: ACPI: toshiba_acpi: Add support for the generic backlight device Add support for the generic backlight interface below /sys/class/backlight. Keep the procfs brightness handling for backward compatibility. To achive this, add two generic functions get_lcd and set_lcd to be used both by the procfs related and the sysfs related methods. [apw@shadowen.org: backlight users need to select BACKLIGHT_CLASS_DEVICE] Signed-off-by: Holger Macht Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 1 + drivers/acpi/toshiba_acpi.c | 88 +++++++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 9b64c4e6c63e..59f9def21720 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -227,6 +227,7 @@ config ACPI_IBM_DOCK config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 + select BACKLIGHT_CLASS_DEVICE ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index 7fe0b7ae9733..2f35f891593f 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c @@ -41,6 +41,8 @@ #include #include #include +#include + #include #include @@ -210,6 +212,7 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) } static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; +static struct backlight_device *toshiba_backlight_device; static int force_fan; static int last_key_event; static int key_event_valid; @@ -271,14 +274,23 @@ dispatch_write(struct file *file, const char __user * buffer, return result; } -static char *read_lcd(char *p) +static int get_lcd(struct backlight_device *bd) { u32 hci_result; u32 value; hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); if (hci_result == HCI_SUCCESS) { - value = value >> HCI_LCD_BRIGHTNESS_SHIFT; + return (value >> HCI_LCD_BRIGHTNESS_SHIFT); + } else + return -EFAULT; +} + +static char *read_lcd(char *p) +{ + int value = get_lcd(NULL); + + if (value >= 0) { p += sprintf(p, "brightness: %d\n", value); p += sprintf(p, "brightness_levels: %d\n", HCI_LCD_BRIGHTNESS_LEVELS); @@ -289,22 +301,34 @@ static char *read_lcd(char *p) return p; } +static int set_lcd(int value) +{ + u32 hci_result; + + value = value << HCI_LCD_BRIGHTNESS_SHIFT; + hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); + if (hci_result != HCI_SUCCESS) + return -EFAULT; + + return 0; +} + +static int set_lcd_status(struct backlight_device *bd) +{ + return set_lcd(bd->props->brightness); +} + static unsigned long write_lcd(const char *buffer, unsigned long count) { int value; - u32 hci_result; + int ret = count; if (sscanf(buffer, " brightness : %i", &value) == 1 && - value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - value = value << HCI_LCD_BRIGHTNESS_SHIFT; - hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); - if (hci_result != HCI_SUCCESS) - return -EFAULT; - } else { - return -EINVAL; - } - - return count; + value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) + ret = set_lcd(value); + else + ret = -EINVAL; + return ret; } static char *read_video(char *p) @@ -506,6 +530,26 @@ static acpi_status __exit remove_device(void) return AE_OK; } +static struct backlight_properties toshiba_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = get_lcd, + .update_status = set_lcd_status, + .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1, +}; + +static void __exit toshiba_acpi_exit(void) +{ + if (toshiba_backlight_device) + backlight_device_unregister(toshiba_backlight_device); + + remove_device(); + + if (toshiba_proc_dir) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + + return; +} + static int __init toshiba_acpi_init(void) { acpi_status status = AE_OK; @@ -546,17 +590,15 @@ static int __init toshiba_acpi_init(void) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } - return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; -} - -static void __exit toshiba_acpi_exit(void) -{ - remove_device(); - - if (toshiba_proc_dir) - remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + toshiba_backlight_device = backlight_device_register("toshiba", NULL, + &toshiba_backlight_data); + if (IS_ERR(toshiba_backlight_device)) { + printk(KERN_ERR "Could not register toshiba backlight device\n"); + toshiba_backlight_device = NULL; + toshiba_acpi_exit(); + } - return; + return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; } module_init(toshiba_acpi_init); -- cgit v1.2.2 From 3dfd35cd214f7874c4917dfedff81f107d845c15 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 10:32:32 -0200 Subject: ACPI: ibm-acpi: do not use / in driver names ibm-acpi uses sub-device names like ibm/hotkey, which get in the way of a sysfs conversion. Fix it to use ibm_hotkey instead. Thanks to Zhang Rui for noticing this. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index cd8722e4157e..ce69f7592a22 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1854,7 +1854,7 @@ static int __init register_driver(struct ibm_struct *ibm) } memset(ibm->driver, 0, sizeof(struct acpi_driver)); - sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); + sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); ibm->driver->ids = ibm->hid; ibm->driver->ops.add = &ibm_device_add; -- cgit v1.2.2 From 8d29726434697a5ed08d4e0dfba9399a098922f4 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:07 -0200 Subject: ACPI: ibm-acpi: trivial Lindent cleanups This patch just makes drives/acpi/ibm-acpi.c Lindent-clean, as requested by Len Brown. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ce69f7592a22..b81a95d2067c 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -584,8 +584,7 @@ static int wan_status(void) { int status; - if (!wan_supported || - !acpi_evalf(hkey_handle, &status, "GWAN", "d")) + if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) status = 0; return status; @@ -910,6 +909,7 @@ static int _sta(acpi_handle handle) return status; } + #ifdef CONFIG_ACPI_IBM_DOCK #define dock_docked() (_sta(dock_handle) & 1) @@ -1386,12 +1386,12 @@ static int brightness_offset = 0x31; static int brightness_get(struct backlight_device *bd) { - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; - level &= 0x7; - return level; + level &= 0x7; + return level; } static int brightness_read(char *p) @@ -1993,10 +1993,10 @@ IBM_PARAM(volume); IBM_PARAM(fan); static struct backlight_properties ibm_backlight_data = { - .owner = THIS_MODULE, - .get_brightness = brightness_get, - .update_status = brightness_update_status, - .max_brightness = 7, + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, }; static void acpi_ibm_exit(void) @@ -2075,7 +2075,7 @@ static int __init acpi_ibm_init(void) ibm_backlight_device = backlight_device_register("ibm", NULL, &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { + if (IS_ERR(ibm_backlight_device)) { printk(IBM_ERR "Could not register ibm backlight device\n"); ibm_backlight_device = NULL; acpi_ibm_exit(); -- cgit v1.2.2 From a26f878abcd0491906b5bbac8dd174f27019e907 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:08 -0200 Subject: ACPI: ibm-acpi: Use a enum to select the thermal sensor reading strategy This patch consolidades all decisions regarding the strategy to be used to read thinkpad thermal sensors into a single enum, and refactors the thermal sensor reading code to use a much more readable (and easier to extend) switch() construct, in a separate function. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 90 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index b81a95d2067c..baf9492f0a79 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -217,6 +217,17 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ #define IBM_HKEY_HID "IBM0068" #define IBM_PCI_HID "PNP0A03" +enum thermal_access_mode { + IBMACPI_THERMAL_NONE = 0, /* No thermal support */ + IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ + IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ +}; + +#define IBMACPI_MAX_THERMAL_SENSORS 8 /* Max thermal sensors supported */ +struct ibm_thermal_sensors_struct { + s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; +}; + struct ibm_struct { char *name; char param[32]; @@ -1275,50 +1286,79 @@ static int acpi_ec_write(int i, u8 v) return 1; } -static int thermal_tmp_supported; -static int thermal_updt_supported; +static enum thermal_access_mode thermal_read_mode; static int thermal_init(void) { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - /* 600e/x, 770e, 770x */ - thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv"); + if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) { + if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; + } else { + /* Standard ACPI TMPx access, max 8 sensors */ + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } + } else { + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + thermal_read_mode = IBMACPI_THERMAL_NONE; + } return 0; } -static int thermal_read(char *p) +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { - int len = 0; + int i, t; + char tmpi[] = "TMPi"; - if (!thermal_tmp_supported) - len += sprintf(p + len, "temperatures:\tnot supported\n"); - else { - int i, t; - char tmpi[] = "TMPi"; - s8 tmp[8]; + if (!s) + return -EINVAL; - if (thermal_updt_supported) - if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + switch (thermal_read_mode) { + case IBMACPI_THERMAL_ACPI_UPDT: + if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + return -EIO; + for (i = 0; i < 8; i++) { + tmpi[3] = '0' + i; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) return -EIO; + s->temp[i] = (t - 2732) * 100; + } + return 8; + case IBMACPI_THERMAL_ACPI_TMP07: for (i = 0; i < 8; i++) { tmpi[3] = '0' + i; if (!acpi_evalf(ec_handle, &t, tmpi, "d")) return -EIO; - if (thermal_updt_supported) - tmp[i] = (t - 2732 + 5) / 10; - else - tmp[i] = t; + s->temp[i] = t * 1000; } + return 8; - len += sprintf(p + len, - "temperatures:\t%d %d %d %d %d %d %d %d\n", - tmp[0], tmp[1], tmp[2], tmp[3], - tmp[4], tmp[5], tmp[6], tmp[7]); + case IBMACPI_THERMAL_NONE: + default: + return 0; } +} + +static int thermal_read(char *p) +{ + int len = 0; + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (unlikely(n < 0)) + return n; + + len += sprintf(p + len, "temperatures:\t"); + + if (n > 0) { + for (i = 0; i < (n - 1); i++) + len += sprintf(p + len, "%d ", t.temp[i] / 1000); + len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + } else + len += sprintf(p + len, "not supported\n"); return len; } -- cgit v1.2.2 From 60eb0b35a9cc3400251cb4028d100e350649cf8a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:08 -0200 Subject: ACPI: ibm-acpi: Implement direct-ec-access thermal reading modes for up to 16 sensors This patch extends ibm-acpi to support reading thermal sensors directly through ACPI EC register access. It uses a DMI match to detect ThinkPads with a new-style embedded controller, that are known to have forward- compatible register maps and use 0x00 to fill in non-used registers and export thermal sensors at EC offsets 0x78-7F and 0xC0-C7. Direct ACPI EC register access is implemented for 8-sensor and 16-sensor new-style ThinkPad controller firmwares as an experimental feature. The code does some limited sanity checks on the temperatures read through EC access, and will default to the old ACPI TMP0-7 mode if anything is amiss. Userspace ABI is not changed for 8 sensors, but /proc/acpi/ibm/thermal is extended for 16 sensors if the firmware supports 16 sensors. A documentation update is also provided. The information about the ThinkPad register map was determined by studying ibm-acpi "ecdump" output from various ThinkPad models, submitted by subscribers of the linux-thinkpad mailinglist. Futher information was gathered from the DSDT tables, as they describe the EC register map in recent ThinkPads. DSDT source shows that TMP0-7 access and direct register access are actually the same thing on these firmwares, but unfortunately IBM never did update their DSDT EC register map to export TMP8-TMP15 for the second range of sensors. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index baf9492f0a79..1703c617c0e8 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -221,13 +222,17 @@ enum thermal_access_mode { IBMACPI_THERMAL_NONE = 0, /* No thermal support */ IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ + IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ }; -#define IBMACPI_MAX_THERMAL_SENSORS 8 /* Max thermal sensors supported */ +#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +static int ibm_thinkpad_ec_found; + struct ibm_struct { char *name; char param[32]; @@ -1290,7 +1295,52 @@ static enum thermal_access_mode thermal_read_mode; static int thermal_init(void) { - if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) { + u8 t, ta1, ta2; + int i; + int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (ibm_thinkpad_ec_found && experimental) { + /* + * Direct EC access mode: sensors at registers + * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for + * non-implemented, thermal sensors return 0x80 when + * not available + */ + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (likely(acpi_ec_read(0x78 + i, &t))) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (likely(acpi_ec_read(0xC0 + i, &t))) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "falling back to ACPI TMPx access mode\n"); + thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "disabling thermal sensors access\n"); + thermal_read_mode = IBMACPI_THERMAL_NONE; + } + } else { + thermal_read_mode = + (ta2 != 0) ? + IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; + } + } else if (acpi_tmp7) { if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { /* 600e/x, 770e, 770x */ thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; @@ -1309,12 +1359,30 @@ static int thermal_init(void) static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { int i, t; + s8 tmp; char tmpi[] = "TMPi"; if (!s) return -EINVAL; switch (thermal_read_mode) { +#if IBMACPI_MAX_THERMAL_SENSORS >= 16 + case IBMACPI_THERMAL_TPEC_16: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0xC0 + i, &tmp)) + return -EIO; + s->temp[i + 8] = tmp * 1000; + } + /* fallthrough */ +#endif + case IBMACPI_THERMAL_TPEC_8: + for (i = 0; i < 8; i++) { + if (!acpi_ec_read(0x78 + i, &tmp)) + return -EIO; + s->temp[i] = tmp * 1000; + } + return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; + case IBMACPI_THERMAL_ACPI_UPDT: if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) return -EIO; @@ -2052,6 +2120,24 @@ static void acpi_ibm_exit(void) remove_proc_entry(IBM_DIR, acpi_root_dir); } +static int __init check_dmi_for_ec(void) +{ + struct dmi_device *dev = NULL; + + /* + * ThinkPad T23 or newer, A31 or newer, R50e or newer, + * X32 or newer, all Z series; Some models must have an + * up-to-date BIOS or they will not be detected. + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + */ + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (strstr(dev->name, "IBM ThinkPad Embedded Controller")) + return 1; + } + return 0; +} + static int __init acpi_ibm_init(void) { int ret, i; @@ -2071,6 +2157,9 @@ static int __init acpi_ibm_init(void) return -ENODEV; } + /* Models with newer firmware report the EC in DMI */ + ibm_thinkpad_ec_found = check_dmi_for_ec(); + /* these handles are not required */ IBM_HANDLE_INIT(vid); IBM_HANDLE_INIT(vid2); -- cgit v1.2.2 From 69ba91cbd6d79aa197adbdd10a44e71c84044b44 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:09 -0200 Subject: ACPI: ibm-acpi: prepare to cleanup fan_read and fan_write This patch lays some groundwork for a fan_read and fan_write cleanup in the next patches. To do so, it provides a new fan_init initializer, and also some constants (through enums). Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 1703c617c0e8..7d0bd61eebc7 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -231,6 +231,31 @@ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +enum fan_status_access_mode { + IBMACPI_FAN_NONE = 0, /* No fan status or control */ + IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ +}; + +enum fan_control_access_mode { + IBMACPI_FAN_WR_NONE = 0, /* No fan control */ + IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ + IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ +}; + +enum fan_control_commands { + IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ + IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ +}; + +enum { /* Fan control constants */ + fan_status_offset = 0x2f, /* EC register 0x2f */ + fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) + * 0x84 must be read before 0x85 */ +}; + static int ibm_thinkpad_ec_found; struct ibm_struct { @@ -1659,8 +1684,59 @@ static int volume_write(char *buf) return 0; } -static int fan_status_offset = 0x2f; -static int fan_rpm_offset = 0x84; +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; + +static int fan_init(void) +{ + u8 status; + + fan_status_access_mode = IBMACPI_FAN_NONE; + fan_control_access_mode = IBMACPI_FAN_WR_NONE; + fan_control_commands = 0; + + if (gfan_handle) { + /* 570, 600e/x, 770e, 770x */ + fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; + } else { + /* all other ThinkPads: note that even old-style + * ThinkPad ECs supports the fan control register */ + if (likely(acpi_ec_read(fan_status_offset, &status))) { + fan_status_access_mode = IBMACPI_FAN_RD_TPEC; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "fan status and control unavailable\n"); + return 0; + } + } + + if (sfan_handle) { + /* 570, 770x-JL */ + fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; + fan_control_commands |= IBMACPI_FAN_CMD_LEVEL; + } else { + if (!gfan_handle) { + /* gfan without sfan means no fan control */ + /* all other models implement TP EC 0x2f control */ + + if (fans_handle) { + /* X31, X40 */ + fan_control_access_mode = + IBMACPI_FAN_WR_ACPI_FANS; + fan_control_commands |= + IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_ENABLE; + } else { + fan_control_access_mode = IBMACPI_FAN_WR_TPEC; + fan_control_commands |= IBMACPI_FAN_CMD_ENABLE; + } + } + } + + return 0; +} static int fan_read(char *p) { @@ -1849,6 +1925,7 @@ static struct ibm_struct ibms[] = { .name = "fan", .read = fan_read, .write = fan_write, + .init = fan_init, .experimental = 1, }, }; -- cgit v1.2.2 From 3ef8a6096ca98d45a54999a97c7c8e14977d2e3e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:10 -0200 Subject: ACPI: ibm-acpi: clean up fan_read This patch cleans up fan_read so that it is much easier to read and extend. The patch fixes the userspace ABI to return "status: not supported" (like all other ibm-acpi functions) when neither fan status or fan control are possible. It also fixes the userspace ABI to return EIO if ACPI access to the EC fails, instead of returning "status: unreadable" or "speed: unreadable". Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 7d0bd61eebc7..e8de5e349d21 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1741,40 +1741,48 @@ static int fan_init(void) static int fan_read(char *p) { int len = 0; - int s; u8 lo, hi, status; - if (gfan_handle) { + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ - if (!acpi_evalf(gfan_handle, &s, NULL, "d")) + if (unlikely(!acpi_evalf(gfan_handle, &status, NULL, "d"))) return -EIO; - len += sprintf(p + len, "level:\t\t%d\n", s); - } else { + len += sprintf(p + len, "level:\t\t%d\n", status); + + break; + + case IBMACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_read(fan_status_offset, &status)) - len += sprintf(p + len, "status:\t\tunreadable\n"); + if (unlikely(!acpi_ec_read(fan_status_offset, &status))) + return -EIO; else len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); - if (!acpi_ec_read(fan_rpm_offset, &lo) || - !acpi_ec_read(fan_rpm_offset + 1, &hi)) - len += sprintf(p + len, "speed:\t\tunreadable\n"); + if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi))) + return -EIO; else len += sprintf(p + len, "speed:\t\t%d\n", (hi << 8) + lo); + + break; + + case IBMACPI_FAN_NONE: + default: + len += sprintf(p + len, "status:\t\tnot supported\n"); } - if (sfan_handle) - /* 570, 770x-JL */ + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) len += sprintf(p + len, "commands:\tlevel " " ( is 0-7)\n"); - if (!gfan_handle) - /* all except 570, 600e/x, 770e, 770x */ + + if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) len += sprintf(p + len, "commands:\tenable, disable\n"); - if (fans_handle) - /* X31, X40 */ + + if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) len += sprintf(p + len, "commands:\tspeed " " ( is 0-65535)\n"); -- cgit v1.2.2 From c52f0aa574246f133a0bc2041e9468a06d34da7b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:10 -0200 Subject: ACPI: ibm-acpi: break fan_read into separate functions This patch breaks fan_read mechanics into a generic function to get fan status and speed, and leaves only the procfs interface code in fan_read. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 78 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index e8de5e349d21..9275dfc130f2 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1738,36 +1738,90 @@ static int fan_init(void) return 0; } -static int fan_read(char *p) +static int fan_get_status(u8 *status) { - int len = 0; - u8 lo, hi, status; + u8 s; switch (fan_status_access_mode) { case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_evalf(gfan_handle, &status, NULL, "d"))) + + if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) return -EIO; - len += sprintf(p + len, "level:\t\t%d\n", status); + if (likely(status)) + *status = s & 0x07; break; case IBMACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_status_offset, &status))) + if (unlikely(!acpi_ec_read(fan_status_offset, &s))) return -EIO; - else - len += sprintf(p + len, "status:\t\t%s\n", - enabled(status, 7)); + if (likely(status)) + *status = s; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_get_speed(unsigned int *speed) +{ + u8 hi, lo; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; - else - len += sprintf(p + len, "speed:\t\t%d\n", - (hi << 8) + lo); + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_read(char *p) +{ + int len = 0; + int rc; + u8 status; + unsigned int speed = 0; + + switch (fan_status_access_mode) { + case IBMACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + len += sprintf(p + len, "level:\t\t%d\n", status); + + break; + + case IBMACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status(&status)) < 0) + return rc; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); + + if ((rc = fan_get_speed(&speed)) < 0) + return rc; + + len += sprintf(p + len, "speed:\t\t%d\n", speed); break; case IBMACPI_FAN_NONE: -- cgit v1.2.2 From 18ad7996e17649d81c15a2c9dae03c75050a05a8 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:11 -0200 Subject: ACPI: ibm-acpi: cleanup fan_write This patch cleans up fan_write so that it is much easier to read and extend. It separates the proc api handling from the operations themselves. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 147 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 9275dfc130f2..187af1bfcb9d 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1843,40 +1843,143 @@ static int fan_read(char *p) return len; } -static int fan_write(char *buf) +static int fan_set_level(int level) { - char *cmd; - int level, speed; - - while ((cmd = next_cmd(&buf))) { - if (sfan_handle && - sscanf(cmd, "level %d", &level) == 1 && - level >= 0 && level <= 7) { - /* 570, 770x-JL */ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + if (level >= 0 && level <= 7) { if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) return -EIO; - } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) { - /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_write(fan_status_offset, 0x80)) - return -EIO; - } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) { - /* all except 570, 600e/x, 770e, 770x */ - if (!acpi_ec_write(fan_status_offset, 0x00)) - return -EIO; - } else if (fans_handle && - sscanf(cmd, "speed %d", &speed) == 1 && - speed >= 0 && speed <= 65535) { - /* X31, X40 */ + } else + return -EINVAL; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_enable(void) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x80)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_disable(void) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x00)) + return -EIO; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_speed(int speed) +{ + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_FANS: + if (speed >= 0 && speed <= 65535) { if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", speed, speed, speed)) return -EIO; } else return -EINVAL; - } + break; + default: + return -ENXIO; + } return 0; } +static int fan_write_cmd_level(const char *cmd, int *rc) +{ + int level; + + if (sscanf(cmd, "level %d", &level) != 1) + return 0; + + if ((*rc = fan_set_level(level)) == -ENXIO) + printk(IBM_ERR "level command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_enable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "enable") != 0) + return 0; + + if ((*rc = fan_set_enable()) == -ENXIO) + printk(IBM_ERR "enable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_disable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "disable") != 0) + return 0; + + if ((*rc = fan_set_disable()) == -ENXIO) + printk(IBM_ERR "disable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_speed(const char *cmd, int *rc) +{ + int speed; + + if (sscanf(cmd, "speed %d", &speed) != 1) + return 0; + + if ((*rc = fan_set_speed(speed)) == -ENXIO) + printk(IBM_ERR "speed command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write(char *buf) +{ + char *cmd; + int rc = 0; + + while (!rc && (cmd = next_cmd(&buf))) { + if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && + fan_write_cmd_level(cmd, &rc)) && + !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && + (fan_write_cmd_enable(cmd, &rc) || + fan_write_cmd_disable(cmd, &rc))) && + !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && + fan_write_cmd_speed(cmd, &rc)) + ) + rc = -EINVAL; + } + + return rc; +} + static struct ibm_struct ibms[] = { { .name = "driver", -- cgit v1.2.2 From a8b7a6626d7605a795b33317cd730b7d76da3d0a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:11 -0200 Subject: ACPI: ibm-acpi: document fan control This patch documents the ThinkPad fan control strategies. Source of the data: 0. ibm-acpi source 1. DSDTs for various ThinkPads (770, X31, X40, X41, T43, A21m, T22) 2. http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues 3. http://thinkwiki.org/wiki/How_to_control_fan_speed 4. Various threads about windows fan control utilities in thinkpads.com Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 187af1bfcb9d..faf78d3eac2d 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -205,7 +205,7 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */ IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ -IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */ +IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ @@ -231,6 +231,106 @@ struct ibm_thermal_sensors_struct { s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; }; +/* + * FAN ACCESS MODES + * + * IBMACPI_FAN_RD_ACPI_GFAN: + * ACPI GFAN method: returns fan level + * + * see IBMACPI_FAN_WR_ACPI_SFAN + * EC 0x2f not available if GFAN exists + * + * IBMACPI_FAN_WR_ACPI_SFAN: + * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) + * + * EC 0x2f might be available *for reading*, but never for writing. + * + * IBMACPI_FAN_WR_TPEC: + * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported + * on almost all ThinkPads + * + * Fan speed changes of any sort (including those caused by the + * disengaged mode) are usually done slowly by the firmware as the + * maximum ammount of fan duty cycle change per second seems to be + * limited. + * + * Reading is not available if GFAN exists. + * Writing is not available if SFAN exists. + * + * Bits + * 7 automatic mode engaged; + * (default operation mode of the ThinkPad) + * fan level is ignored in this mode. + * 6 disengage mode (takes precedence over bit 7); + * not available on all thinkpads. May disable + * the tachometer, and speeds up fan to 100% duty-cycle, + * which speeds it up far above the standard RPM + * levels. It is not impossible that it could cause + * hardware damage. + * 5-3 unused in some models. Extra bits for fan level + * in others, but still useless as all values above + * 7 map to the same speed as level 7 in these models. + * 2-0 fan level (0..7 usually) + * 0x00 = stop + * 0x07 = max (set when temperatures critical) + * Some ThinkPads may have other levels, see + * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) + * + * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at + * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * does so, its initial value is meaningless (0x07). + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * ---- + * + * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): + * Main fan tachometer reading (in RPM) + * + * This register is present on all ThinkPads with a new-style EC, and + * it is known not to be present on the A21m/e, and T22, as there is + * something else in offset 0x84 according to the ACPI DSDT. Other + * ThinkPads from this same time period (and earlier) probably lack the + * tachometer as well. + * + * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * was never fixed by IBM to report the EC firmware version string + * probably support the tachometer (like the early X models), so + * detecting it is quite hard. We need more data to know for sure. + * + * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings + * might result. + * + * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this + * register is not invalidated in ThinkPads that disable tachometer + * readings. Thus, the tachometer readings go stale. + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * IBMACPI_FAN_WR_ACPI_FANS: + * ThinkPad X31, X40, X41. Not available in the X60. + * + * FANS ACPI handle: takes three arguments: low speed, medium speed, + * high speed. ACPI DSDT seems to map these three speeds to levels + * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH + * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") + * + * The speeds are stored on handles + * (FANA:FAN9), (FANC:FANB), (FANE:FAND). + * + * There are three default speed sets, acessible as handles: + * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H + * + * ACPI DSDT switches which set is in use depending on various + * factors. + * + * IBMACPI_FAN_WR_TPEC is also available and should be used to + * command the fan. The X31/X40/X41 seems to have 8 fan levels, + * but the ACPI tables just mention level 7. + */ + enum fan_status_access_mode { IBMACPI_FAN_NONE = 0, /* No fan status or control */ IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ @@ -1722,7 +1822,7 @@ static int fan_init(void) /* all other models implement TP EC 0x2f control */ if (fans_handle) { - /* X31, X40 */ + /* X31, X40, X41 */ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_FANS; fan_control_commands |= @@ -1742,6 +1842,9 @@ static int fan_get_status(u8 *status) { u8 s; + /* TODO: + * Add IBMACPI_FAN_RD_ACPI_FANS ? */ + switch (fan_status_access_mode) { case IBMACPI_FAN_RD_ACPI_GFAN: /* 570, 600e/x, 770e, 770x */ @@ -1950,6 +2053,9 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) { int speed; + /* TODO: + * Support speed ? */ + if (sscanf(cmd, "speed %d", &speed) != 1) return 0; -- cgit v1.2.2 From bab812a329cc244ca63c2675b0e05016518855ce Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:12 -0200 Subject: ACPI: ibm-acpi: extend fan status functions This patch fixes fan_read to return correct values for all fan access modes. It also implements some fan access mode status output that was missing, and normalizes the proc fan abi to return consistent data across all fan read/write modes. Userspace ABI changes and extensions: 1. Return status: enable/disable for *all* modes (this actually improves compatibility with userspace utils!) 2. Return level: auto and level: disengaged for EC 2f access mode 3. Return level: for EC 0x2f access mode 4. Return level 0 as well as "disabled" in level-aware modes Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index faf78d3eac2d..b6ad2ed5fd6c 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -354,6 +354,11 @@ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) * 0x84 must be read before 0x85 */ + + IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer + * disengaged */ + IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan + * control */ }; static int ibm_thinkpad_ec_found; @@ -1910,8 +1915,9 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; - len += sprintf(p + len, "level:\t\t%d\n", status); - + len += sprintf(p + len, "status:\t\t%s\n" + "level:\t\t%d\n", + (status != 0) ? "enabled" : "disabled", status); break; case IBMACPI_FAN_RD_TPEC: @@ -1919,12 +1925,21 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 7)); + len += sprintf(p + len, "status:\t\t%s\n", + (status != 0) ? "enabled" : "disabled"); if ((rc = fan_get_speed(&speed)) < 0) return rc; len += sprintf(p + len, "speed:\t\t%d\n", speed); + + if (status & IBMACPI_FAN_EC_DISENGAGED) + /* Disengaged mode takes precedence */ + len += sprintf(p + len, "level:\t\tdisengaged\n"); + else if (status & IBMACPI_FAN_EC_AUTO) + len += sprintf(p + len, "level:\t\tauto\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", status); break; case IBMACPI_FAN_NONE: -- cgit v1.2.2 From 1c6a334e9c028c2b72c5350650cb14e6d5fdc232 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:12 -0200 Subject: ACPI: ibm-acpi: fix and extend fan enable This patch fix fan enable to attempt to do the right thing and not slow down the fan if it is forced to the maximum speed. It also extends fan enable to work on older thinkpads. ABI changes: 1. Support enable/disable for all level-based write access modes Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index b6ad2ed5fd6c..ecb5ece79a37 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1820,7 +1820,8 @@ static int fan_init(void) if (sfan_handle) { /* 570, 770x-JL */ fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; - fan_control_commands |= IBMACPI_FAN_CMD_LEVEL; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -1980,10 +1981,34 @@ static int fan_set_level(int level) static int fan_set_enable(void) { + u8 s; + int rc; + switch (fan_control_access_mode) { case IBMACPI_FAN_WR_ACPI_FANS: case IBMACPI_FAN_WR_TPEC: - if (!acpi_ec_write(fan_status_offset, 0x80)) + if ((rc = fan_get_status(&s)) < 0) + return rc; + + /* Don't go out of emergency fan mode */ + if (s != 7) + s = IBMACPI_FAN_EC_AUTO; + + if (!acpi_ec_write(fan_status_offset, s)) + return -EIO; + break; + + case IBMACPI_FAN_WR_ACPI_SFAN: + if ((rc = fan_get_status(&s)) < 0) + return rc; + + s &= 0x07; + + /* Set fan to at least level 4 */ + if (s < 4) + s = 4; + + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) return -EIO; break; @@ -2002,6 +2027,11 @@ static int fan_set_disable(void) return -EIO; break; + case IBMACPI_FAN_WR_ACPI_SFAN: + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) + return -EIO; + break; + default: return -ENXIO; } -- cgit v1.2.2 From a12095c2b50c8a7c80517e37c00d6e6c863d43c5 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:13 -0200 Subject: ACPI: ibm-acpi: fix and extend fan control functions This patch extend fan control functions, implementing enable/disable for all write access modes, implementing level control for all level-capable write access modes. The patch also updates the documentation, explaining levels auto and disengaged. ABI changes: 1. Support level 0 as an equivalent to disable 2. Add support for level auto and level disengaged when doing EC 0x2f fan control 3. Support enable/disable for all level-based write access modes 4. Add support for level command on FANS thinkpads, as per thinkwiki reports Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ecb5ece79a37..4001ad193ddc 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1833,10 +1833,13 @@ static int fan_init(void) IBMACPI_FAN_WR_ACPI_FANS; fan_control_commands |= IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; } else { fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= IBMACPI_FAN_CMD_ENABLE; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | + IBMACPI_FAN_CMD_ENABLE; } } } @@ -1948,9 +1951,20 @@ static int fan_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); } - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel "); + + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " ( is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " ( is 0-7, " + "auto, disengaged)\n"); + break; + } + } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) len += sprintf(p + len, "commands:\tenable, disable\n"); @@ -1973,6 +1987,17 @@ static int fan_set_level(int level) return -EINVAL; break; + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if ((level != IBMACPI_FAN_EC_AUTO) && + (level != IBMACPI_FAN_EC_DISENGAGED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + break; + default: return -ENXIO; } @@ -2060,7 +2085,11 @@ static int fan_write_cmd_level(const char *cmd, int *rc) { int level; - if (sscanf(cmd, "level %d", &level) != 1) + if (strlencmp(cmd, "level auto") == 0) + level = IBMACPI_FAN_EC_AUTO; + else if (strlencmp(cmd, "level disengaged") == 0) + level = IBMACPI_FAN_EC_DISENGAGED; + else if (sscanf(cmd, "level %d", &level) != 1) return 0; if ((*rc = fan_set_level(level)) == -ENXIO) -- cgit v1.2.2 From 49a13cd6a2acd284ee106eaea7eeea8f2cc6796a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:13 -0200 Subject: ACPI: ibm-acpi: store embedded controller firmware version for matching This patch changes the ThinkPad Embedded Controller DMI matching code to store the firmware version of the EC for later usage, e.g. for quirks. It also prints the firmware version when starting up. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 4001ad193ddc..3c091c4b461f 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ enum { /* Fan control constants */ * control */ }; -static int ibm_thinkpad_ec_found; +static char* ibm_thinkpad_ec_found = NULL; struct ibm_struct { char *name; @@ -2540,11 +2541,15 @@ static void acpi_ibm_exit(void) ibm_exit(&ibms[i]); remove_proc_entry(IBM_DIR, acpi_root_dir); + + if (ibm_thinkpad_ec_found) + kfree(ibm_thinkpad_ec_found); } -static int __init check_dmi_for_ec(void) +static char* __init check_dmi_for_ec(void) { struct dmi_device *dev = NULL; + char ec_fw_string[18]; /* * ThinkPad T23 or newer, A31 or newer, R50e or newer, @@ -2554,10 +2559,15 @@ static int __init check_dmi_for_ec(void) * See http://thinkwiki.org/wiki/List_of_DMI_IDs */ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (strstr(dev->name, "IBM ThinkPad Embedded Controller")) - return 1; + if (sscanf(dev->name, + "IBM ThinkPad Embedded Controller -[%17c", + ec_fw_string) == 1) { + ec_fw_string[sizeof(ec_fw_string) - 1] = 0; + ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; + return kstrdup(ec_fw_string, GFP_KERNEL); + } } - return 0; + return NULL; } static int __init acpi_ibm_init(void) @@ -2581,6 +2591,9 @@ static int __init acpi_ibm_init(void) /* Models with newer firmware report the EC in DMI */ ibm_thinkpad_ec_found = check_dmi_for_ec(); + if (ibm_thinkpad_ec_found) + printk(IBM_INFO "ThinkPad EC firmware %s\n", + ibm_thinkpad_ec_found); /* these handles are not required */ IBM_HANDLE_INIT(vid); -- cgit v1.2.2 From 778b4d742b210b9cac31f223527f30f1fc70312b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:14 -0200 Subject: ACPI: ibm-acpi: workaround for EC 0x2f initialization bug A few ThinkPads fail to initialize EC register 0x2f both in the EC firmware and ACPI DSDT. If the BIOS and the ACPI DSDT also do not initialize it, then the initial status of that register does not correspond to reality. On all reported buggy machines, EC 0x2f will read 0x07 (fan level 7) upon cold boot, when the EC is actually in mode 0x80 (auto mode). Since returning a text string ("unknown") would break a number of userspace programs, instead we correct the reading for the most probably correct answer, and return it is in auto mode. The workaround flags the status and level as unknown on module load/kernel boot, until we are certain at least one fan control command was issued, either by us, or by something else. We don't work around the bug by doing a "fan enable" at module load/startup (which would initialize the EC register) because it is not known if these ThinkPad ACPI DSDT might have set the fan to level 7 instead of "auto" (we don't know if they can do this or not) due to a thermal condition, and we don't want to override that, should they be capable of it. We should be setting the workaround flag to "status known" upon resume, as both reports and a exaustive search on the DSDT tables at acpi.sf.net show that the DSDTs always enable the fan on resume, thus working around the bug. But since we don't have suspend/resume handlers in ibm-acpi yet and the "EC register 0x2f was modified" logic is likely to catch the change anyway, we don't. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 53 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 3c091c4b461f..56743c5a0439 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -362,7 +362,7 @@ enum { /* Fan control constants */ * control */ }; -static char* ibm_thinkpad_ec_found = NULL; +static char *ibm_thinkpad_ec_found = NULL; struct ibm_struct { char *name; @@ -1794,13 +1794,15 @@ static enum fan_status_access_mode fan_status_access_mode; static enum fan_control_access_mode fan_control_access_mode; static enum fan_control_commands fan_control_commands; +static int fan_control_status_known; +static u8 fan_control_initial_status; + static int fan_init(void) { - u8 status; - fan_status_access_mode = IBMACPI_FAN_NONE; fan_control_access_mode = IBMACPI_FAN_WR_NONE; fan_control_commands = 0; + fan_control_status_known = 1; if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ @@ -1808,8 +1810,33 @@ static int fan_init(void) } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ - if (likely(acpi_ec_read(fan_status_offset, &status))) { + if (likely(acpi_ec_read(fan_status_offset, + &fan_control_initial_status))) { fan_status_access_mode = IBMACPI_FAN_RD_TPEC; + + /* In some ThinkPads, neither the EC nor the ACPI + * DSDT initialize the fan status, and it ends up + * being set to 0x07 when it *could* be either + * 0x07 or 0x80. + * + * Enable for TP-1Y (T43), TP-78 (R51e), + * TP-76 (R52), TP-70 (T43, R52), which are known + * to be buggy. */ + if (fan_control_initial_status == 0x07 && + ibm_thinkpad_ec_found && + ((ibm_thinkpad_ec_found[0] == '1' && + ibm_thinkpad_ec_found[1] == 'Y') || + (ibm_thinkpad_ec_found[0] == '7' && + (ibm_thinkpad_ec_found[1] == '6' || + ibm_thinkpad_ec_found[1] == '8' || + ibm_thinkpad_ec_found[1] == '0')) + )) { + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + fan_control_status_known = 0; + } } else { printk(IBM_ERR "ThinkPad ACPI EC access misbehaving, " @@ -1930,9 +1957,21 @@ static int fan_read(char *p) if ((rc = fan_get_status(&status)) < 0) return rc; + if (unlikely(!fan_control_status_known)) { + if (status != fan_control_initial_status) + fan_control_status_known = 1; + else + /* Return most likely status. In fact, it + * might be the only possible status */ + status = IBMACPI_FAN_EC_AUTO; + } + len += sprintf(p + len, "status:\t\t%s\n", (status != 0) ? "enabled" : "disabled"); + /* No ThinkPad boots on disengaged mode, we can safely + * assume the tachometer is online if fan control status + * was unknown */ if ((rc = fan_get_speed(&speed)) < 0) return rc; @@ -1997,6 +2036,8 @@ static int fan_set_level(int level) if (!acpi_ec_write(fan_status_offset, level)) return -EIO; + else + fan_control_status_known = 1; break; default: @@ -2022,6 +2063,8 @@ static int fan_set_enable(void) if (!acpi_ec_write(fan_status_offset, s)) return -EIO; + else + fan_control_status_known = 1; break; case IBMACPI_FAN_WR_ACPI_SFAN: @@ -2051,6 +2094,8 @@ static int fan_set_disable(void) case IBMACPI_FAN_WR_TPEC: if (!acpi_ec_write(fan_status_offset, 0x00)) return -EIO; + else + fan_control_status_known = 1; break; case IBMACPI_FAN_WR_ACPI_SFAN: -- cgit v1.2.2 From 16663a87ad1df7022661bc8813b7a2e84e7f5e66 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:14 -0200 Subject: ACPI: ibm-acpi: implement fan watchdog command This patch implements a fan control safety watchdog, by request of the authors of userspace fan control scripts. When the watchdog timer expires, the equivalent action of a "fan enable" command is executed. The watchdog timer is reset at every reception of a fan control command that could change the state of the fan itself. This command is meant to be used by userspace fan control daemons, to make sure the fan is never left set to an unsafe level because of userspace problems. Users of the X31/X40/X41 "speed" command are on their own, the current implementation of "speed" is just too incomplete to be used safely, anyway. Better to never use it, and just use the "level" command instead. The watchdog is programmed using echo "watchdog " > fan, where number is the number of seconds to wait before doing an "enable", and zero disables the watchdog. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 56743c5a0439..e5b8745b9070 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -82,6 +82,8 @@ #include #include #include +#include +#include #include #include @@ -348,7 +350,8 @@ enum fan_control_access_mode { enum fan_control_commands { IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ }; enum { /* Fan control constants */ @@ -1797,12 +1800,17 @@ static enum fan_control_commands fan_control_commands; static int fan_control_status_known; static u8 fan_control_initial_status; +static void fan_watchdog_fire(void *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL); + static int fan_init(void) { fan_status_access_mode = IBMACPI_FAN_NONE; fan_control_access_mode = IBMACPI_FAN_WR_NONE; fan_control_commands = 0; fan_control_status_known = 1; + fan_watchdog_maxinterval = 0; if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ @@ -1934,6 +1942,31 @@ static int fan_get_speed(unsigned int *speed) return 0; } +static void fan_exit(void) +{ + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + static int fan_read(char *p) { int len = 0; @@ -2007,7 +2040,9 @@ static int fan_read(char *p) } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n"); + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog ( is 0 (off), " + "1-120 (seconds))\n"); if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) len += sprintf(p + len, "commands:\tspeed " @@ -2186,6 +2221,21 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) return 1; } +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + static int fan_write(char *buf) { char *cmd; @@ -2196,16 +2246,29 @@ static int fan_write(char *buf) fan_write_cmd_level(cmd, &rc)) && !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc))) && + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && fan_write_cmd_speed(cmd, &rc)) ) rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); } return rc; } +static void fan_watchdog_fire(void *ignored) +{ + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + if (fan_set_enable()) { + printk(IBM_ERR "fan watchdog: error while enabling fan\n"); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + static struct ibm_struct ibms[] = { { .name = "driver", @@ -2317,6 +2380,7 @@ static struct ibm_struct ibms[] = { .read = fan_read, .write = fan_write, .init = fan_init, + .exit = fan_exit, .experimental = 1, }, }; -- cgit v1.2.2 From e0298997acdba929e7f5b5987d305b67b50a3969 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: add support for the ultrabay on the T60,X60 This patch adds support for the ultrabay on the T60, X60 and other new ThinkPads that have a SATA ultrabay. I intend to keep bay and dock support in ibm-acpi working and updated until it finally gets deprecated and removed in favour of the generic dock and bay support. But we aren't there yet. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index e5b8745b9070..392abbb7d2dc 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -174,6 +174,7 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ #endif IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ ); /* A21e, R30, R31 */ -- cgit v1.2.2 From 2df910b4c3edcce9a0c12394db6f5f4a6e69c712 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: make non-generic bay support optional This patch makes it possible to disable ibm-acpi non-generic bay support, as generic bay support already works well for a number of ThinkPads. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/Kconfig | 11 +++++++++++ drivers/acpi/ibm_acpi.c | 13 ++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 59f9def21720..6f8c50ea54e1 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -224,6 +224,17 @@ config ACPI_IBM_DOCK If you are not sure, say N here. +config ACPI_IBM_BAY + bool "Legacy Removable Bay Support" + depends on ACPI_IBM + depends on ACPI_BAY=n + default n + ---help--- + Allows the ibm_acpi driver to handle removable bays. + This support is obsoleted by CONFIG_ACPI_BAY. + + If you are not sure, say N here. + config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 392abbb7d2dc..fbb4970ce16a 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -172,6 +172,7 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ "\\_SB.PCI.ISA.SLCE", /* 570 */ ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ #endif +#ifdef CONFIG_ACPI_IBM_BAY IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ @@ -189,6 +190,7 @@ IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ "_EJ0", /* 770x */ ); /* all others */ +#endif /* don't list other alternatives as we install a notify handler on the 570 */ IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ @@ -1051,6 +1053,7 @@ static int light_write(char *buf) return 0; } +#if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY) static int _sta(acpi_handle handle) { int status; @@ -1060,7 +1063,7 @@ static int _sta(acpi_handle handle) return status; } - +#endif #ifdef CONFIG_ACPI_IBM_DOCK #define dock_docked() (_sta(dock_handle) & 1) @@ -1126,6 +1129,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event) } #endif +#ifdef CONFIG_ACPI_IBM_BAY static int bay_status_supported; static int bay_status2_supported; static int bay_eject_supported; @@ -1201,6 +1205,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event) { acpi_bus_generate_event(ibm->device, event, 0); } +#endif static int cmos_read(char *p) { @@ -2330,6 +2335,7 @@ static struct ibm_struct ibms[] = { .type = ACPI_SYSTEM_NOTIFY, }, #endif +#ifdef CONFIG_ACPI_IBM_BAY { .name = "bay", .init = bay_init, @@ -2339,6 +2345,7 @@ static struct ibm_struct ibms[] = { .handle = &bay_handle, .type = ACPI_SYSTEM_NOTIFY, }, +#endif { .name = "cmos", .read = cmos_read, @@ -2624,7 +2631,9 @@ IBM_PARAM(light); #ifdef CONFIG_ACPI_IBM_DOCK IBM_PARAM(dock); #endif +#ifdef CONFIG_ACPI_IBM_BAY IBM_PARAM(bay); +#endif IBM_PARAM(cmos); IBM_PARAM(led); IBM_PARAM(beep); @@ -2717,12 +2726,14 @@ static int __init acpi_ibm_init(void) IBM_HANDLE_INIT(dock); #endif IBM_HANDLE_INIT(pci); +#ifdef CONFIG_ACPI_IBM_BAY IBM_HANDLE_INIT(bay); if (bay_handle) IBM_HANDLE_INIT(bay_ej); IBM_HANDLE_INIT(bay2); if (bay2_handle) IBM_HANDLE_INIT(bay2_ej); +#endif IBM_HANDLE_INIT(beep); IBM_HANDLE_INIT(ecrd); IBM_HANDLE_INIT(ecwr); -- cgit v1.2.2 From fb87a811a4c232e2af8d746dc75330cbe5b0780c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:35:09 -0200 Subject: ACPI: ibm-acpi: backlight device cleanup This patch cleans up the recently added backlight device support by Holger Macht to fit well with the rest of the code, using the ibms struct as the other "subdrivers" in ibm-acpi. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 50 ++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index fbb4970ce16a..36e2667d8652 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -397,7 +397,7 @@ struct ibm_struct { static struct proc_dir_entry *proc_dir = NULL; -static struct backlight_device *ibm_backlight_device; +static struct backlight_device *ibm_backlight_device = NULL; #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -1639,6 +1639,7 @@ static int brightness_get(struct backlight_device *bd) return -EIO; level &= 0x7; + return level; } @@ -1713,6 +1714,33 @@ static int brightness_update_status(struct backlight_device *bd) return brightness_set(bd->props->brightness); } +static struct backlight_properties ibm_backlight_data = { + .owner = THIS_MODULE, + .get_brightness = brightness_get, + .update_status = brightness_update_status, + .max_brightness = 7, +}; + +static int brightness_init(void) +{ + ibm_backlight_device = backlight_device_register("ibm", NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register backlight device\n"); + return PTR_ERR(ibm_backlight_device); + } + + return 0; +} + +static void brightness_exit(void) +{ + if (ibm_backlight_device) { + backlight_device_unregister(ibm_backlight_device); + ibm_backlight_device = NULL; + } +} + static int volume_offset = 0x30; static int volume_read(char *p) @@ -2377,6 +2405,8 @@ static struct ibm_struct ibms[] = { .name = "brightness", .read = brightness_read, .write = brightness_write, + .init = brightness_init, + .exit = brightness_exit, }, { .name = "volume", @@ -2642,20 +2672,10 @@ IBM_PARAM(brightness); IBM_PARAM(volume); IBM_PARAM(fan); -static struct backlight_properties ibm_backlight_data = { - .owner = THIS_MODULE, - .get_brightness = brightness_get, - .update_status = brightness_update_status, - .max_brightness = 7, -}; - static void acpi_ibm_exit(void) { int i; - if (ibm_backlight_device) - backlight_device_unregister(ibm_backlight_device); - for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) ibm_exit(&ibms[i]); @@ -2758,14 +2778,6 @@ static int __init acpi_ibm_init(void) } } - ibm_backlight_device = backlight_device_register("ibm", NULL, - &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { - printk(IBM_ERR "Could not register ibm backlight device\n"); - ibm_backlight_device = NULL; - acpi_ibm_exit(); - } - return 0; } -- cgit v1.2.2 From 9a8e1738c1136a857c1fd3ae0c5019f9767427ad Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:36:00 -0200 Subject: ACPI: ibm-acpi: style fixes and cruft removal This patch just fixes style, move some #defines to enums, and removes some old cruft. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 100 +++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 36e2667d8652..8bb41bd85cd3 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -78,9 +78,11 @@ #include #include #include + #include #include #include + #include #include #include @@ -121,28 +123,6 @@ static acpi_handle root_handle = NULL; static char *object##_path; \ static char *object##_paths[] = { paths } -/* - * The following models are supported to various degrees: - * - * 570, 600e, 600x, 770e, 770x - * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p - * G40, G41 - * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51 - * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43 - * X20, X21, X22, X23, X24, X30, X31, X40 - * - * The following models have no supported features: - * - * 240, 240x, i1400 - * - * Still missing DSDTs for the following models: - * - * A20p, A22e, A22m - * R52 - * S31 - * T43p - */ - IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ "\\_SB.PCI.ISA.EC", /* 570 */ "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ @@ -785,12 +765,15 @@ static int wan_write(char *buf) return 0; } -static int video_supported; -static int video_orig_autosw; +enum video_access_mode { + IBMACPI_VIDEO_NONE = 0, + IBMACPI_VIDEO_570, /* 570 */ + IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ + IBMACPI_VIDEO_NEW, /* all others */ +}; -#define VIDEO_570 1 -#define VIDEO_770 2 -#define VIDEO_NEW 3 +static enum video_access_mode video_supported; +static int video_orig_autosw; static int video_init(void) { @@ -802,16 +785,16 @@ static int video_init(void) if (!vid_handle) /* video switching not supported on R30, R31 */ - video_supported = 0; + video_supported = IBMACPI_VIDEO_NONE; else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) /* 570 */ - video_supported = VIDEO_570; + video_supported = IBMACPI_VIDEO_570; else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) /* 600e/x, 770e, 770x */ - video_supported = VIDEO_770; + video_supported = IBMACPI_VIDEO_770; else /* all others */ - video_supported = VIDEO_NEW; + video_supported = IBMACPI_VIDEO_NEW; return 0; } @@ -821,15 +804,15 @@ static int video_status(void) int status = 0; int i; - if (video_supported == VIDEO_570) { + if (video_supported == IBMACPI_VIDEO_570) { if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) status = i & 3; - } else if (video_supported == VIDEO_770) { + } else if (video_supported == IBMACPI_VIDEO_770) { if (acpi_evalf(NULL, &i, "\\VCDL", "d")) status |= 0x01 * i; if (acpi_evalf(NULL, &i, "\\VCDC", "d")) status |= 0x02 * i; - } else if (video_supported == VIDEO_NEW) { + } else if (video_supported == IBMACPI_VIDEO_NEW) { acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); if (acpi_evalf(NULL, &i, "\\VCDC", "d")) status |= 0x02 * i; @@ -848,9 +831,10 @@ static int video_autosw(void) { int autosw = 0; - if (video_supported == VIDEO_570) + if (video_supported == IBMACPI_VIDEO_570) acpi_evalf(vid_handle, &autosw, "SWIT", "d"); - else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW) + else if (video_supported == IBMACPI_VIDEO_770 || + video_supported == IBMACPI_VIDEO_NEW) acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); return autosw & 1; @@ -870,12 +854,12 @@ static int video_read(char *p) len += sprintf(p + len, "status:\t\tsupported\n"); len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); - if (video_supported == VIDEO_NEW) + if (video_supported == IBMACPI_VIDEO_NEW) len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); - if (video_supported == VIDEO_NEW) + if (video_supported == IBMACPI_VIDEO_NEW) len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); @@ -890,7 +874,7 @@ static int video_switch(void) if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) return -EIO; - ret = video_supported == VIDEO_570 ? + ret = video_supported == IBMACPI_VIDEO_570 ? acpi_evalf(ec_handle, NULL, "_Q16", "v") : acpi_evalf(vid_handle, NULL, "VSWT", "v"); acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); @@ -900,9 +884,9 @@ static int video_switch(void) static int video_expand(void) { - if (video_supported == VIDEO_570) + if (video_supported == IBMACPI_VIDEO_570) return acpi_evalf(ec_handle, NULL, "_Q17", "v"); - else if (video_supported == VIDEO_770) + else if (video_supported == IBMACPI_VIDEO_770) return acpi_evalf(vid_handle, NULL, "VEXP", "v"); else return acpi_evalf(NULL, NULL, "\\VEXP", "v"); @@ -912,10 +896,10 @@ static int video_switch2(int status) { int ret; - if (video_supported == VIDEO_570) { + if (video_supported == IBMACPI_VIDEO_570) { ret = acpi_evalf(NULL, NULL, "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); - } else if (video_supported == VIDEO_770) { + } else if (video_supported == IBMACPI_VIDEO_770) { int autosw = video_autosw(); if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) return -EIO; @@ -951,10 +935,10 @@ static int video_write(char *buf) enable |= 0x02; } else if (strlencmp(cmd, "crt_disable") == 0) { disable |= 0x02; - } else if (video_supported == VIDEO_NEW && + } else if (video_supported == IBMACPI_VIDEO_NEW && strlencmp(cmd, "dvi_enable") == 0) { enable |= 0x08; - } else if (video_supported == VIDEO_NEW && + } else if (video_supported == IBMACPI_VIDEO_NEW && strlencmp(cmd, "dvi_disable") == 0) { disable |= 0x08; } else if (strlencmp(cmd, "auto_enable") == 0) { @@ -1253,26 +1237,28 @@ static int cmos_write(char *buf) return 0; } -static int led_supported; - -#define LED_570 1 -#define LED_OLD 2 -#define LED_NEW 3 +enum led_access_mode { + IBMACPI_LED_NONE = 0, + IBMACPI_LED_570, /* 570 */ + IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + IBMACPI_LED_NEW, /* all others */ +}; +static enum led_access_mode led_supported; static int led_init(void) { if (!led_handle) /* led not supported on R30, R31 */ - led_supported = 0; + led_supported = IBMACPI_LED_NONE; else if (strlencmp(led_path, "SLED") == 0) /* 570 */ - led_supported = LED_570; + led_supported = IBMACPI_LED_570; else if (strlencmp(led_path, "SYSL") == 0) /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = LED_OLD; + led_supported = IBMACPI_LED_OLD; else /* all others */ - led_supported = LED_NEW; + led_supported = IBMACPI_LED_NEW; return 0; } @@ -1289,7 +1275,7 @@ static int led_read(char *p) } len += sprintf(p + len, "status:\t\tsupported\n"); - if (led_supported == LED_570) { + if (led_supported == IBMACPI_LED_570) { /* 570 */ int i, status; for (i = 0; i < 8; i++) { @@ -1338,13 +1324,13 @@ static int led_write(char *buf) } else return -EINVAL; - if (led_supported == LED_570) { + if (led_supported == IBMACPI_LED_570) { /* 570 */ led = 1 << led; if (!acpi_evalf(led_handle, NULL, NULL, "vdd", led, led_sled_arg1[ind])) return -EIO; - } else if (led_supported == LED_OLD) { + } else if (led_supported == IBMACPI_LED_OLD) { /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ led = 1 << led; ret = ec_write(EC_HLMS, led); -- cgit v1.2.2 From f9ff43a6268d36acf8df18a76bb881a26a42dc1e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 25 Nov 2006 16:37:38 -0200 Subject: ACPI: ibm-acpi: update version and copyright Bump up module version, add myself to copyright and MODULE_AUTHOR. Signed-off-by: Henrique de Moraes Holschuh --- drivers/acpi/ibm_acpi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 8bb41bd85cd3..92e7b6eb8f19 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -3,6 +3,7 @@ * * * Copyright (C) 2004-2005 Borislav Deianov + * Copyright (C) 2006 Henrique de Moraes Holschuh * * 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 @@ -19,10 +20,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define IBM_VERSION "0.12a" +#define IBM_VERSION "0.13" /* * Changelog: + * + * 2006-11-22 0.13 new maintainer + * changelog now lives in git commit history, and will + * not be updated further in-file. * * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels * 2005-03-17 0.11 support for 600e, 770x @@ -95,7 +100,7 @@ #define IBM_FILE "ibm_acpi" #define IBM_URL "http://ibm-acpi.sf.net/" -MODULE_AUTHOR("Borislav Deianov"); +MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); MODULE_DESCRIPTION(IBM_DESC); MODULE_VERSION(IBM_VERSION); MODULE_LICENSE("GPL"); -- cgit v1.2.2