From e927c08da53e5c87ca07f7a828d4a0048e7bacf0 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:19 -0200 Subject: [PATCH 01/39] ACPI: thinkpad-acpi: revert keymap changes Revert commit fba956c46a72f9e7503fd464ffee43c632307e31, "Map volume and brightness events on thinkpads". That commit made some modifications to the default keymaps that cause bad behaviour on all IBM ThinkPads if HAL doesn't know to change them into passive (on-screen-display only) events. The proper solution for IBM ThinkPads is to use the _NOTIFY version of the key codes for the IBM default map (which are not available in mainline yet), and for the Lenovo keymap, it will take some studying of the various DSDTs and testing to know the best path (which I will do shortly). For more data, refer to: http://thread.gmane.org/gmane.linux.kernel/591037/focus=591045 Signed-off-by: Henrique de Moraes Holschuh Cc: Jeremy Katz Signed-off-by: Len Brown --- drivers/misc/thinkpad_acpi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index e953276664a0..63b1e2610c60 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -964,15 +964,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ KEY_UNKNOWN, /* 0x0D: FN+INSERT */ KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ + KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ - KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ + KEY_RESERVED, /* 0x10: FN+END (brightness down) */ KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - KEY_VOLUMEUP, /* 0x14: VOLUME UP */ - KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */ - KEY_MUTE, /* 0x16: MUTE */ + KEY_RESERVED, /* 0x14: VOLUME UP */ + KEY_RESERVED, /* 0x15: VOLUME DOWN */ + KEY_RESERVED, /* 0x16: MUTE */ KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ /* (assignments unknown, please report if found) */ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, @@ -993,9 +993,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - KEY_VOLUMEUP, /* 0x14: VOLUME UP */ - KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */ - KEY_MUTE, /* 0x16: MUTE */ + KEY_RESERVED, /* 0x14: VOLUME UP */ + KEY_RESERVED, /* 0x15: VOLUME DOWN */ + KEY_RESERVED, /* 0x16: MUTE */ KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ /* (assignments unknown, please report if found) */ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, From a3f104c02ab842574e699186cf953551aafe2ca9 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:20 -0200 Subject: [PATCH 02/39] ACPI: thinkpad-acpi: support 16 levels of brightness (v3) Lenovo ThinkPads often have 16 brightness levels in EC, and not just eight levels like older ThinkPads. They also have standard ACPI backlight brightness control. We detect the number of brightness levels by the presence of a BCLL package with 16 entries. If BCLL is not there, we assume eight levels (Z6*). If it is there, but it doesn't have 16 entries, we assume eight levels (T60). Otherwise we assume sixteen levels (T61, X61, etc). We don't use _BCL because it can have side-effects in thinkpads. Thanks to Thomas Renninger for notifying me of this potential problem. Using the standard ACPI backlight brightness control *instead* of the native thinkpad backlight control is a better idea, though. A different patch will take care of this. Signed-off-by: Henrique de Moraes Holschuh Cc: Thomas Renninger Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 57 ++++++++++++--------- drivers/misc/thinkpad_acpi.c | 91 +++++++++++++++++++++++++++++---- drivers/misc/thinkpad_acpi.h | 3 +- 3 files changed, 116 insertions(+), 35 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index ec499265deca..a743bfa62b95 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -923,19 +923,26 @@ sysfs backlight device "thinkpad_screen" This feature allows software control of the LCD brightness on ThinkPad models which don't have a hardware brightness slider. -It has some limitations: the LCD backlight cannot be actually turned on or off -by this interface, and in many ThinkPad models, the "dim while on battery" -functionality will be enabled by the BIOS when this interface is used, and -cannot be controlled. +It has some limitations: the LCD backlight cannot be actually turned on or +off by this interface, and in many ThinkPad models, the "dim while on +battery" functionality will be enabled by the BIOS when this interface is +used, and cannot be controlled. -The backlight control has eight levels, ranging from 0 to 7. Some of the -levels may not be distinct. +On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control +has eight brightness levels, ranging from 0 to 7. Some of the levels +may not be distinct. Later Lenovo models that implement the ACPI +display backlight brightness control methods have 16 levels, ranging +from 0 to 15. -There are two interfaces to the firmware for brightness control, EC and CMOS. -To select which one should be used, use the brightness_mode module parameter: -brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode, -brightness_mode=3 selects both EC and CMOS. The driver tries to autodetect -which interface to use. +There are two interfaces to the firmware for direct brightness control, +EC and CMOS. To select which one should be used, use the +brightness_mode module parameter: brightness_mode=1 selects EC mode, +brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC +and CMOS. The driver tries to autodetect which interface to use. + +When display backlight brightness controls are available through the +standard ACPI interface, it is best to use it instead of this direct +ThinkPad-specific interface. Procfs notes: @@ -947,11 +954,11 @@ Procfs notes: Sysfs notes: -The interface is implemented through the backlight sysfs class, which is poorly -documented at this time. +The interface is implemented through the backlight sysfs class, which is +poorly documented at this time. -Locate the thinkpad_screen device under /sys/class/backlight, and inside it -there will be the following attributes: +Locate the thinkpad_screen device under /sys/class/backlight, and inside +it there will be the following attributes: max_brightness: Reads the maximum brightness the hardware can be set to. @@ -961,17 +968,19 @@ there will be the following attributes: Reads what brightness the screen is set to at this instant. brightness: - Writes request the driver to change brightness to the given - value. Reads will tell you what brightness the driver is trying - to set the display to when "power" is set to zero and the display - has not been dimmed by a kernel power management event. + Writes request the driver to change brightness to the + given value. Reads will tell you what brightness the + driver is trying to set the display to when "power" is set + to zero and the display has not been dimmed by a kernel + power management event. power: - power management mode, where 0 is "display on", and 1 to 3 will - dim the display backlight to brightness level 0 because - thinkpad-acpi cannot really turn the backlight off. Kernel - power management events can temporarily increase the current - power management level, i.e. they can dim the display. + power management mode, where 0 is "display on", and 1 to 3 + will dim the display backlight to brightness level 0 + because thinkpad-acpi cannot really turn the backlight + off. Kernel power management events can temporarily + increase the current power management level, i.e. they can + dim the display. Volume control -- /proc/acpi/ibm/volume diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 63b1e2610c60..322ba25b4798 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -3114,6 +3114,66 @@ static struct backlight_ops ibm_backlight_data = { static struct mutex brightness_mutex; +static int __init tpacpi_query_bcll_levels(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + int rc; + + if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + printk(IBM_ERR "Unknown BCLL data, " + "please report this to %s\n", IBM_MAIL); + rc = 0; + } else { + rc = obj->package.count; + } + } else { + return 0; + } + + kfree(buffer.pointer); + return rc; +} + +static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { sizeof(name), &name }; + + if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && + !strncmp("BCLL", name, sizeof(name) - 1)) { + if (tpacpi_query_bcll_levels(handle) == 16) { + *rv = handle; + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } + } else { + return AE_OK; + } +} + +static int __init brightness_check_levels(void) +{ + int status; + void *found_node = NULL; + + if (!vid_handle) { + IBM_ACPIHANDLE_INIT(vid); + } + if (!vid_handle) + return 0; + + /* Search for a BCLL package with 16 levels */ + status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3, + brightness_find_bcll, NULL, &found_node); + + return (ACPI_SUCCESS(status) && found_node != NULL); +} + static int __init brightness_init(struct ibm_init_struct *iibm) { int b; @@ -3135,10 +3195,17 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (brightness_mode > 3) return -EINVAL; + tp_features.bright_16levels = + thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO && + brightness_check_levels(); + b = brightness_get(NULL); if (b < 0) return 1; + if (tp_features.bright_16levels) + printk(IBM_INFO "detected a 16-level brightness capable ThinkPad\n"); + ibm_backlight_device = backlight_device_register( TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, &ibm_backlight_data); @@ -3148,7 +3215,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm) } vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); - ibm_backlight_device->props.max_brightness = 7; + ibm_backlight_device->props.max_brightness = + (tp_features.bright_16levels)? 15 : 7; ibm_backlight_device->props.brightness = b; backlight_update_status(ibm_backlight_device); @@ -3184,13 +3252,14 @@ static int brightness_get(struct backlight_device *bd) if (brightness_mode & 1) { if (!acpi_ec_read(brightness_offset, &lec)) return -EIO; - lec &= 7; + lec &= (tp_features.bright_16levels)? 0x0f : 0x07; level = lec; }; if (brightness_mode & 2) { lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; + lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07; level = lcmos; } @@ -3211,7 +3280,7 @@ static int brightness_set(int value) int cmos_cmd, inc, i, res; int current_value; - if (value > 7) + if (value > ((tp_features.bright_16levels)? 15 : 7)) return -EINVAL; res = mutex_lock_interruptible(&brightness_mutex); @@ -3227,7 +3296,7 @@ static int brightness_set(int value) cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; - inc = value > current_value ? 1 : -1; + inc = (value > current_value)? 1 : -1; res = 0; for (i = current_value; i != value; i += inc) { @@ -3256,10 +3325,11 @@ static int brightness_read(char *p) 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); + len += sprintf(p + len, "level:\t\t%d\n", level); len += sprintf(p + len, "commands:\tup, down\n"); len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); + " ( is 0-%d)\n", + (tp_features.bright_16levels) ? 15 : 7); } return len; @@ -3270,18 +3340,19 @@ static int brightness_write(char *buf) int level; int new_level; char *cmd; + int max_level = (tp_features.bright_16levels) ? 15 : 7; while ((cmd = next_cmd(&buf))) { if ((level = brightness_get(NULL)) < 0) return level; - level &= 7; if (strlencmp(cmd, "up") == 0) { - new_level = level == 7 ? 7 : level + 1; + new_level = level == (max_level)? + max_level : level + 1; } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0 ? 0 : level - 1; + new_level = level == 0? 0 : level - 1; } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 7) { + new_level >= 0 && new_level <= max_level) { /* new_level set */ } else return -EINVAL; diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 3abcc8120634..8ca19c333727 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -84,7 +84,7 @@ /* ThinkPad CMOS NVRAM constants */ #define TP_NVRAM_ADDR_BRIGHTNESS 0x5e -#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07 +#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x0f #define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0 #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") @@ -246,6 +246,7 @@ static struct { u32 hotkey_wlsw:1; u32 light:1; u32 light_status:1; + u32 bright_16levels:1; u32 wan:1; u32 fan_ctrl_status_undef:1; u32 input_device_registered:1; From 87cc537a54fc017d998cf603f5fab9ca4a85d668 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 18:02:07 -0200 Subject: [PATCH 03/39] ACPI: thinkpad-acpi: add brightness_force parameter Add a "brightness_enable" module parameter that allows the local admin to force the backlight support to not be enabled. It can also be used to force the backlight support to be enabled, but that is currently a no-op as the backlight support is enabled by default when available. This will be changed by a different patch. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 4 ++++ drivers/misc/thinkpad_acpi.c | 9 +++++++++ drivers/misc/thinkpad_acpi.h | 1 + 3 files changed, 14 insertions(+) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index a743bfa62b95..f877c13c5ef8 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -944,6 +944,10 @@ When display backlight brightness controls are available through the standard ACPI interface, it is best to use it instead of this direct ThinkPad-specific interface. +The brightness_enable module parameter can be used to control whether +the LCD brightness control feature will be enabled when available. +brightness_enable=0 forces it to be disabled. + Procfs notes: The available commands are: diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 322ba25b4798..56a21e6b80a9 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -3182,6 +3182,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) mutex_init(&brightness_mutex); + if (!brightness_enable) { + dbg_printk(TPACPI_DBG_INIT, + "brightness support disabled by module parameter\n"); + return 1; + } + if (!brightness_mode) { if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) brightness_mode = 2; @@ -4803,6 +4809,9 @@ module_param_named(fan_control, fan_control_allowed, bool, 0); static int brightness_mode; module_param_named(brightness_mode, brightness_mode, int, 0); +static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ +module_param(brightness_enable, uint, 0); + static unsigned int hotkey_report_mode; module_param(hotkey_report_mode, uint, 0); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 8ca19c333727..8fba2bbe345e 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -339,6 +339,7 @@ static int bluetooth_write(char *buf); static struct backlight_device *ibm_backlight_device; static int brightness_offset = 0x31; static int brightness_mode; +static unsigned int brightness_enable; /* 0 = no, 1 = yes, 2 = auto */ static int brightness_init(struct ibm_init_struct *iibm); static void brightness_exit(void); From e11e211a0b21bbb625fac2056bdb54dd02020556 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:22 -0200 Subject: [PATCH 04/39] ACPI: thinkpad-acpi: prefer standard ACPI backlight level control Newer Lenovo BIOSes support the standard ACPI backlight brightness interface (_BCM, _BQC, _BCL). It should be used instead of the native thinkpad backlight brightness control interface when possible. This patch disables the native brightness support in the driver by default when we detect that the standard ACPI interface is available. The local admin can still enable it using the module parameter "brightness_enable". Note that we need to detect the standard ACPI backlight interface only in boxes for which we would load the native backlight interface in the first place, and that no ThinkPad BIOS has _BCL but misses the other methods, so the detection routines can be really simple. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 8 +++++-- drivers/misc/thinkpad_acpi.c | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index f877c13c5ef8..bb1c7a60b9bb 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -942,11 +942,15 @@ and CMOS. The driver tries to autodetect which interface to use. When display backlight brightness controls are available through the standard ACPI interface, it is best to use it instead of this direct -ThinkPad-specific interface. +ThinkPad-specific interface. The driver will disable its native +backlight brightness control interface if it detects that the standard +ACPI interface is available in the ThinkPad. The brightness_enable module parameter can be used to control whether the LCD brightness control feature will be enabled when available. -brightness_enable=0 forces it to be disabled. +brightness_enable=0 forces it to be disabled. brightness_enable=1 +forces it to be enabled when available, even if the standard ACPI +interface is also available. Procfs notes: diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 56a21e6b80a9..109bd2750439 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -3174,6 +3174,39 @@ static int __init brightness_check_levels(void) return (ACPI_SUCCESS(status) && found_node != NULL); } +static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { sizeof(name), &name }; + + if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && + !strncmp("_BCL", name, sizeof(name) - 1)) { + *rv = handle; + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } +} + +static int __init brightness_check_std_acpi_support(void) +{ + int status; + void *found_node = NULL; + + if (!vid_handle) { + IBM_ACPIHANDLE_INIT(vid); + } + if (!vid_handle) + return 0; + + /* Search for a _BCL method, but don't execute it */ + status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, + brightness_find_bcl, NULL, &found_node); + + return (ACPI_SUCCESS(status) && found_node != NULL); +} + static int __init brightness_init(struct ibm_init_struct *iibm) { int b; @@ -3186,6 +3219,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) dbg_printk(TPACPI_DBG_INIT, "brightness support disabled by module parameter\n"); return 1; + } else if (brightness_enable > 1) { + if (brightness_check_std_acpi_support()) { + printk(IBM_NOTICE + "standard ACPI backlight interface available, not loading native one...\n"); + return 1; + } } if (!brightness_mode) { From b856f5b8c022b75bb0504a8c1ce16a5f1656e08b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:23 -0200 Subject: [PATCH 05/39] ACPI: thinkpad-acpi: bump up version to 0.17 The lm-sensors 3.0.0/libsensors4 compatibility changes are reason enough to bump up the version string. Do it. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/thinkpad-acpi.txt | 4 ++-- drivers/misc/thinkpad_acpi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index bb1c7a60b9bb..10c041ca13c7 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.16 - August 2nd, 2007 + Version 0.17 + October 04th, 2007 Borislav Deianov Henrique de Moraes Holschuh diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 109bd2750439..251110d0ec80 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -21,7 +21,7 @@ * 02110-1301, USA. */ -#define IBM_VERSION "0.16" +#define IBM_VERSION "0.17" #define TPACPI_SYSFS_VERSION 0x020000 /* From fc589a3ce5f38db6239c147da4f9172a25575ecc Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:24 -0200 Subject: [PATCH 06/39] ACPI: thinkpad-acpi: allow for syscall restart in sysfs handlers Map an mutex_lock_interruptible() error return into ERESTARTSYS, as the only possible error from mutex_lock_interruptible is EINTR, and that will only happen if signal_pending() causes the mutex lock attempt to abort. This still allows signals to be delivered ASAP, which is much nicer than just doing mutex_lock, and still shadows userspace from EINTR when SA_RESTART is active. Problem reported by Peter Jordan. Signed-off-by: Henrique de Moraes Holschuh Cc: Jean Delvare Cc: Peter Jordan Cc: Richard Neill Signed-off-by: Len Brown --- drivers/misc/thinkpad_acpi.c | 40 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 251110d0ec80..306daa524c03 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1342,9 +1342,8 @@ static int hotkey_read(char *p) return len; } - res = mutex_lock_interruptible(&hotkey_mutex); - if (res < 0) - return res; + if (mutex_lock_interruptible(&hotkey_mutex)) + return -ERESTARTSYS; res = hotkey_get(&status, &mask); mutex_unlock(&hotkey_mutex); if (res) @@ -1373,9 +1372,8 @@ static int hotkey_write(char *buf) if (!tp_features.hotkey) return -ENODEV; - res = mutex_lock_interruptible(&hotkey_mutex); - if (res < 0) - return res; + if (mutex_lock_interruptible(&hotkey_mutex)) + return -ERESTARTSYS; res = hotkey_get(&status, &mask); if (res) @@ -3768,9 +3766,8 @@ static ssize_t fan_pwm1_store(struct device *dev, /* scale down from 0-255 to 0-7 */ newlevel = (s >> 5) & 0x07; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = fan_get_status(&status); if (!rc && (status & @@ -4020,9 +4017,8 @@ static int fan_get_status_safe(u8 *status) int rc; u8 s; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = fan_get_status(&s); if (!rc) fan_update_desired_level(s); @@ -4156,9 +4152,8 @@ static int fan_set_level_safe(int level) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; if (level == TPACPI_FAN_LAST_LEVEL) level = fan_control_desired_level; @@ -4179,9 +4174,8 @@ static int fan_set_enable(void) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_FANS: @@ -4235,9 +4229,8 @@ static int fan_set_disable(void) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = 0; switch (fan_control_access_mode) { @@ -4274,9 +4267,8 @@ static int fan_set_speed(int speed) if (!fan_control_allowed) return -EPERM; - rc = mutex_lock_interruptible(&fan_mutex); - if (rc < 0) - return rc; + if (mutex_lock_interruptible(&fan_mutex)) + return -ERESTARTSYS; rc = 0; switch (fan_control_access_mode) { From 4273af8d08c823d5898a2b1c2d0f25b4a8b9eaee Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 30 Oct 2007 17:46:25 -0200 Subject: [PATCH 07/39] ACPI: thinkpad-acpi: fix brightness_set error paths The code calling brightness_set() can't handle EINTR/ERESTARTSYS well, nor is it checking brightness_set() return status properly. Fix it. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/misc/thinkpad_acpi.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 306daa524c03..8c9430775285 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -3278,6 +3278,8 @@ static void brightness_exit(void) static int brightness_update_status(struct backlight_device *bd) { + /* it is the backlight class's job (caller) to handle + * EINTR and other errors properly */ return brightness_set( (bd->props.fb_blank == FB_BLANK_UNBLANK && bd->props.power == FB_BLANK_UNBLANK) ? @@ -3318,6 +3320,7 @@ static int brightness_get(struct backlight_device *bd) return level; } +/* May return EINTR which can always be mapped to ERESTARTSYS */ static int brightness_set(int value) { int cmos_cmd, inc, i, res; @@ -3381,29 +3384,34 @@ static int brightness_read(char *p) static int brightness_write(char *buf) { int level; - int new_level; + int rc; char *cmd; int max_level = (tp_features.bright_16levels) ? 15 : 7; - while ((cmd = next_cmd(&buf))) { - if ((level = brightness_get(NULL)) < 0) - return level; + level = brightness_get(NULL); + if (level < 0) + return level; + while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "up") == 0) { - new_level = level == (max_level)? - max_level : level + 1; + if (level < max_level) + level++; } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= max_level) { - /* new_level set */ + if (level > 0) + level--; + } else if (sscanf(cmd, "level %d", &level) == 1 && + level >= 0 && level <= max_level) { + /* new level set */ } else return -EINVAL; - - brightness_set(new_level); } - return 0; + /* + * Now we know what the final level should be, so we try to set it. + * Doing it this way makes the syscall restartable in case of EINTR + */ + rc = brightness_set(level); + return (rc == -EINTR)? ERESTARTSYS : rc; } static struct ibm_struct brightness_driver_data = { From 4c41d3ad6544f1c9aec37c441af04f5d0ad3a731 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 7 Nov 2007 15:09:09 -0800 Subject: [PATCH 08/39] ACPI: Always return valid 'status' from acpi_battery_get_property() If a battery is at a critical charge level and not being charged or discharged, then the ACPI _BST method will return a state of 4, and the current acpi_battery_get_property() code will not set any property value for POWER_SUPPLY_PROP_STATUS. This will cause an oops in power_supply_show_property() when it reads off the end of the status_text array. This actually was causing a 100% reproducible crash on boot on my laptop with two batteries, when one battery was completely drained and the laptop was not plugged in. Fix this by making sure acpi_battery_get_property() returns POWER_SUPPLY_STATUS_UNKNOWN for any battery state it doesn't already handle explicitly. There doesn't seem to be any status enum value defined that makes more sense than 'unknown' for a battery at a critical charge level. Signed-off-by: Roland Dreier Acked-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/battery.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index c2ce0ad21693..cbb27b4ddea4 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -152,6 +152,8 @@ static int acpi_battery_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (battery->state == 0) val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; break; case POWER_SUPPLY_PROP_PRESENT: val->intval = acpi_battery_present(battery); From 91c05c667b2d8e43e0bbc5f269bf45d4821001d6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 5 Nov 2007 11:43:29 -0500 Subject: [PATCH 09/39] ACPI: video - fit input device into sysfs tree Properly set up parent on input device registered by the video driver. Signed-off-by: Dmitry Torokhov Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index bac956b30c57..c0e4e7423fcd 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1961,6 +1961,7 @@ static int acpi_video_bus_add(struct acpi_device *device) input->phys = video->phys; input->id.bustype = BUS_HOST; input->id.product = 0x06; + input->dev.parent = &device->dev; input->evbit[0] = BIT(EV_KEY); set_bit(KEY_SWITCHVIDEOMODE, input->keybit); set_bit(KEY_VIDEO_NEXT, input->keybit); @@ -1990,7 +1991,7 @@ static int acpi_video_bus_add(struct acpi_device *device) video->flags.rom ? "yes" : "no", video->flags.post ? "yes" : "no"); - end: + end: if (result) kfree(video); From f51e83916a0a022d3d0ea39ae2f877c703032923 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 5 Nov 2007 11:43:30 -0500 Subject: [PATCH 10/39] ACPI: video - add missing input_free_device() If input_register_device() fails input_free_device() must be called to release memory allocated for the device. Also consolidate error handling in acpi_bus_video_add() and handle input_allocate_device() failures. Signed-off-by: Dmitry Torokhov Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 69 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index c0e4e7423fcd..be66a7c04d38 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1897,14 +1897,10 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) static int instance; static int acpi_video_bus_add(struct acpi_device *device) { - int result = 0; - acpi_status status = 0; - struct acpi_video_bus *video = NULL; + acpi_status status; + struct acpi_video_bus *video; struct input_dev *input; - - - if (!device) - return -EINVAL; + int error; video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); if (!video) @@ -1923,13 +1919,13 @@ static int acpi_video_bus_add(struct acpi_device *device) acpi_driver_data(device) = video; acpi_video_bus_find_cap(video); - result = acpi_video_bus_check(video); - if (result) - goto end; + error = acpi_video_bus_check(video); + if (error) + goto err_free_video; - result = acpi_video_bus_add_fs(device); - if (result) - goto end; + error = acpi_video_bus_add_fs(device); + if (error) + goto err_free_video; init_MUTEX(&video->sem); INIT_LIST_HEAD(&video->video_device_list); @@ -1943,16 +1939,15 @@ static int acpi_video_bus_add(struct acpi_device *device) if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing notify handler\n")); - acpi_video_bus_stop_devices(video); - acpi_video_bus_put_devices(video); - kfree(video->attached_array); - acpi_video_bus_remove_fs(device); - result = -ENODEV; - goto end; + error = -ENODEV; + goto err_stop_video; } - video->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_uninstall_notify; + } snprintf(video->phys, sizeof(video->phys), "%s/video/input0", acpi_device_hid(video->device)); @@ -1972,18 +1967,10 @@ static int acpi_video_bus_add(struct acpi_device *device) set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); set_bit(KEY_DISPLAY_OFF, input->keybit); set_bit(KEY_UNKNOWN, input->keybit); - result = input_register_device(input); - if (result) { - acpi_remove_notify_handler(video->device->handle, - ACPI_DEVICE_NOTIFY, - acpi_video_bus_notify); - acpi_video_bus_stop_devices(video); - acpi_video_bus_put_devices(video); - kfree(video->attached_array); - acpi_video_bus_remove_fs(device); - goto end; - } + error = input_register_device(input); + if (error) + goto err_free_input_dev; printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), @@ -1991,11 +1978,23 @@ static int acpi_video_bus_add(struct acpi_device *device) video->flags.rom ? "yes" : "no", video->flags.post ? "yes" : "no"); - end: - if (result) - kfree(video); + return 0; - return result; + err_free_input_dev: + input_free_device(input); + err_uninstall_notify: + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_video_bus_notify); + err_stop_video: + acpi_video_bus_stop_devices(video); + acpi_video_bus_put_devices(video); + kfree(video->attached_array); + acpi_video_bus_remove_fs(device); + err_free_video: + kfree(video); + acpi_driver_data(device) = NULL; + + return error; } static int acpi_video_bus_remove(struct acpi_device *device, int type) From ff102ea99099c36250e93a87a9794b5233801020 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 5 Nov 2007 11:43:31 -0500 Subject: [PATCH 11/39] ACPI: video - remove unsafe uses of list_for_each_safe() list_for_each_safe() only protects list from list alterations performed by the same thread. One still needs to implement proper locking when list is being accessed from several threads. Signed-off-by: Dmitry Torokhov Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 71 +++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index be66a7c04d38..36b64a751676 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1462,12 +1462,14 @@ acpi_video_bus_get_one_device(struct acpi_device *device, static void acpi_video_device_rebind(struct acpi_video_bus *video) { - struct list_head *node, *next; - list_for_each_safe(node, next, &video->video_device_list) { - struct acpi_video_device *dev = - container_of(node, struct acpi_video_device, entry); + struct acpi_video_device *dev; + + down(&video->sem); + + list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_device_bind(video, dev); - } + + up(&video->sem); } /* @@ -1592,30 +1594,33 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) static int acpi_video_switch_output(struct acpi_video_bus *video, int event) { - struct list_head *node, *next; + struct list_head *node; struct acpi_video_device *dev = NULL; struct acpi_video_device *dev_next = NULL; struct acpi_video_device *dev_prev = NULL; unsigned long state; int status = 0; + down(&video->sem); - list_for_each_safe(node, next, &video->video_device_list) { + list_for_each(node, &video->video_device_list) { dev = container_of(node, struct acpi_video_device, entry); status = acpi_video_device_get_state(dev, &state); if (state & 0x2) { - dev_next = - container_of(node->next, struct acpi_video_device, - entry); - dev_prev = - container_of(node->prev, struct acpi_video_device, - entry); + dev_next = container_of(node->next, + struct acpi_video_device, entry); + dev_prev = container_of(node->prev, + struct acpi_video_device, entry); goto out; } } + dev_next = container_of(node->next, struct acpi_video_device, entry); dev_prev = container_of(node->prev, struct acpi_video_device, entry); - out: + + out: + up(&video->sem); + switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE: case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: @@ -1691,24 +1696,17 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, struct acpi_device *device) { int status = 0; - struct list_head *node, *next; - + struct acpi_device *dev; acpi_video_device_enumerate(video); - list_for_each_safe(node, next, &device->children) { - struct acpi_device *dev = - list_entry(node, struct acpi_device, node); - - if (!dev) - continue; + list_for_each_entry(dev, &device->children, node) { status = acpi_video_bus_get_one_device(dev, video); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Cant attach device")); continue; } - } return status; } @@ -1724,9 +1722,6 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) video = device->video; - down(&video->sem); - list_del(&device->entry); - up(&video->sem); acpi_video_device_remove_fs(device->dev); status = acpi_remove_notify_handler(device->dev->handle, @@ -1734,32 +1729,34 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) acpi_video_device_notify); backlight_device_unregister(device->backlight); video_output_unregister(device->output_dev); + return 0; } static int acpi_video_bus_put_devices(struct acpi_video_bus *video) { int status; - struct list_head *node, *next; + struct acpi_video_device *dev, *next; + down(&video->sem); - list_for_each_safe(node, next, &video->video_device_list) { - struct acpi_video_device *data = - list_entry(node, struct acpi_video_device, entry); - if (!data) - continue; + list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { - status = acpi_video_bus_put_one_device(data); + status = acpi_video_bus_put_one_device(dev); if (ACPI_FAILURE(status)) printk(KERN_WARNING PREFIX "hhuuhhuu bug in acpi video driver.\n"); - if (data->brightness) - kfree(data->brightness->levels); - kfree(data->brightness); - kfree(data); + if (dev->brightness) { + kfree(dev->brightness->levels); + kfree(dev->brightness); + } + list_del(&dev->entry); + kfree(dev); } + up(&video->sem); + return 0; } From bbac81f5487175e4bd5602a80c17689d8f82a63e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 5 Nov 2007 11:43:32 -0500 Subject: [PATCH 12/39] ACPI: video - convert semaphore to a mutex Signed-off-by: Dmitry Torokhov Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 36b64a751676..d54c83d70532 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -135,8 +136,8 @@ struct acpi_video_bus { u8 attached_count; struct acpi_video_bus_cap cap; struct acpi_video_bus_flags flags; - struct semaphore sem; struct list_head video_device_list; + struct mutex device_list_lock; /* protects video_device_list */ struct proc_dir_entry *dir; struct input_dev *input; char phys[32]; /* for input device */ @@ -1436,9 +1437,9 @@ acpi_video_bus_get_one_device(struct acpi_device *device, return -ENODEV; } - down(&video->sem); + mutex_lock(&video->device_list_lock); list_add_tail(&data->entry, &video->video_device_list); - up(&video->sem); + mutex_unlock(&video->device_list_lock); acpi_video_device_add_fs(device); @@ -1464,12 +1465,12 @@ static void acpi_video_device_rebind(struct acpi_video_bus *video) { struct acpi_video_device *dev; - down(&video->sem); + mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_device_bind(video, dev); - up(&video->sem); + mutex_unlock(&video->device_list_lock); } /* @@ -1601,7 +1602,7 @@ static int acpi_video_switch_output(struct acpi_video_bus *video, int event) unsigned long state; int status = 0; - down(&video->sem); + mutex_lock(&video->device_list_lock); list_for_each(node, &video->video_device_list) { dev = container_of(node, struct acpi_video_device, entry); @@ -1619,7 +1620,7 @@ static int acpi_video_switch_output(struct acpi_video_bus *video, int event) dev_prev = container_of(node->prev, struct acpi_video_device, entry); out: - up(&video->sem); + mutex_unlock(&video->device_list_lock); switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE: @@ -1738,7 +1739,7 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video) int status; struct acpi_video_device *dev, *next; - down(&video->sem); + mutex_lock(&video->device_list_lock); list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { @@ -1755,7 +1756,7 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video) kfree(dev); } - up(&video->sem); + mutex_unlock(&video->device_list_lock); return 0; } @@ -1924,7 +1925,7 @@ static int acpi_video_bus_add(struct acpi_device *device) if (error) goto err_free_video; - init_MUTEX(&video->sem); + mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); acpi_video_bus_get_devices(video, device); From a4f0c2767e9c55123d7dad7176554e9d6e6056bc Mon Sep 17 00:00:00 2001 From: Len Brown Date: Wed, 14 Nov 2007 12:49:13 -0500 Subject: [PATCH 13/39] ACPI: video - delete stray run-time printk printk("video bus notify\n"); Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index d54c83d70532..dce0a6e47f5a 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1780,9 +1780,6 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) struct input_dev *input; int keycode; - - printk("video bus notify\n"); - if (!video) return; From 16ee2db63ef78c387bdda6e0cde3a2051518b6cf Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 9 Nov 2007 07:03:13 +0100 Subject: [PATCH 14/39] x86_64: remove acpi_pci_link_exit() acpi_pci_link_exit() is both unused and empty. Signed-off-by: Adrian Bunk Signed-off-by: Len Brown --- arch/x86/kernel/acpi/sleep_64.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/kernel/acpi/sleep_64.c b/arch/x86/kernel/acpi/sleep_64.c index 79475d237071..da42de261ba8 100644 --- a/arch/x86/kernel/acpi/sleep_64.c +++ b/arch/x86/kernel/acpi/sleep_64.c @@ -115,6 +115,3 @@ static int __init acpi_sleep_setup(char *str) __setup("acpi_sleep=", acpi_sleep_setup); -void acpi_pci_link_exit(void) -{ -} From 55b8d50c1a7b2d53eddaa3114dc55b0ed00df0f3 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 9 Nov 2007 07:02:11 +0100 Subject: [PATCH 15/39] x86: acpi_pciprobe_dmi_table[] must be __devinitdata This patch fixes the following section mismatches with CONFIG_HOTPLUG=n: <-- snip --> ... WARNING: vmlinux.o(.data+0x23640): Section mismatch: reference to .init.text.20:can_skip_ioresource_align (between 'acpi_pciprobe_dmi_table' and 'pcibios_irq_mask') WARNING: vmlinux.o(.data+0x2366c): Section mismatch: reference to .init.text.20:can_skip_ioresource_align (between 'acpi_pciprobe_dmi_table' and 'pcibios_irq_mask') WARNING: vmlinux.o(.data+0x23698): Section mismatch: reference to .init.text.20:can_skip_ioresource_align (between 'acpi_pciprobe_dmi_table' and 'pcibios_irq_mask') ... <-- snip --> Signed-off-by: Adrian Bunk Signed-off-by: Len Brown --- arch/x86/pci/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 2d88f7c6d6ac..a7536dcc2acf 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -13,7 +13,7 @@ static int __devinit can_skip_ioresource_align(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id acpi_pciprobe_dmi_table[] = { +static struct dmi_system_id acpi_pciprobe_dmi_table[] __devinitdata = { /* * Systems where PCI IO resource ISA alignment can be skipped * when the ISA enable bit in the bridge control is not set From ef54d5ad2f58f899be6419fd1090cdeb2439851a Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 16:59:30 +0800 Subject: [PATCH 16/39] ACPI: Enforce T-state limit changes immediately When a T-state limit change notification is received, Linux must evaluate _TPC and change its current T-state immediately to comply with the new limit. Previously, Linux would notice the new limit only upon the next throttling change. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 50 ++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 0b8204e7082a..5fa8ac1c0c84 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -70,7 +70,55 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr) int acpi_processor_tstate_has_changed(struct acpi_processor *pr) { - return acpi_processor_get_platform_limit(pr); + int result = 0; + int throttling_limit; + int current_state; + struct acpi_processor_limit *limit; + int target_state; + + result = acpi_processor_get_platform_limit(pr); + if (result) { + /* Throttling Limit is unsupported */ + return result; + } + + throttling_limit = pr->throttling_platform_limit; + if (throttling_limit >= pr->throttling.state_count) { + /* Uncorrect Throttling Limit */ + return -EINVAL; + } + + current_state = pr->throttling.state; + if (current_state > throttling_limit) { + /* + * The current state can meet the requirement of + * _TPC limit. But it is reasonable that OSPM changes + * t-states from high to low for better performance. + * Of course the limit condition of thermal + * and user should be considered. + */ + limit = &pr->limit; + target_state = throttling_limit; + if (limit->thermal.tx > target_state) + target_state = limit->thermal.tx; + if (limit->user.tx > target_state) + target_state = limit->user.tx; + } else if (current_state == throttling_limit) { + /* + * Unnecessary to change the throttling state + */ + return 0; + } else { + /* + * If the current state is lower than the limit of _TPC, it + * will be forced to switch to the throttling state defined + * by throttling_platfor_limit. + * Because the previous state meets with the limit condition + * of thermal and user, it is unnecessary to check it again. + */ + target_state = throttling_limit; + } + return acpi_processor_set_throttling(pr, target_state); } /* From 49fbabf56dc715bbb51e59742e82ba762790aac0 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:01:06 +0800 Subject: [PATCH 17/39] ACPI: Handle I/O access width requestst that are not a multiple of 8 bits. We've run into BIOS that hand us 4-bit access width requests for T-state control when the code expected only multipls of 8-bits. Round up. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/osl.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index aabc6ca4a81c..e3a673a00845 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -387,17 +387,14 @@ acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) if (!value) value = &dummy; - switch (width) { - case 8: + *value = 0; + if (width <= 8) { *(u8 *) value = inb(port); - break; - case 16: + } else if (width <= 16) { *(u16 *) value = inw(port); - break; - case 32: + } else if (width <= 32) { *(u32 *) value = inl(port); - break; - default: + } else { BUG(); } @@ -408,17 +405,13 @@ EXPORT_SYMBOL(acpi_os_read_port); acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) { - switch (width) { - case 8: + if (width <= 8) { outb(value, port); - break; - case 16: + } else if (width <= 16) { outw(value, port); - break; - case 32: + } else if (width <= 32) { outl(value, port); - break; - default: + } else { BUG(); } From 22cc50199d0616f7b002563a0e9117ba479356e1 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:02:03 +0800 Subject: [PATCH 18/39] ACPI: If _TSS exists, do not access FADT.duty_width Factor out legacy FADT.duty_width code and run it only in the non _TSS case. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 66 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 5fa8ac1c0c84..20d82f55ce5f 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -478,6 +478,40 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) return pr->throttling.acpi_processor_get_throttling(pr); } +static int acpi_processor_get_fadt_info(struct acpi_processor *pr) +{ + int i, step; + + if (!pr->throttling.address) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); + return -EINVAL; + } else if (!pr->throttling.duty_width) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n")); + return -EINVAL; + } + /* TBD: Support duty_cycle values that span bit 4. */ + else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) { + printk(KERN_WARNING PREFIX "duty_cycle spans bit 4\n"); + return -EINVAL; + } + + pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width; + + /* + * Compute state values. Note that throttling displays a linear power + * performance relationship (at 50% performance the CPU will consume + * 50% power). Values are in 1/10th of a percent to preserve accuracy. + */ + + step = (1000 / pr->throttling.state_count); + + for (i = 0; i < pr->throttling.state_count; i++) { + pr->throttling.states[i].performance = 1000 - step * i; + pr->throttling.states[i].power = 1000 - step * i; + } + return 0; +} + static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr, int state) { @@ -591,8 +625,6 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) int acpi_processor_get_throttling_info(struct acpi_processor *pr) { int result = 0; - int step = 0; - int i = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", @@ -611,6 +643,8 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) acpi_processor_get_throttling_states(pr) || acpi_processor_get_platform_limit(pr)) { + if (acpi_processor_get_fadt_info(pr)) + return 0; pr->throttling.acpi_processor_get_throttling = &acpi_processor_get_throttling_fadt; pr->throttling.acpi_processor_set_throttling = @@ -624,19 +658,6 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) acpi_processor_get_tsd(pr); - if (!pr->throttling.address) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); - return 0; - } else if (!pr->throttling.duty_width) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n")); - return 0; - } - /* TBD: Support duty_cycle values that span bit 4. */ - else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) { - printk(KERN_WARNING PREFIX "duty_cycle spans bit 4\n"); - return 0; - } - /* * PIIX4 Errata: We don't support throttling on the original PIIX4. * This shouldn't be an issue as few (if any) mobile systems ever @@ -648,21 +669,6 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) return 0; } - pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width; - - /* - * Compute state values. Note that throttling displays a linear power/ - * performance relationship (at 50% performance the CPU will consume - * 50% power). Values are in 1/10th of a percent to preserve accuracy. - */ - - step = (1000 / pr->throttling.state_count); - - for (i = 0; i < pr->throttling.state_count; i++) { - pr->throttling.states[i].performance = step * i; - pr->throttling.states[i].power = step * i; - } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", pr->throttling.state_count)); From 0753f6e0a3d9568fb6b8e34753b944d9f8eed05b Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:03:46 +0800 Subject: [PATCH 19/39] ACPI: throttle: Change internal APIs better handle _PTC Change the function interface for throttling control via PTC. The following functions are concerned: acpi_read_throttling_status() acpi_write_throttling_state() acpi_get_throttling_value() acpi_get_throttling_state() Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 60 +++++++++++++++++++---------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 20d82f55ce5f..5c96ef18e94c 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -376,16 +376,23 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) return 0; } -static int acpi_read_throttling_status(struct acpi_processor_throttling - *throttling) +static int acpi_read_throttling_status(struct acpi_processor *pr, + acpi_integer *value) { - int value = -1; + u64 ptc_value; + struct acpi_processor_throttling *throttling; + int ret = -1; + + throttling = &pr->throttling; switch (throttling->status_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: + ptc_value = 0; acpi_os_read_port((acpi_io_address) throttling->status_register. - address, &value, + address, (u32 *) &ptc_value, (u32) throttling->status_register.bit_width * 8); + *value = (acpi_integer) ptc_value; + ret = 0; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: printk(KERN_ERR PREFIX @@ -395,18 +402,22 @@ static int acpi_read_throttling_status(struct acpi_processor_throttling printk(KERN_ERR PREFIX "Unknown addr space %d\n", (u32) (throttling->status_register.space_id)); } - return value; + return ret; } -static int acpi_write_throttling_state(struct acpi_processor_throttling - *throttling, int value) +static int acpi_write_throttling_state(struct acpi_processor *pr, + acpi_integer value) { + u64 ptc_value; + struct acpi_processor_throttling *throttling; int ret = -1; + throttling = &pr->throttling; switch (throttling->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: + ptc_value = value; acpi_os_write_port((acpi_io_address) throttling-> - control_register.address, value, + control_register.address, (u32) ptc_value, (u32) throttling->control_register. bit_width * 8); ret = 0; @@ -422,7 +433,8 @@ static int acpi_write_throttling_state(struct acpi_processor_throttling return ret; } -static int acpi_get_throttling_state(struct acpi_processor *pr, int value) +static int acpi_get_throttling_state(struct acpi_processor *pr, + acpi_integer value) { int i; @@ -438,22 +450,26 @@ static int acpi_get_throttling_state(struct acpi_processor *pr, int value) return i; } -static int acpi_get_throttling_value(struct acpi_processor *pr, int state) +static int acpi_get_throttling_value(struct acpi_processor *pr, + int state, acpi_integer *value) { - int value = -1; + int ret = -1; + if (state >= 0 && state <= pr->throttling.state_count) { struct acpi_processor_tx_tss *tx = (struct acpi_processor_tx_tss *)&(pr->throttling. states_tss[state]); - value = tx->control; + *value = tx->control; + ret = 0; } - return value; + return ret; } static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) { int state = 0; - u32 value = 0; + int ret; + acpi_integer value; if (!pr) return -EINVAL; @@ -463,8 +479,9 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) pr->throttling.state = 0; local_irq_disable(); - value = acpi_read_throttling_status(&pr->throttling); - if (value >= 0) { + value = 0; + ret = acpi_read_throttling_status(pr, &value); + if (ret >= 0) { state = acpi_get_throttling_state(pr, value); pr->throttling.state = state; } @@ -588,7 +605,8 @@ static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr, static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, int state) { - u32 value = 0; + int ret; + acpi_integer value; if (!pr) return -EINVAL; @@ -606,10 +624,10 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, return -EPERM; local_irq_disable(); - - value = acpi_get_throttling_value(pr, state); - if (value >= 0) { - acpi_write_throttling_state(&pr->throttling, value); + value = 0; + ret = acpi_get_throttling_value(pr, state, &value); + if (ret >= 0) { + acpi_write_throttling_state(pr, value); pr->throttling.state = state; } local_irq_enable(); From 9bcb27217344c2c1389db3983a436e19484c2f50 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:05:05 +0800 Subject: [PATCH 20/39] ACPI: Use _TSS for throttling control, when present. Add error checks. _TSS was erroneously ignored, in favor of the FADT. When TSS is used, the access width is included in the PTC control/status register. So it is unnecessary that the access bit width is multiplied by 8. At the same time the bit_offset should be considered for system I/O Access. It should be checked the bit_width and bit_offset of PTC regsiter in order to avoid the failure of system I/O access. It means that bit_width plus bit_offset can't be greater than 32. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 42 ++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 5c96ef18e94c..f4b5f7d5dbe6 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -131,6 +131,7 @@ static int acpi_processor_get_throttling_control(struct acpi_processor *pr) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *ptc = NULL; union acpi_object obj = { 0 }; + struct acpi_processor_throttling *throttling; status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -182,6 +183,22 @@ static int acpi_processor_get_throttling_control(struct acpi_processor *pr) memcpy(&pr->throttling.status_register, obj.buffer.pointer, sizeof(struct acpi_ptc_register)); + throttling = &pr->throttling; + + if ((throttling->control_register.bit_width + + throttling->control_register.bit_offset) > 32) { + printk(KERN_ERR PREFIX "Invalid _PTC control register\n"); + result = -EFAULT; + goto end; + } + + if ((throttling->status_register.bit_width + + throttling->status_register.bit_offset) > 32) { + printk(KERN_ERR PREFIX "Invalid _PTC status register\n"); + result = -EFAULT; + goto end; + } + end: kfree(buffer.pointer); @@ -379,7 +396,9 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) static int acpi_read_throttling_status(struct acpi_processor *pr, acpi_integer *value) { + u32 bit_width, bit_offset; u64 ptc_value; + u64 ptc_mask; struct acpi_processor_throttling *throttling; int ret = -1; @@ -387,11 +406,14 @@ static int acpi_read_throttling_status(struct acpi_processor *pr, switch (throttling->status_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: ptc_value = 0; + bit_width = throttling->status_register.bit_width; + bit_offset = throttling->status_register.bit_offset; + acpi_os_read_port((acpi_io_address) throttling->status_register. address, (u32 *) &ptc_value, - (u32) throttling->status_register.bit_width * - 8); - *value = (acpi_integer) ptc_value; + (u32) (bit_width + bit_offset)); + ptc_mask = (1 << bit_width) - 1; + *value = (acpi_integer) ((ptc_value >> bit_offset) & ptc_mask); ret = 0; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: @@ -408,18 +430,24 @@ static int acpi_read_throttling_status(struct acpi_processor *pr, static int acpi_write_throttling_state(struct acpi_processor *pr, acpi_integer value) { + u32 bit_width, bit_offset; u64 ptc_value; + u64 ptc_mask; struct acpi_processor_throttling *throttling; int ret = -1; throttling = &pr->throttling; switch (throttling->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: - ptc_value = value; + bit_width = throttling->control_register.bit_width; + bit_offset = throttling->control_register.bit_offset; + ptc_mask = (1 << bit_width) - 1; + ptc_value = value & ptc_mask; + acpi_os_write_port((acpi_io_address) throttling-> - control_register.address, (u32) ptc_value, - (u32) throttling->control_register. - bit_width * 8); + control_register.address, + (u32) (ptc_value << bit_offset), + (u32) (bit_width + bit_offset)); ret = 0; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: From 0ac3c571315a53c14d2733564f14ebdb911fe903 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:05:46 +0800 Subject: [PATCH 21/39] ACPI: Get throttling info from BIOS only after evaluating _PDC Previously _PDC was evaluated later, and thus we'd not get the chance to tell the BIOS that we can suport FixedHW registers (MSRs) and the BIOS would always ask us to use System I/O access for throttling. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 235a51e328c3..e93318bb029e 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -612,12 +612,6 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) request_region(pr->throttling.address, 6, "ACPI CPU throttle"); } -#ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr); -#endif - acpi_processor_get_throttling_info(pr); - acpi_processor_get_limit_info(pr); - return 0; } @@ -665,6 +659,12 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) /* _PDC call should be done before doing anything else (if reqd.). */ arch_acpi_processor_init_pdc(pr); acpi_processor_set_pdc(pr); +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr); +#endif + acpi_processor_get_throttling_info(pr); + acpi_processor_get_limit_info(pr); + acpi_processor_power_init(pr, device); From f79f06ab9f86d7203006d2ec8992ac80df36a34e Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 15 Nov 2007 17:06:36 +0800 Subject: [PATCH 22/39] ACPI: Enable MSR (FixedHW) support for T-States Add throttling control via MSR when T-states uses the FixHW Control Status registers. Signed-off-by: Zhao Yakui Signed-off-by: Li Shaohua Signed-off-by: Len Brown --- arch/x86/kernel/acpi/processor.c | 3 ++ drivers/acpi/processor_throttling.c | 74 +++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/acpi/processor.c b/arch/x86/kernel/acpi/processor.c index f63e5ff0aca1..a25db514c719 100644 --- a/arch/x86/kernel/acpi/processor.c +++ b/arch/x86/kernel/acpi/processor.c @@ -49,6 +49,9 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) if (cpu_has(c, X86_FEATURE_EST)) buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP; + if (cpu_has(c, X86_FEATURE_ACPI)) + buf[2] |= ACPI_PDC_T_FFH; + obj->type = ACPI_TYPE_BUFFER; obj->buffer.length = 12; obj->buffer.pointer = (u8 *) buf; diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index f4b5f7d5dbe6..c26c61fb36c3 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -393,6 +393,74 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) return 0; } +#ifdef CONFIG_X86 +static int acpi_throttling_rdmsr(struct acpi_processor *pr, + acpi_integer * value) +{ + struct cpuinfo_x86 *c; + u64 msr_high, msr_low; + unsigned int cpu; + u64 msr = 0; + int ret = -1; + + cpu = pr->id; + c = &cpu_data(cpu); + + if ((c->x86_vendor != X86_VENDOR_INTEL) || + !cpu_has(c, X86_FEATURE_ACPI)) { + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + } else { + msr_low = 0; + msr_high = 0; + rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, + (u32 *)&msr_low , (u32 *) &msr_high); + msr = (msr_high << 32) | msr_low; + *value = (acpi_integer) msr; + ret = 0; + } + return ret; +} + +static int acpi_throttling_wrmsr(struct acpi_processor *pr, acpi_integer value) +{ + struct cpuinfo_x86 *c; + unsigned int cpu; + int ret = -1; + u64 msr; + + cpu = pr->id; + c = &cpu_data(cpu); + + if ((c->x86_vendor != X86_VENDOR_INTEL) || + !cpu_has(c, X86_FEATURE_ACPI)) { + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + } else { + msr = value; + wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, + msr & 0xffffffff, msr >> 32); + ret = 0; + } + return ret; +} +#else +static int acpi_throttling_rdmsr(struct acpi_processor *pr, + acpi_integer * value) +{ + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + return -1; +} + +static int acpi_throttling_wrmsr(struct acpi_processor *pr, acpi_integer value) +{ + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + return -1; +} +#endif + static int acpi_read_throttling_status(struct acpi_processor *pr, acpi_integer *value) { @@ -417,8 +485,7 @@ static int acpi_read_throttling_status(struct acpi_processor *pr, ret = 0; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); + ret = acpi_throttling_rdmsr(pr, value); break; default: printk(KERN_ERR PREFIX "Unknown addr space %d\n", @@ -451,8 +518,7 @@ static int acpi_write_throttling_state(struct acpi_processor *pr, ret = 0; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: - printk(KERN_ERR PREFIX - "HARDWARE addr space,NOT supported yet\n"); + ret = acpi_throttling_wrmsr(pr, value); break; default: printk(KERN_ERR PREFIX "Unknown addr space %d\n", From 037cbc63fd83162a8ee0c69680207ce4609adbea Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sun, 18 Nov 2007 00:32:31 +0300 Subject: [PATCH 23/39] ACPI: SBS: Fix retval warning drivers/acpi/sbs.c: In function acpi_battery_add: drivers/acpi/sbs.c:811: warning: ignoring return value of device_create_file, declared with attribute warn_unused_result Additional cleanups: * use struct acpi_battery in acpi_battery_remove() to clean up function calls, just like acpi_battery_add() already does. * put braces around unregister call, as it depends on dev being not NULL. * remove unneeded braces Signed-off-by: Jeff Garzik Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/sbs.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 90fd09c65f95..73c1ba314299 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -113,6 +113,7 @@ struct acpi_battery { u16 spec; u8 id; u8 present:1; + u8 have_sysfs_alarm:1; }; #define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); @@ -808,7 +809,13 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) } battery->bat.get_property = acpi_sbs_battery_get_property; result = power_supply_register(&sbs->device->dev, &battery->bat); - device_create_file(battery->bat.dev, &alarm_attr); + if (result) + goto end; + result = device_create_file(battery->bat.dev, &alarm_attr); + if (result) + goto end; + battery->have_sysfs_alarm = 1; + end: printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), battery->name, sbs->battery->present ? "present" : "absent"); @@ -817,14 +824,16 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) static void acpi_battery_remove(struct acpi_sbs *sbs, int id) { - if (sbs->battery[id].bat.dev) - device_remove_file(sbs->battery[id].bat.dev, &alarm_attr); - power_supply_unregister(&sbs->battery[id].bat); -#ifdef CONFIG_ACPI_PROCFS - if (sbs->battery[id].proc_entry) { - acpi_sbs_remove_fs(&(sbs->battery[id].proc_entry), - acpi_battery_dir); + struct acpi_battery *battery = &sbs->battery[id]; + + if (battery->bat.dev) { + if (battery->have_sysfs_alarm) + device_remove_file(battery->bat.dev, &alarm_attr); + power_supply_unregister(&battery->bat); } +#ifdef CONFIG_ACPI_PROCFS + if (battery->proc_entry) + acpi_sbs_remove_fs(&battery->proc_entry, acpi_battery_dir); #endif } From f2d68935ba08cf80f151bbdb5628381184e4a498 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Mon, 19 Nov 2007 01:37:03 +0300 Subject: [PATCH 24/39] ACPI: EC: Workaround for optimized controllers Some controllers fail to send confirmation GPE after address write. Detect this and don't expect such confirmation in future. This is a generalization of previous workaround (66c5f4e7367b0085652931b2f3366de29e7ff5ec), which did only read address. http://bugzilla.kernel.org/show_bug.cgi?id=9327 Signed-off-by: Alexey Starikovskiy Tested-by: Romano Giannetti Signed-off-by: Len Brown --- drivers/acpi/ec.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 06b78e5e33a1..390f8f8666da 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -75,7 +75,8 @@ enum { EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */ EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_GPE_MODE, /* Expect GPE to be sent for status change */ - EC_FLAGS_ONLY_IBF_GPE, /* Expect GPE only for IBF = 0 event */ + EC_FLAGS_NO_ADDRESS_GPE, /* Expect GPE only for non-address event */ + EC_FLAGS_ADDRESS, /* Address is being written */ }; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -166,38 +167,45 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) { + int ret = 0; + if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) && + test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags))) + force_poll = 1; if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) && likely(!force_poll)) { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event), msecs_to_jiffies(ACPI_EC_DELAY))) - return 0; + goto end; clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (acpi_ec_check_status(ec, event)) { - if (event == ACPI_EC_EVENT_OBF_1) { - /* miss OBF = 1 GPE, don't expect it anymore */ - printk(KERN_INFO PREFIX "missing OBF_1 confirmation," - "switching to degraded mode.\n"); - set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags); + if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) { + /* miss address GPE, don't expect it anymore */ + printk(KERN_INFO PREFIX "missing address confirmation," + "don't expect it any longer.\n"); + set_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags); } else { /* missing GPEs, switch back to poll mode */ - printk(KERN_INFO PREFIX "missing IBF_1 confirmations," + printk(KERN_INFO PREFIX "missing confirmations," "switch off interrupt mode.\n"); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); } - return 0; + goto end; } } else { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event)) - return 0; + goto end; } } printk(KERN_ERR PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", acpi_ec_read_status(ec), event); - return -ETIME; + ret = -ETIME; + end: + clear_bit(EC_FLAGS_ADDRESS, &ec->flags); + return ret; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, @@ -216,6 +224,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, "write_cmd timeout, command = %d\n", command); goto end; } + /* mark the address byte written to EC */ + if (rdata_len + wdata_len > 1) + set_bit(EC_FLAGS_ADDRESS, &ec->flags); set_bit(EC_FLAGS_WAIT_GPE, &ec->flags); acpi_ec_write_data(ec, *(wdata++)); } @@ -231,8 +242,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); for (; rdata_len > 0; --rdata_len) { - if (test_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags)) - force_poll = 1; result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, force_poll); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", From c88c5786d3df51ccfa4e2d111fc9c8fc0f5b2797 Mon Sep 17 00:00:00 2001 From: Danny Baumann Date: Fri, 2 Nov 2007 13:47:53 +0100 Subject: [PATCH 25/39] ACPI: Video: Increase buffer size for writes to brightness proc file. In order to be able to write the value "100" to /proc/acpi/video/.../brightness, we have to allocate 5 bytes: 4 characters will be written (1, 0, 0 plus null byte), and 1 byte should be buffer for a terminating NULL character. http://bugzilla.kernel.org/show_bug.cgi?id=9278 Signed-off-by: Danny Baumann Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index dce0a6e47f5a..44a0d9ba9bd6 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -897,7 +897,7 @@ acpi_video_device_write_brightness(struct file *file, { struct seq_file *m = file->private_data; struct acpi_video_device *dev = m->private; - char str[4] = { 0 }; + char str[5] = { 0 }; unsigned int level = 0; int i; From fdcedbba2f98c94bfbac9f6e712ab765f997b8dc Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Mon, 19 Nov 2007 16:33:45 +0300 Subject: [PATCH 26/39] ACPI: Split out control for /proc/acpi entries from battery, ac, and sbs. Introduce new ACPI_PROCFS_POWER (default Yes) config option and move procfs code in battery, ac, and sbs drivers under it. This is done to allow ACPI_PROCFS to be default No. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 14 ++++++++++++++ drivers/acpi/Makefile | 2 +- drivers/acpi/ac.c | 20 ++++++++++---------- drivers/acpi/battery.c | 20 ++++++++++---------- drivers/acpi/sbs.c | 20 ++++++++++---------- 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 087a7028ae84..497e92d3833b 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -69,7 +69,21 @@ config ACPI_PROCFS and functions which do not yet exist in /sys. Say N to delete /proc/acpi/ files that have moved to /sys/ +config ACPI_PROCFS_POWER + bool "Deprecated power /proc/acpi folders" + depends on PROC_FS + default y + ---help--- + For backwards compatibility, this option allows + deprecated power /proc/acpi/ folders to exist, even when + they have been replaced by functions in /sys. + The deprecated folders (and their replacements) include: + /proc/acpi/battery/* (/sys/class/power_supply/*) + /proc/acpi/ac_adapter/* (sys/class/power_supply/*) + This option has no effect on /proc/acpi/ folders + and functions, which do not yet exist in /sys + Say N to delete power /proc/acpi/ folders that have moved to /sys/ config ACPI_PROC_EVENT bool "Deprecated /proc/acpi/event support" depends on PROC_FS diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 54e3ab0e5fc0..456446f90077 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -58,6 +58,6 @@ obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o -obj-y += cm_sbs.o +obj-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += sbs.o obj-$(CONFIG_ACPI_SBS) += sbshc.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 30238f6ff232..76ed4f52bebd 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -27,7 +27,7 @@ #include #include #include -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER #include #include #endif @@ -51,7 +51,7 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI AC Adapter Driver"); MODULE_LICENSE("GPL"); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER extern struct proc_dir_entry *acpi_lock_ac_dir(void); extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); static int acpi_ac_open_fs(struct inode *inode, struct file *file); @@ -86,7 +86,7 @@ struct acpi_ac { #define to_acpi_ac(x) container_of(x, struct acpi_ac, charger); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER static const struct file_operations acpi_ac_fops = { .open = acpi_ac_open_fs, .read = seq_read, @@ -136,7 +136,7 @@ static int acpi_ac_get_state(struct acpi_ac *ac) return 0; } -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -275,7 +275,7 @@ static int acpi_ac_add(struct acpi_device *device) if (result) goto end; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_ac_add_fs(device); #endif if (result) @@ -300,7 +300,7 @@ static int acpi_ac_add(struct acpi_device *device) end: if (result) { -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_remove_fs(device); #endif kfree(ac); @@ -339,7 +339,7 @@ static int acpi_ac_remove(struct acpi_device *device, int type) ACPI_ALL_NOTIFY, acpi_ac_notify); if (ac->charger.dev) power_supply_unregister(&ac->charger); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_remove_fs(device); #endif @@ -355,7 +355,7 @@ static int __init acpi_ac_init(void) if (acpi_disabled) return -ENODEV; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_dir = acpi_lock_ac_dir(); if (!acpi_ac_dir) return -ENODEV; @@ -363,7 +363,7 @@ static int __init acpi_ac_init(void) result = acpi_bus_register_driver(&acpi_ac_driver); if (result < 0) { -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_ac_dir(acpi_ac_dir); #endif return -ENODEV; @@ -377,7 +377,7 @@ static void __exit acpi_ac_exit(void) acpi_bus_unregister_driver(&acpi_ac_driver); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_ac_dir(acpi_ac_dir); #endif diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 192c244f6190..b871f289dd30 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -31,7 +31,7 @@ #include #include -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER #include #include #include @@ -63,7 +63,7 @@ static unsigned int cache_time = 1000; module_param(cache_time, uint, 0644); MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER extern struct proc_dir_entry *acpi_lock_battery_dir(void); extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); @@ -221,7 +221,7 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER inline char *acpi_battery_units(struct acpi_battery *battery) { return (battery->power_unit)?"mA":"mW"; @@ -479,7 +479,7 @@ static int acpi_battery_update(struct acpi_battery *battery) FS Interface (/proc) -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER static struct proc_dir_entry *acpi_battery_dir; static int acpi_battery_print_info(struct seq_file *seq, int result) @@ -786,7 +786,7 @@ static int acpi_battery_add(struct acpi_device *device) acpi_driver_data(device) = battery; mutex_init(&battery->lock); acpi_battery_update(battery); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_battery_add_fs(device); if (result) goto end; @@ -804,7 +804,7 @@ static int acpi_battery_add(struct acpi_device *device) device->status.battery_present ? "present" : "absent"); end: if (result) { -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif kfree(battery); @@ -823,7 +823,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif sysfs_remove_battery(battery); @@ -859,13 +859,13 @@ static int __init acpi_battery_init(void) { if (acpi_disabled) return -ENODEV; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) return -ENODEV; #endif if (acpi_bus_register_driver(&acpi_battery_driver) < 0) { -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_battery_dir(acpi_battery_dir); #endif return -ENODEV; @@ -876,7 +876,7 @@ static int __init acpi_battery_init(void) static void __exit acpi_battery_exit(void) { acpi_bus_unregister_driver(&acpi_battery_driver); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_battery_dir(acpi_battery_dir); #endif } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 90fd09c65f95..278d20f9366a 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -29,7 +29,7 @@ #include #include -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER #include #include #include @@ -88,7 +88,7 @@ MODULE_DEVICE_TABLE(acpi, sbs_device_ids); struct acpi_battery { struct power_supply bat; struct acpi_sbs *sbs; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER struct proc_dir_entry *proc_entry; #endif unsigned long update_time; @@ -122,7 +122,7 @@ struct acpi_sbs { struct acpi_device *device; struct acpi_smb_hc *hc; struct mutex lock; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER struct proc_dir_entry *charger_entry; #endif struct acpi_battery battery[MAX_SBS_BAT]; @@ -468,7 +468,7 @@ static struct device_attribute alarm_attr = { FS Interface (/proc/acpi) -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER /* Generic Routines */ static int acpi_sbs_add_fs(struct proc_dir_entry **dir, @@ -789,7 +789,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) return result; sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_sbs_add_fs(&battery->proc_entry, acpi_battery_dir, battery->name, &acpi_battery_info_fops, &acpi_battery_state_fops, &acpi_battery_alarm_fops, @@ -820,7 +820,7 @@ static void acpi_battery_remove(struct acpi_sbs *sbs, int id) if (sbs->battery[id].bat.dev) device_remove_file(sbs->battery[id].bat.dev, &alarm_attr); power_supply_unregister(&sbs->battery[id].bat); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER if (sbs->battery[id].proc_entry) { acpi_sbs_remove_fs(&(sbs->battery[id].proc_entry), acpi_battery_dir); @@ -835,7 +835,7 @@ static int acpi_charger_add(struct acpi_sbs *sbs) result = acpi_ac_get_present(sbs); if (result) goto end; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_sbs_add_fs(&sbs->charger_entry, acpi_ac_dir, ACPI_AC_DIR_NAME, NULL, &acpi_ac_state_fops, NULL, sbs); @@ -859,7 +859,7 @@ static void acpi_charger_remove(struct acpi_sbs *sbs) { if (sbs->charger.dev) power_supply_unregister(&sbs->charger); -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER if (sbs->charger_entry) acpi_sbs_remove_fs(&sbs->charger_entry, acpi_ac_dir); #endif @@ -965,7 +965,7 @@ static int acpi_sbs_remove(struct acpi_device *device, int type) static void acpi_sbs_rmdirs(void) { -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER if (acpi_ac_dir) { acpi_unlock_ac_dir(acpi_ac_dir); acpi_ac_dir = NULL; @@ -1004,7 +1004,7 @@ static int __init acpi_sbs_init(void) if (acpi_disabled) return -ENODEV; -#ifdef CONFIG_ACPI_PROCFS +#ifdef CONFIG_ACPI_PROCFS_POWER acpi_ac_dir = acpi_lock_ac_dir(); if (!acpi_ac_dir) return -ENODEV; From 3539a901d60ae84f8b0748cd26c1c263c2b3ef5f Mon Sep 17 00:00:00 2001 From: Len Brown Date: Mon, 19 Nov 2007 11:22:35 -0500 Subject: [PATCH 27/39] Revert "ACPI: add documentation for deprecated /proc/acpi/battery in ACPI_PROCFS" This reverts commit 6e800af233e0bdf108efb7bd23c11ea6fa34cdeb. --- drivers/acpi/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 497e92d3833b..5772e1405f72 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -61,7 +61,6 @@ config ACPI_PROCFS /proc/acpi/info (/sys/modules/acpi/parameters/acpica_version) /proc/acpi/dsdt (/sys/firmware/acpi/tables/DSDT) /proc/acpi/fadt (/sys/firmware/acpi/tables/FACP) - /proc/acpi/battery (/sys/class/power_supply) /proc/acpi/debug_layer (/sys/module/acpi/parameters/debug_layer) /proc/acpi/debug_level (/sys/module/acpi/parameters/debug_level) From 65ea6520375cc09d19ecb46f03ab7ef70bcf06dd Mon Sep 17 00:00:00 2001 From: Len Brown Date: Mon, 19 Nov 2007 11:22:44 -0500 Subject: [PATCH 28/39] Revert "acpi: make ACPI_PROCFS default to y" This reverts commit cbff2fbf55c21f50298b1aef1263b11bf510e35f. --- drivers/acpi/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5772e1405f72..b9f923ef173d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -50,7 +50,6 @@ config ACPI_SLEEP config ACPI_PROCFS bool "Deprecated /proc/acpi files" depends on PROC_FS - default y ---help--- For backwards compatibility, this option allows deprecated /proc/acpi/ files to exist, even when From 5870a8cd23181703cc76f88f630372f8602c7648 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 15 Nov 2007 21:52:47 +0300 Subject: [PATCH 29/39] ACPI: EC: Don't init EC early if it has no _INI Option to init EC early inserted to handle #8598 ASUS problem, introduced several others. EC driver in this particular case has fake _INI method, not present on other machines, which don't need or break from this workaround, so lets use its presence as a flag for early init. http://bugzilla.kernel.org/show_bug.cgi?id=9262 http://bugzilla.kernel.org/show_bug.cgi?id=8598 https://bugzilla.novell.com/show_bug.cgi?id=334806 Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 06b78e5e33a1..56afe13af592 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -881,12 +881,20 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->gpe = ecdt_ptr->gpe; boot_ec->handle = ACPI_ROOT_OBJECT; } else { + /* This workaround is needed only on some broken machines, + * which require early EC, but fail to provide ECDT */ + acpi_handle x; printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, boot_ec, NULL); /* Check that acpi_get_devices actually find something */ if (ACPI_FAILURE(status) || !boot_ec->handle) goto error; + /* We really need to limit this workaround, the only ASUS, + * which needs it, has fake EC._INI method, so use it as flag. + */ + if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &x))) + goto error; } ret = ec_install_handlers(boot_ec); From f0714d20234062bd0a8f49a6b32f7d1d7f3c2943 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Mon, 19 Nov 2007 12:23:59 -0500 Subject: [PATCH 30/39] Revert "Fix very high interrupt rate for IRQ8 (rtc) unless pnpacpi=off" This reverts commit 9cd8047b463f213c294f756119ac353312e7a152. --- drivers/pnp/pnpacpi/rsparser.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index cd0a204d96d1..0e3b8d0ff06b 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -85,16 +85,6 @@ static void pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, if (i >= PNP_MAX_IRQ) return; -#ifdef CONFIG_X86 - if (gsi < 16 && (triggering != ACPI_EDGE_SENSITIVE || - polarity != ACPI_ACTIVE_HIGH)) { - pnp_warn("BIOS BUG: legacy PNP IRQ %d should be edge trigger, " - "active high", gsi); - triggering = ACPI_EDGE_SENSITIVE; - polarity = ACPI_ACTIVE_HIGH; - } -#endif - res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag res->irq_resource[i].flags |= irq_flags(triggering, polarity); irq = acpi_register_gsi(gsi, triggering, polarity); From 59f91ff11e594913a5b3c03a4707fdf02338c8df Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 18 Nov 2007 09:18:29 -0200 Subject: [PATCH 31/39] ACPI: thinkpad-acpi: fix oops when a module parameter has no value set_ibm_param() could OOPS with a NULL pointer derreference if one did not give any values for a module parameter it handles. This would, of course, cause all sort of trouble for future modprobing and require a reboot to clean up properly. Fix it by returning -EINVAL if no values are given for the parameter, and also avoid any nastyness from BUG_ON while at it. How to reproduce: modprobe thinkpad-acpi brightness Signed-off-by: Henrique de Moraes Holschuh Tested-by: Mike Kershaw Signed-off-by: Len Brown --- drivers/misc/thinkpad_acpi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 8c9430775285..ab23a3221585 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -4817,9 +4817,15 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) unsigned int i; struct ibm_struct *ibm; + if (!kp || !kp->name || !val) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ibm = ibms_init[i].data; - BUG_ON(ibm == NULL); + WARN_ON(ibm == NULL); + + if (!ibm || !ibm->name) + continue; if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { if (strlen(val) > sizeof(ibms_init[i].param) - 2) From c9c860e5349ef62cd9226694b3aa625ef66f504e Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 19 Nov 2007 19:48:00 -0500 Subject: [PATCH 32/39] cpuidle: fix C3 for no bus-master control case Port 18eab8550397f1f3d4b8b2c5257c88dae25d58ed (Enable C3 even when PM2_control is zero) to cpuidle. Without this patch, some systems will notice a regression when enabling CPU_IDLE -- C3 would no longer be available. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0cad56ca342b..943a88069001 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1514,23 +1514,38 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, } else { acpi_idle_update_bm_rld(pr, cx); - spin_lock(&c3_lock); - c3_cpu_count++; - /* Disable bus master arbitration when all CPUs are in C3 */ - if (c3_cpu_count == num_online_cpus()) - acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); - spin_unlock(&c3_lock); + /* + * disable bus master + * bm_check implies we need ARB_DIS + * !bm_check implies we need cache flush + * bm_control implies whether we can do ARB_DIS + * + * That leaves a case where bm_check is set and bm_control is + * not set. In that case we cannot do much, we enter C3 + * without doing anything. + */ + if (pr->flags.bm_check && pr->flags.bm_control) { + spin_lock(&c3_lock); + c3_cpu_count++; + /* Disable bus master arbitration when all CPUs are in C3 */ + if (c3_cpu_count == num_online_cpus()) + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); + spin_unlock(&c3_lock); + } else if (!pr->flags.bm_check) { + ACPI_FLUSH_CPU_CACHE(); + } t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); acpi_idle_do_entry(cx); t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - spin_lock(&c3_lock); /* Re-enable bus master arbitration */ - if (c3_cpu_count == num_online_cpus()) + if (pr->flags.bm_check && pr->flags.bm_control) { + spin_lock(&c3_lock); acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); - c3_cpu_count--; - spin_unlock(&c3_lock); + c3_cpu_count--; + spin_unlock(&c3_lock); + } } #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) From 5062911830a66df0c0ad28c387a8c0623cb0d28c Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 19 Nov 2007 19:49:00 -0500 Subject: [PATCH 33/39] cpuidle: add sched_clock_idle_[sleep|wakeup]_event() hooks Port 2aa44d0567ed21b47b87d68819415d48194cb923 (sched: sched_clock_idle_[sleep|wakeup]_event()) to cpuidle. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 943a88069001..1af0694e8520 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1411,6 +1411,8 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, struct acpi_processor *pr; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); u32 t1, t2; + int sleep_ticks = 0; + pr = processors[smp_processor_id()]; if (unlikely(!pr)) @@ -1440,6 +1442,8 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, ACPI_FLUSH_CPU_CACHE(); t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + /* Tell the scheduler that we are going deep-idle: */ + sched_clock_idle_sleep_event(); acpi_state_timer_broadcast(pr, cx, 1); acpi_idle_do_entry(cx); t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); @@ -1448,6 +1452,10 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, /* TSC could halt in idle, so notify users */ mark_tsc_unstable("TSC halts in idle");; #endif + sleep_ticks = ticks_elapsed(t1, t2); + + /* Tell the scheduler how much we idled: */ + sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS); local_irq_enable(); current_thread_info()->status |= TS_POLLING; @@ -1455,7 +1463,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, cx->usage++; acpi_state_timer_broadcast(pr, cx, 0); - cx->time += ticks_elapsed(t1, t2); + cx->time += sleep_ticks; return ticks_elapsed_in_us(t1, t2); } @@ -1475,6 +1483,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, struct acpi_processor *pr; struct acpi_processor_cx *cx = cpuidle_get_statedata(state); u32 t1, t2; + int sleep_ticks = 0; + pr = processors[smp_processor_id()]; if (unlikely(!pr)) @@ -1497,6 +1507,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, return 0; } + /* Tell the scheduler that we are going deep-idle: */ + sched_clock_idle_sleep_event(); /* * Must be done before busmaster disable as we might need to * access HPET ! @@ -1552,6 +1564,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, /* TSC could halt in idle, so notify users */ mark_tsc_unstable("TSC halts in idle"); #endif + sleep_ticks = ticks_elapsed(t1, t2); + /* Tell the scheduler how much we idled: */ + sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS); local_irq_enable(); current_thread_info()->status |= TS_POLLING; @@ -1559,7 +1574,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, cx->usage++; acpi_state_timer_broadcast(pr, cx, 0); - cx->time += ticks_elapsed(t1, t2); + cx->time += sleep_ticks; return ticks_elapsed_in_us(t1, t2); } From ddc081a19585c8ba5aad437779950c2ef215360a Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 19 Nov 2007 21:43:22 -0500 Subject: [PATCH 34/39] cpuidle: fix HP nx6125 regression Fix for http://bugzilla.kernel.org/show_bug.cgi?id=9355 cpuidle always used to fallback to C2 if there is some bm activity while entering C3. But, presence of C2 is not always guaranteed. Change cpuidle algorithm to detect a safe_state to fallback in case of bm_activity and use that state instead of C2. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 125 +++++++++++++++------------------- include/acpi/processor.h | 1 - include/linux/cpuidle.h | 1 + 3 files changed, 56 insertions(+), 71 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 1af0694e8520..8904f5c82a1c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -197,6 +197,19 @@ static inline u32 ticks_elapsed_in_us(u32 t1, u32 t2) return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2); } +static void acpi_safe_halt(void) +{ + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we + * test NEED_RESCHED: + */ + smp_mb(); + if (!need_resched()) + safe_halt(); + current_thread_info()->status |= TS_POLLING; +} + #ifndef CONFIG_CPU_IDLE static void @@ -239,19 +252,6 @@ acpi_processor_power_activate(struct acpi_processor *pr, return; } -static void acpi_safe_halt(void) -{ - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we - * test NEED_RESCHED: - */ - smp_mb(); - if (!need_resched()) - safe_halt(); - current_thread_info()->status |= TS_POLLING; -} - static atomic_t c3_cpu_count; /* Common C-state entry for C2, C3, .. */ @@ -1385,15 +1385,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, if (pr->flags.bm_check) acpi_idle_update_bm_rld(pr, cx); - current_thread_info()->status &= ~TS_POLLING; - /* - * TS_POLLING-cleared state must be visible before we test - * NEED_RESCHED: - */ - smp_mb(); - if (!need_resched()) - safe_halt(); - current_thread_info()->status |= TS_POLLING; + acpi_safe_halt(); cx->usage++; @@ -1493,6 +1485,15 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, if (acpi_idle_suspend) return(acpi_idle_enter_c1(dev, state)); + if (acpi_idle_bm_check()) { + if (dev->safe_state) { + return dev->safe_state->enter(dev, dev->safe_state); + } else { + acpi_safe_halt(); + return 0; + } + } + local_irq_disable(); current_thread_info()->status &= ~TS_POLLING; /* @@ -1515,49 +1516,39 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, */ acpi_state_timer_broadcast(pr, cx, 1); - if (acpi_idle_bm_check()) { - cx = pr->power.bm_state; + acpi_idle_update_bm_rld(pr, cx); - acpi_idle_update_bm_rld(pr, cx); + /* + * disable bus master + * bm_check implies we need ARB_DIS + * !bm_check implies we need cache flush + * bm_control implies whether we can do ARB_DIS + * + * That leaves a case where bm_check is set and bm_control is + * not set. In that case we cannot do much, we enter C3 + * without doing anything. + */ + if (pr->flags.bm_check && pr->flags.bm_control) { + spin_lock(&c3_lock); + c3_cpu_count++; + /* Disable bus master arbitration when all CPUs are in C3 */ + if (c3_cpu_count == num_online_cpus()) + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); + spin_unlock(&c3_lock); + } else if (!pr->flags.bm_check) { + ACPI_FLUSH_CPU_CACHE(); + } - t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); - acpi_idle_do_entry(cx); - t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - } else { - acpi_idle_update_bm_rld(pr, cx); + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - /* - * disable bus master - * bm_check implies we need ARB_DIS - * !bm_check implies we need cache flush - * bm_control implies whether we can do ARB_DIS - * - * That leaves a case where bm_check is set and bm_control is - * not set. In that case we cannot do much, we enter C3 - * without doing anything. - */ - if (pr->flags.bm_check && pr->flags.bm_control) { - spin_lock(&c3_lock); - c3_cpu_count++; - /* Disable bus master arbitration when all CPUs are in C3 */ - if (c3_cpu_count == num_online_cpus()) - acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); - spin_unlock(&c3_lock); - } else if (!pr->flags.bm_check) { - ACPI_FLUSH_CPU_CACHE(); - } - - t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); - acpi_idle_do_entry(cx); - t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); - - /* Re-enable bus master arbitration */ - if (pr->flags.bm_check && pr->flags.bm_control) { - spin_lock(&c3_lock); - acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); - c3_cpu_count--; - spin_unlock(&c3_lock); - } + /* Re-enable bus master arbitration */ + if (pr->flags.bm_check && pr->flags.bm_control) { + spin_lock(&c3_lock); + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); + c3_cpu_count--; + spin_unlock(&c3_lock); } #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) @@ -1626,12 +1617,14 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) case ACPI_STATE_C1: state->flags |= CPUIDLE_FLAG_SHALLOW; state->enter = acpi_idle_enter_c1; + dev->safe_state = state; break; case ACPI_STATE_C2: state->flags |= CPUIDLE_FLAG_BALANCED; state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_simple; + dev->safe_state = state; break; case ACPI_STATE_C3: @@ -1652,14 +1645,6 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) if (!count) return -EINVAL; - /* find the deepest state that can handle active BM */ - if (pr->flags.bm_check) { - for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) - if (pr->power.states[i].type == ACPI_STATE_C3) - break; - pr->power.bm_state = &pr->power.states[i-1]; - } - return 0; } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 26d79f6db8a0..76411b1fc4fd 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -78,7 +78,6 @@ struct acpi_processor_cx { struct acpi_processor_power { struct cpuidle_device dev; struct acpi_processor_cx *state; - struct acpi_processor_cx *bm_state; unsigned long bm_check_timestamp; u32 default_state; u32 bm_activity; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 16a51546db44..c4e00161a247 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -92,6 +92,7 @@ struct cpuidle_device { struct kobject kobj; struct completion kobj_unregister; void *governor_data; + struct cpuidle_state *safe_state; }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); From 4fdb2a05ef5703553fdd28f1b96ebdd79f173657 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 19 Nov 2007 17:48:02 -0800 Subject: [PATCH 35/39] ACPI: Add missing spaces to printk format Signed-off-by: Joe Perches Signed-off-by: Len Brown --- drivers/acpi/ec.c | 4 ++-- drivers/acpi/processor_core.c | 2 +- drivers/acpi/tables/tbutils.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 06b78e5e33a1..cb09e5e03400 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -175,12 +175,12 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) if (acpi_ec_check_status(ec, event)) { if (event == ACPI_EC_EVENT_OBF_1) { /* miss OBF = 1 GPE, don't expect it anymore */ - printk(KERN_INFO PREFIX "missing OBF_1 confirmation," + printk(KERN_INFO PREFIX "missing OBF_1 confirmation, " "switching to degraded mode.\n"); set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags); } else { /* missing GPEs, switch back to poll mode */ - printk(KERN_INFO PREFIX "missing IBF_1 confirmations," + printk(KERN_INFO PREFIX "missing IBF_1 confirmations, " "switch off interrupt mode.\n"); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); } diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 235a51e328c3..44156e73de61 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -647,7 +647,7 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) */ if (processor_device_array[pr->id] != NULL && processor_device_array[pr->id] != device) { - printk(KERN_WARNING "BIOS reported wrong ACPI id" + printk(KERN_WARNING "BIOS reported wrong ACPI id " "for the processor\n"); return -ENODEV; } diff --git a/drivers/acpi/tables/tbutils.c b/drivers/acpi/tables/tbutils.c index 5f1d85f2ffe4..010f19652f80 100644 --- a/drivers/acpi/tables/tbutils.c +++ b/drivers/acpi/tables/tbutils.c @@ -449,7 +449,7 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags) /* XSDT has NULL entry, RSDT is used */ address = rsdt_address; table_entry_size = sizeof(u32); - ACPI_WARNING((AE_INFO, "BIOS XSDT has NULL entry," + ACPI_WARNING((AE_INFO, "BIOS XSDT has NULL entry, " "using RSDT")); } } From 61fd47e0c84764f49b4e52bfd8170fac52636f00 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Sat, 17 Nov 2007 01:05:28 -0500 Subject: [PATCH 36/39] ACPI: fix two IRQ8 issues in IOAPIC mode Use mp_irqs[] to get PNP device's interrupt polarity and trigger. There are two reasons to do this: 1. BIOS bug for PNP interrupt 2. BIOS explictly does override mp_irqs[] should cover all the cases. http://bugzilla.kernel.org/show_bug.cgi?id=5243 http://bugzilla.kernel.org/show_bug.cgi?id=7679 http://bugzilla.kernel.org/show_bug.cgi?id=9153 [lenb: fixed !IOAPIC and 64-bit !SMP builds] Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- arch/x86/kernel/io_apic_32.c | 21 ++++++++++++++++++++- arch/x86/kernel/io_apic_64.c | 24 ++++++++++++++++++++++-- drivers/pnp/pnpacpi/rsparser.c | 18 ++++++++++++++++++ include/linux/acpi.h | 5 +++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c index f35c6eb33da9..6bb80ea5f4ee 100644 --- a/arch/x86/kernel/io_apic_32.c +++ b/arch/x86/kernel/io_apic_32.c @@ -962,7 +962,7 @@ static int EISA_ELCR(unsigned int irq) #define default_MCA_trigger(idx) (1) #define default_MCA_polarity(idx) (0) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2830,6 +2830,25 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a return 0; } +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + #endif /* CONFIG_ACPI */ static int __init parse_disable_timer_pin_1(char *arg) diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index 953328b55a30..435a8c9b55f8 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -546,7 +546,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) #define default_PCI_trigger(idx) (1) #define default_PCI_polarity(idx) (1) -static int __init MPBIOS_polarity(int idx) +static int MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; int polarity; @@ -2222,8 +2222,27 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p return 0; } -#endif /* CONFIG_ACPI */ +int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) +{ + int i; + + if (skip_ioapic_setup) + return -1; + + for (i = 0; i < mp_irq_entries; i++) + if (mp_irqs[i].mpc_irqtype == mp_INT && + mp_irqs[i].mpc_srcbusirq == bus_irq) + break; + if (i >= mp_irq_entries) + return -1; + + *trigger = irq_trigger(i); + *polarity = irq_polarity(i); + return 0; +} + +#endif /* CONFIG_ACPI */ /* * This function currently is only a helper for the i386 smp boot process where @@ -2260,3 +2279,4 @@ void __init setup_ioapic_dest(void) } } #endif + diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 0e3b8d0ff06b..11adab13f2b7 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -75,6 +75,7 @@ static void pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, { int i = 0; int irq; + int p, t; if (!valid_IRQ(gsi)) return; @@ -85,6 +86,23 @@ static void pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, if (i >= PNP_MAX_IRQ) return; + /* + * in IO-APIC mode, use overrided attribute. Two reasons: + * 1. BIOS bug in DSDT + * 2. BIOS uses IO-APIC mode Interrupt Source Override + */ + if (!acpi_get_override_irq(gsi, &t, &p)) { + t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; + p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; + + if (triggering != t || polarity != p) { + pnp_warn("IRQ %d override to %s, %s", + gsi, t ? "edge":"level", p ? "low":"high"); + triggering = t; + polarity = p; + } + } + res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag res->irq_resource[i].flags |= irq_flags(triggering, polarity); irq = acpi_register_gsi(gsi, triggering, polarity); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8ccedf7a0a5a..e3c16c981e46 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -132,6 +132,11 @@ extern unsigned long acpi_realmode_flags; int acpi_register_gsi (u32 gsi, int triggering, int polarity); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); +#ifdef CONFIG_X86_IO_APIC +extern int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity); +#else +#define acpi_get_override_irq(bus, trigger, polarity) (-1) +#endif /* * This function undoes the effect of one call to acpi_register_gsi(). * If this matches the last registration, any IRQ resources for gsi From 0af2f653c504d302d83d3a648c0408882ff62d4c Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 20 Nov 2007 19:59:08 -0500 Subject: [PATCH 37/39] Revert "ACPI: EC: Workaround for optimized controllers" This reverts commit f2d68935ba08cf80f151bbdb5628381184e4a498. --- drivers/acpi/ec.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 390f8f8666da..06b78e5e33a1 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -75,8 +75,7 @@ enum { EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */ EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_GPE_MODE, /* Expect GPE to be sent for status change */ - EC_FLAGS_NO_ADDRESS_GPE, /* Expect GPE only for non-address event */ - EC_FLAGS_ADDRESS, /* Address is being written */ + EC_FLAGS_ONLY_IBF_GPE, /* Expect GPE only for IBF = 0 event */ }; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -167,45 +166,38 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) { - int ret = 0; - if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) && - test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags))) - force_poll = 1; if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) && likely(!force_poll)) { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event), msecs_to_jiffies(ACPI_EC_DELAY))) - goto end; + return 0; clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (acpi_ec_check_status(ec, event)) { - if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) { - /* miss address GPE, don't expect it anymore */ - printk(KERN_INFO PREFIX "missing address confirmation," - "don't expect it any longer.\n"); - set_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags); + if (event == ACPI_EC_EVENT_OBF_1) { + /* miss OBF = 1 GPE, don't expect it anymore */ + printk(KERN_INFO PREFIX "missing OBF_1 confirmation," + "switching to degraded mode.\n"); + set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags); } else { /* missing GPEs, switch back to poll mode */ - printk(KERN_INFO PREFIX "missing confirmations," + printk(KERN_INFO PREFIX "missing IBF_1 confirmations," "switch off interrupt mode.\n"); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); } - goto end; + return 0; } } else { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event)) - goto end; + return 0; } } printk(KERN_ERR PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", acpi_ec_read_status(ec), event); - ret = -ETIME; - end: - clear_bit(EC_FLAGS_ADDRESS, &ec->flags); - return ret; + return -ETIME; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, @@ -224,9 +216,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, "write_cmd timeout, command = %d\n", command); goto end; } - /* mark the address byte written to EC */ - if (rdata_len + wdata_len > 1) - set_bit(EC_FLAGS_ADDRESS, &ec->flags); set_bit(EC_FLAGS_WAIT_GPE, &ec->flags); acpi_ec_write_data(ec, *(wdata++)); } @@ -242,6 +231,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); for (; rdata_len > 0; --rdata_len) { + if (test_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags)) + force_poll = 1; result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, force_poll); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", From 3ebe08a749a0971a5407818169dc16212ef562f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Wed, 21 Nov 2007 03:23:26 +0300 Subject: [PATCH 38/39] ACPI: EC: use printk_ratelimit(), add some DEBUG mode messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it is usefull to see raw protocol dump. Uncomment '#define DEBUG' at the beginning of file to make EC really verbose. Signed-off-by: Márton Németh Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 50 +++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 06b78e5e33a1..d5a5958d4b20 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -47,6 +47,9 @@ #undef PREFIX #define PREFIX "ACPI: EC: " +/* Uncomment next line to get verbose print outs*/ +/* #define DEBUG */ + /* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ @@ -131,21 +134,27 @@ static struct acpi_ec { static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { - return inb(ec->command_addr); + u8 x = inb(ec->command_addr); + pr_debug(PREFIX "---> status = 0x%2x\n", x); + return x; } static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { + u8 x = inb(ec->data_addr); + pr_debug(PREFIX "---> data = 0x%2x\n", x); return inb(ec->data_addr); } static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { + pr_debug(PREFIX "<--- command = 0x%2x\n", command); outb(command, ec->command_addr); } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { + pr_debug(PREFIX "<--- data = 0x%2x\n", data); outb(data, ec->data_addr); } @@ -175,13 +184,14 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) if (acpi_ec_check_status(ec, event)) { if (event == ACPI_EC_EVENT_OBF_1) { /* miss OBF = 1 GPE, don't expect it anymore */ - printk(KERN_INFO PREFIX "missing OBF_1 confirmation," + pr_info(PREFIX "missing OBF_1 confirmation," "switching to degraded mode.\n"); set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags); } else { /* missing GPEs, switch back to poll mode */ - printk(KERN_INFO PREFIX "missing IBF_1 confirmations," - "switch off interrupt mode.\n"); + if (printk_ratelimit()) + pr_info(PREFIX "missing IBF_1 confirmations," + "switch off interrupt mode.\n"); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); } return 0; @@ -194,7 +204,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) return 0; } } - printk(KERN_ERR PREFIX "acpi_ec_wait timeout," + pr_err(PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", acpi_ec_read_status(ec), event); return -ETIME; @@ -208,11 +218,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, int result = 0; set_bit(EC_FLAGS_WAIT_GPE, &ec->flags); acpi_ec_write_cmd(ec, command); - + pr_debug(PREFIX "transaction start\n"); for (; wdata_len > 0; --wdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll); if (result) { - printk(KERN_ERR PREFIX + pr_err(PREFIX "write_cmd timeout, command = %d\n", command); goto end; } @@ -223,7 +233,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, if (!rdata_len) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll); if (result) { - printk(KERN_ERR PREFIX + pr_err(PREFIX "finish-write timeout, command = %d\n", command); goto end; } @@ -235,8 +245,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, force_poll = 1; result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, force_poll); if (result) { - printk(KERN_ERR PREFIX "read timeout, command = %d\n", - command); + pr_err(PREFIX "read timeout, command = %d\n", command); goto end; } /* Don't expect GPE after last read */ @@ -245,6 +254,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, *(rdata++) = acpi_ec_read_data(ec); } end: + pr_debug(PREFIX "transaction end\n"); return result; } @@ -273,8 +283,8 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0); if (status) { - printk(KERN_ERR PREFIX - "input buffer is not empty, aborting transaction\n"); + pr_err(PREFIX "input buffer is not empty, " + "aborting transaction\n"); goto end; } @@ -488,6 +498,7 @@ static u32 acpi_ec_gpe_handler(void *data) acpi_status status = AE_OK; struct acpi_ec *ec = data; + pr_debug(PREFIX "~~~> interrupt\n"); clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) wake_up(&ec->wait); @@ -498,8 +509,9 @@ static u32 acpi_ec_gpe_handler(void *data) acpi_ec_gpe_query, ec); } else if (unlikely(!test_bit(EC_FLAGS_GPE_MODE, &ec->flags))) { /* this is non-query, must be confirmation */ - printk(KERN_INFO PREFIX "non-query interrupt received," - " switching to interrupt mode\n"); + if (printk_ratelimit()) + pr_info(PREFIX "non-query interrupt received," + " switching to interrupt mode\n"); set_bit(EC_FLAGS_GPE_MODE, &ec->flags); } @@ -701,10 +713,10 @@ static void ec_remove_handlers(struct acpi_ec *ec) { if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) - printk(KERN_ERR PREFIX "failed to remove space handler\n"); + pr_err(PREFIX "failed to remove space handler\n"); if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler))) - printk(KERN_ERR PREFIX "failed to remove gpe handler\n"); + pr_err(PREFIX "failed to remove gpe handler\n"); ec->handlers_installed = 0; } @@ -747,9 +759,9 @@ static int acpi_ec_add(struct acpi_device *device) first_ec = ec; acpi_driver_data(device) = ec; acpi_ec_add_fs(device); - printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", + pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", ec->gpe, ec->command_addr, ec->data_addr); - printk(KERN_INFO PREFIX "driver started in %s mode\n", + pr_info(PREFIX "driver started in %s mode\n", (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))?"interrupt":"poll"); return 0; } @@ -875,7 +887,7 @@ int __init acpi_ec_ecdt_probe(void) status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { - printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n"); + pr_info(PREFIX "EC description table is found, configuring boot EC\n"); boot_ec->command_addr = ecdt_ptr->control.address; boot_ec->data_addr = ecdt_ptr->data.address; boot_ec->gpe = ecdt_ptr->gpe; From e790cc8bbb990df900eabdda18a5a480d22a60c8 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Wed, 21 Nov 2007 03:23:32 +0300 Subject: [PATCH 39/39] ACPI: EC: Workaround for optimized controllers (version 3) Some controllers fail to send confirmation GPE after address or data write. Detect this and don't expect such confirmation in future. This is a generalization of previous workaround (66c5f4e7367b0085652931b2f3366de29e7ff5ec), which did only read address. Reference: http://bugzilla.kernel.org/show_bug.cgi?id=9327 Signed-off-by: Alexey Starikovskiy Tested-by: Romano Giannetti Tested-by: Mats Johannesson Signed-off-by: Len Brown --- drivers/acpi/ec.c | 46 +++++++++++++++++++++++++---------- drivers/acpi/processor_core.c | 5 +++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d5a5958d4b20..7d6b84119792 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -78,7 +78,10 @@ enum { EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */ EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_GPE_MODE, /* Expect GPE to be sent for status change */ - EC_FLAGS_ONLY_IBF_GPE, /* Expect GPE only for IBF = 0 event */ + EC_FLAGS_NO_ADDRESS_GPE, /* Expect GPE only for non-address event */ + EC_FLAGS_ADDRESS, /* Address is being written */ + EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */ + EC_FLAGS_WDATA, /* Data is being written */ }; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -175,39 +178,54 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) { + int ret = 0; + if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) && + test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags))) + force_poll = 1; + if (unlikely(test_bit(EC_FLAGS_WDATA, &ec->flags) && + test_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags))) + force_poll = 1; if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) && likely(!force_poll)) { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event), msecs_to_jiffies(ACPI_EC_DELAY))) - return 0; + goto end; clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (acpi_ec_check_status(ec, event)) { - if (event == ACPI_EC_EVENT_OBF_1) { - /* miss OBF = 1 GPE, don't expect it anymore */ - pr_info(PREFIX "missing OBF_1 confirmation," - "switching to degraded mode.\n"); - set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags); + if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) { + /* miss address GPE, don't expect it anymore */ + pr_info(PREFIX "missing address confirmation, " + "don't expect it any longer.\n"); + set_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags); + } else if (test_bit(EC_FLAGS_WDATA, &ec->flags)) { + /* miss write data GPE, don't expect it */ + pr_info(PREFIX "missing write data confirmation, " + "don't expect it any longer.\n"); + set_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags); } else { /* missing GPEs, switch back to poll mode */ if (printk_ratelimit()) - pr_info(PREFIX "missing IBF_1 confirmations," + pr_info(PREFIX "missing confirmations, " "switch off interrupt mode.\n"); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); } - return 0; + goto end; } } else { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event)) - return 0; + goto end; } } pr_err(PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", acpi_ec_read_status(ec), event); - return -ETIME; + ret = -ETIME; + end: + clear_bit(EC_FLAGS_ADDRESS, &ec->flags); + return ret; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, @@ -226,11 +244,15 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, "write_cmd timeout, command = %d\n", command); goto end; } + /* mark the address byte written to EC */ + if (rdata_len + wdata_len > 1) + set_bit(EC_FLAGS_ADDRESS, &ec->flags); set_bit(EC_FLAGS_WAIT_GPE, &ec->flags); acpi_ec_write_data(ec, *(wdata++)); } if (!rdata_len) { + set_bit(EC_FLAGS_WDATA, &ec->flags); result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll); if (result) { pr_err(PREFIX @@ -241,8 +263,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); for (; rdata_len > 0; --rdata_len) { - if (test_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags)) - force_poll = 1; result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, force_poll); if (result) { pr_err(PREFIX "read timeout, command = %d\n", command); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 235a51e328c3..692b2a0a05b2 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -684,7 +684,7 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) { struct acpi_processor *pr = data; struct acpi_device *device = NULL; - + int saved; if (!pr) return; @@ -694,7 +694,10 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) switch (event) { case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: + saved = pr->performance_platform_limit; acpi_processor_ppc_has_changed(pr); + if (saved == pr->performance_platform_limit) + break; acpi_bus_generate_proc_event(device, event, pr->performance_platform_limit); acpi_bus_generate_netlink_event(device->pnp.device_class,