diff options
Diffstat (limited to 'drivers/acpi/thermal.c')
-rw-r--r-- | drivers/acpi/thermal.c | 234 |
1 files changed, 190 insertions, 44 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 88a6fc7fd271..ad898e10c1a9 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -33,6 +33,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/dmi.h> #include <linux/init.h> #include <linux/types.h> #include <linux/proc_fs.h> @@ -40,6 +41,7 @@ #include <linux/jiffies.h> #include <linux/kmod.h> #include <linux/seq_file.h> +#include <linux/reboot.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -59,7 +61,6 @@ #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 #define ACPI_THERMAL_NOTIFY_HOT 0xF1 #define ACPI_THERMAL_MODE_ACTIVE 0x00 -#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" #define ACPI_THERMAL_MAX_ACTIVE 10 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 @@ -74,9 +75,29 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); MODULE_LICENSE("GPL"); +static int act; +module_param(act, int, 0644); +MODULE_PARM_DESC(act, "Disable or override all lowest active trip points."); + +static int crt; +module_param(crt, int, 0644); +MODULE_PARM_DESC(crt, "Disable or lower all critical trip points."); + static int tzp; -module_param(tzp, int, 0); -MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); +module_param(tzp, int, 0444); +MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds."); + +static int nocrt; +module_param(nocrt, int, 0); +MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points."); + +static int off; +module_param(off, int, 0); +MODULE_PARM_DESC(off, "Set to disable ACPI thermal support."); + +static int psv; +module_param(psv, int, 0644); +MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device, int type); @@ -92,10 +113,16 @@ static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file); static ssize_t acpi_thermal_write_polling(struct file *, const char __user *, size_t, loff_t *); +static const struct acpi_device_id thermal_device_ids[] = { + {ACPI_THERMAL_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, thermal_device_ids); + static struct acpi_driver acpi_thermal_driver = { .name = "thermal", .class = ACPI_THERMAL_CLASS, - .ids = ACPI_THERMAL_HID, + .ids = thermal_device_ids, .ops = { .add = acpi_thermal_add, .remove = acpi_thermal_remove, @@ -317,6 +344,20 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) tz->trips.critical.temperature)); } + if (tz->trips.critical.flags.valid == 1) { + if (crt == -1) { + tz->trips.critical.flags.valid = 0; + } else if (crt > 0) { + unsigned long crt_k = CELSIUS_TO_KELVIN(crt); + + /* + * Allow override to lower critical threshold + */ + if (crt_k < tz->trips.critical.temperature) + tz->trips.critical.temperature = crt_k; + } + } + /* Critical Sleep (optional) */ status = @@ -333,9 +374,16 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) /* Passive: Processors (optional) */ - status = - acpi_evaluate_integer(tz->device->handle, "_PSV", NULL, - &tz->trips.passive.temperature); + if (psv == -1) { + status = AE_SUPPORT; + } else if (psv > 0) { + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); + status = AE_OK; + } else { + status = acpi_evaluate_integer(tz->device->handle, + "_PSV", NULL, &tz->trips.passive.temperature); + } + if (ACPI_FAILURE(status)) { tz->trips.passive.flags.valid = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); @@ -380,11 +428,33 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; - status = - acpi_evaluate_integer(tz->device->handle, name, NULL, - &tz->trips.active[i].temperature); - if (ACPI_FAILURE(status)) + if (act == -1) + break; /* disable all active trip points */ + + status = acpi_evaluate_integer(tz->device->handle, + name, NULL, &tz->trips.active[i].temperature); + + if (ACPI_FAILURE(status)) { + if (i == 0) /* no active trip points */ + break; + if (act <= 0) /* no override requested */ + break; + if (i == 1) { /* 1 trip point */ + tz->trips.active[0].temperature = + CELSIUS_TO_KELVIN(act); + } else { /* multiple trips */ + /* + * Don't allow override higher than + * the next higher trip point + */ + tz->trips.active[i - 1].temperature = + (tz->trips.active[i - 2].temperature < + CELSIUS_TO_KELVIN(act) ? + tz->trips.active[i - 2].temperature : + CELSIUS_TO_KELVIN(act)); + } break; + } name[2] = 'L'; status = @@ -419,29 +489,9 @@ static int acpi_thermal_get_devices(struct acpi_thermal *tz) return 0; } -static int acpi_thermal_call_usermode(char *path) -{ - char *argv[2] = { NULL, NULL }; - char *envp[3] = { NULL, NULL, NULL }; - - - if (!path) - return -EINVAL; - - argv[0] = path; - - /* minimal command environment */ - envp[0] = "HOME=/"; - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - call_usermodehelper(argv[0], argv, envp, 0); - - return 0; -} - static int acpi_thermal_critical(struct acpi_thermal *tz) { - if (!tz || !tz->trips.critical.flags.valid) + if (!tz || !tz->trips.critical.flags.valid || nocrt) return -EINVAL; if (tz->temperature >= tz->trips.critical.temperature) { @@ -453,17 +503,21 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) printk(KERN_EMERG "Critical temperature reached (%ld C), shutting down.\n", KELVIN_TO_CELSIUS(tz->temperature)); - acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, + acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); + acpi_bus_generate_netlink_event(tz->device->pnp.device_class, + tz->device->dev.bus_id, + ACPI_THERMAL_NOTIFY_CRITICAL, + tz->trips.critical.flags.enabled); - acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF); + orderly_poweroff(true); return 0; } static int acpi_thermal_hot(struct acpi_thermal *tz) { - if (!tz || !tz->trips.hot.flags.valid) + if (!tz || !tz->trips.hot.flags.valid || nocrt) return -EINVAL; if (tz->temperature >= tz->trips.hot.temperature) { @@ -472,8 +526,12 @@ static int acpi_thermal_hot(struct acpi_thermal *tz) } else if (tz->trips.hot.flags.enabled) tz->trips.hot.flags.enabled = 0; - acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_HOT, + acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); + acpi_bus_generate_netlink_event(tz->device->pnp.device_class, + tz->device->dev.bus_id, + ACPI_THERMAL_NOTIFY_HOT, + tz->trips.hot.flags.enabled); /* TBD: Call user-mode "sleep(S4)" function */ @@ -838,12 +896,14 @@ static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) goto end; if (tz->trips.critical.flags.valid) - seq_printf(seq, "critical (S5): %ld C\n", - KELVIN_TO_CELSIUS(tz->trips.critical.temperature)); + seq_printf(seq, "critical (S5): %ld C%s", + KELVIN_TO_CELSIUS(tz->trips.critical.temperature), + nocrt ? " <disabled>\n" : "\n"); if (tz->trips.hot.flags.valid) - seq_printf(seq, "hot (S4): %ld C\n", - KELVIN_TO_CELSIUS(tz->trips.hot.temperature)); + seq_printf(seq, "hot (S4): %ld C%s", + KELVIN_TO_CELSIUS(tz->trips.hot.temperature), + nocrt ? " <disabled>\n" : "\n"); if (tz->trips.passive.flags.valid) { seq_printf(seq, @@ -1033,9 +1093,9 @@ static int acpi_thermal_add_fs(struct acpi_device *device) entry->owner = THIS_MODULE; } - /* 'trip_points' [R/W] */ + /* 'trip_points' [R] */ entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, - S_IFREG | S_IRUGO | S_IWUSR, + S_IRUGO, acpi_device_dir(device)); if (!entry) return -ENODEV; @@ -1115,12 +1175,16 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) case ACPI_THERMAL_NOTIFY_THRESHOLDS: acpi_thermal_get_trip_points(tz); acpi_thermal_check(tz); - acpi_bus_generate_event(device, event, 0); + acpi_bus_generate_proc_event(device, event, 0); + acpi_bus_generate_netlink_event(device->pnp.device_class, + device->dev.bus_id, event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: if (tz->flags.devices) acpi_thermal_get_devices(tz); - acpi_bus_generate_event(device, event, 0); + acpi_bus_generate_proc_event(device, event, 0); + acpi_bus_generate_netlink_event(device->pnp.device_class, + device->dev.bus_id, event, 0); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -1295,11 +1359,93 @@ static int acpi_thermal_resume(struct acpi_device *device) return AE_OK; } +#ifdef CONFIG_DMI +static int thermal_act(const struct dmi_system_id *d) { + + if (act == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "disabling all active thermal trip points\n", d->ident); + act = -1; + } + return 0; +} +static int thermal_nocrt(const struct dmi_system_id *d) { + + printk(KERN_NOTICE "ACPI: %s detected: " + "disabling all critical thermal trip point actions.\n", d->ident); + nocrt = 1; + return 0; +} +static int thermal_tzp(const struct dmi_system_id *d) { + + if (tzp == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "enabling thermal zone polling\n", d->ident); + tzp = 300; /* 300 dS = 30 Seconds */ + } + return 0; +} +static int thermal_psv(const struct dmi_system_id *d) { + + if (psv == 0) { + printk(KERN_NOTICE "ACPI: %s detected: " + "disabling all passive thermal trip points\n", d->ident); + psv = -1; + } + return 0; +} + +static struct dmi_system_id thermal_dmi_table[] __initdata = { + /* + * Award BIOS on this AOpen makes thermal control almost worthless. + * http://bugzilla.kernel.org/show_bug.cgi?id=8842 + */ + { + .callback = thermal_act, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_psv, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_tzp, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_nocrt, + .ident = "Gigabyte GA-7ZX", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "7ZX"), + }, + }, + {} +}; +#endif /* CONFIG_DMI */ + static int __init acpi_thermal_init(void) { int result = 0; + dmi_check_system(thermal_dmi_table); + if (off) { + printk(KERN_NOTICE "ACPI: thermal control disabled\n"); + return -ENODEV; + } acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); if (!acpi_thermal_dir) return -ENODEV; |