···11+Generic Thermal Sysfs driver How To22+=========================33+44+Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com>55+66+Updated: 2 January 200877+88+Copyright (c) 2008 Intel Corporation99+1010+1111+0. Introduction1212+1313+The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors)1414+and thermal cooling devices (fan, processor...) to register with the thermal management1515+solution and to be a part of it.1616+1717+This how-to focusses on enabling new thermal zone and cooling devices to participate1818+in thermal management.1919+This solution is platform independent and any type of thermal zone devices and2020+cooling devices should be able to make use of the infrastructure.2121+2222+The main task of the thermal sysfs driver is to expose thermal zone attributes as well2323+as cooling device attributes to the user space.2424+An intelligent thermal management application can make decisions based on inputs2525+from thermal zone attributes (the current temperature and trip point temperature)2626+and throttle appropriate devices.2727+2828+[0-*] denotes any positive number starting from 02929+[1-*] denotes any positive number starting from 13030+3131+1. thermal sysfs driver interface functions3232+3333+1.1 thermal zone device interface3434+1.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips,3535+ void *devdata, struct thermal_zone_device_ops *ops)3636+3737+ This interface function adds a new thermal zone device (sensor) to3838+ /sys/class/thermal folder as thermal_zone[0-*].3939+ It tries to bind all the thermal cooling devices registered at the same time.4040+4141+ name: the thermal zone name.4242+ trips: the total number of trip points this thermal zone supports.4343+ devdata: device private data4444+ ops: thermal zone device callbacks.4545+ .bind: bind the thermal zone device with a thermal cooling device.4646+ .unbind: unbing the thermal zone device with a thermal cooling device.4747+ .get_temp: get the current temperature of the thermal zone.4848+ .get_mode: get the current mode (user/kernel) of the thermal zone.4949+ "kernel" means thermal management is done in kernel.5050+ "user" will prevent kernel thermal driver actions upon trip points5151+ so that user applications can take charge of thermal management.5252+ .set_mode: set the mode (user/kernel) of the thermal zone.5353+ .get_trip_type: get the type of certain trip point.5454+ .get_trip_temp: get the temperature above which the certain trip point5555+ will be fired.5656+5757+1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)5858+5959+ This interface function removes the thermal zone device.6060+ It deletes the corresponding entry form /sys/class/thermal folder and unbind all6161+ the thermal cooling devices it uses.6262+6363+1.2 thermal cooling device interface6464+1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,6565+ void *devdata, struct thermal_cooling_device_ops *)6666+6767+ This interface function adds a new thermal cooling device (fan/processor/...) to6868+ /sys/class/thermal/ folder as cooling_device[0-*].6969+ It tries to bind itself to all the thermal zone devices register at the same time.7070+ name: the cooling device name.7171+ devdata: device private data.7272+ ops: thermal cooling devices callbacks.7373+ .get_max_state: get the Maximum throttle state of the cooling device.7474+ .get_cur_state: get the Current throttle state of the cooling device.7575+ .set_cur_state: set the Current throttle state of the cooling device.7676+7777+1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)7878+7979+ This interface function remove the thermal cooling device.8080+ It deletes the corresponding entry form /sys/class/thermal folder and unbind8181+ itself from all the thermal zone devices using it.8282+8383+1.3 interface for binding a thermal zone device with a thermal cooling device8484+1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,8585+ int trip, struct thermal_cooling_device *cdev);8686+8787+ This interface function bind a thermal cooling device to the certain trip point8888+ of a thermal zone device.8989+ This function is usually called in the thermal zone device .bind callback.9090+ tz: the thermal zone device9191+ cdev: thermal cooling device9292+ trip: indicates which trip point the cooling devices is associated with9393+ in this thermal zone.9494+9595+1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,9696+ int trip, struct thermal_cooling_device *cdev);9797+9898+ This interface function unbind a thermal cooling device from the certain trip point9999+ of a thermal zone device.100100+ This function is usually called in the thermal zone device .unbind callback.101101+ tz: the thermal zone device102102+ cdev: thermal cooling device103103+ trip: indicates which trip point the cooling devices is associated with104104+ in this thermal zone.105105+106106+2. sysfs attributes structure107107+108108+RO read only value109109+RW read/write value110110+111111+All thermal sysfs attributes will be represented under /sys/class/thermal112112+/sys/class/thermal/113113+114114+Thermal zone device sys I/F, created once it's registered:115115+|thermal_zone[0-*]:116116+ |-----type: Type of the thermal zone117117+ |-----temp: Current temperature118118+ |-----mode: Working mode of the thermal zone119119+ |-----trip_point_[0-*]_temp: Trip point temperature120120+ |-----trip_point_[0-*]_type: Trip point type121121+122122+Thermal cooling device sys I/F, created once it's registered:123123+|cooling_device[0-*]:124124+ |-----type : Type of the cooling device(processor/fan/...)125125+ |-----max_state: Maximum cooling state of the cooling device126126+ |-----cur_state: Current cooling state of the cooling device127127+128128+129129+These two dynamic attributes are created/removed in pairs.130130+They represent the relationship between a thermal zone and its associated cooling device.131131+They are created/removed for each132132+thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection.133133+134134+|thermal_zone[0-*]135135+ |-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone136136+ |-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with137137+138138+139139+***************************140140+* Thermal zone attributes *141141+***************************142142+143143+type Strings which represent the thermal zone type.144144+ This is given by thermal zone driver as part of registration.145145+ Eg: "ACPI thermal zone" indicates it's a ACPI thermal device146146+ RO147147+ Optional148148+149149+temp Current temperature as reported by thermal zone (sensor)150150+ Unit: degree celsius151151+ RO152152+ Required153153+154154+mode One of the predifned values in [kernel, user]155155+ This file gives information about the algorithm156156+ that is currently managing the thermal zone.157157+ It can be either default kernel based algorithm158158+ or user space application.159159+ RW160160+ Optional161161+ kernel = Thermal management in kernel thermal zone driver.162162+ user = Preventing kernel thermal zone driver actions upon163163+ trip points so that user application can take full164164+ charge of the thermal management.165165+166166+trip_point_[0-*]_temp The temperature above which trip point will be fired167167+ Unit: degree celsius168168+ RO169169+ Optional170170+171171+trip_point_[0-*]_type Strings which indicate the type of the trip point172172+ Eg. it can be one of critical, hot, passive,173173+ active[0-*] for ACPI thermal zone.174174+ RO175175+ Optional176176+177177+cdev[0-*] Sysfs link to the thermal cooling device node where the sys I/F178178+ for cooling device throttling control represents.179179+ RO180180+ Optional181181+182182+cdev[0-*]_trip_point The trip point with which cdev[0-*] is assocated in this thermal zone183183+ -1 means the cooling device is not associated with any trip point.184184+ RO185185+ Optional186186+187187+******************************188188+* Cooling device attributes *189189+******************************190190+191191+type String which represents the type of device192192+ eg: For generic ACPI: this should be "Fan",193193+ "Processor" or "LCD"194194+ eg. For memory controller device on intel_menlow platform:195195+ this should be "Memory controller"196196+ RO197197+ Optional198198+199199+max_state The maximum permissible cooling state of this cooling device.200200+ RO201201+ Required202202+203203+cur_state The current cooling state of this cooling device.204204+ the value can any integer numbers between 0 and max_state,205205+ cur_state == 0 means no cooling206206+ cur_state == max_state means the maximum cooling.207207+ RW208208+ Required209209+210210+3. A simple implementation211211+212212+ACPI thermal zone may support multiple trip points like critical/hot/passive/active.213213+If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time,214214+it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all.215215+It has one processor and one fan, which are both registered as thermal_cooling_device.216216+If the processor is listed in _PSL method, and the fan is listed in _AL0 method,217217+the sys I/F structure will be built like this:218218+219219+/sys/class/thermal:220220+221221+|thermal_zone1:222222+ |-----type: ACPI thermal zone223223+ |-----temp: 37224224+ |-----mode: kernel225225+ |-----trip_point_0_temp: 100226226+ |-----trip_point_0_type: critical227227+ |-----trip_point_1_temp: 80228228+ |-----trip_point_1_type: passive229229+ |-----trip_point_2_temp: 70230230+ |-----trip_point_2_type: active[0]231231+ |-----trip_point_3_temp: 60232232+ |-----trip_point_3_type: active[1]233233+ |-----cdev0: --->/sys/class/thermal/cooling_device0234234+ |-----cdev0_trip_point: 1 /* cdev0 can be used for passive */235235+ |-----cdev1: --->/sys/class/thermal/cooling_device3236236+ |-----cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/237237+238238+|cooling_device0:239239+ |-----type: Processor240240+ |-----max_state: 8241241+ |-----cur_state: 0242242+243243+|cooling_device3:244244+ |-----type: Fan245245+ |-----max_state: 2246246+ |-----cur_state: 0
···186186config ACPI_THERMAL187187 tristate "Thermal Zone"188188 depends on ACPI_PROCESSOR189189+ select THERMAL189190 default y190191 help191192 This driver adds support for ACPI thermal zones. Most mobile and
+25
drivers/acpi/bus.c
···122122123123EXPORT_SYMBOL(acpi_bus_get_status);124124125125+void acpi_bus_private_data_handler(acpi_handle handle,126126+ u32 function, void *context)127127+{128128+ return;129129+}130130+EXPORT_SYMBOL(acpi_bus_private_data_handler);131131+132132+int acpi_bus_get_private_data(acpi_handle handle, void **data)133133+{134134+ acpi_status status = AE_OK;135135+136136+ if (!*data)137137+ return -EINVAL;138138+139139+ status = acpi_get_data(handle, acpi_bus_private_data_handler, data);140140+ if (ACPI_FAILURE(status) || !*data) {141141+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",142142+ handle));143143+ return -ENODEV;144144+ }145145+146146+ return 0;147147+}148148+EXPORT_SYMBOL(acpi_bus_get_private_data);149149+125150/* --------------------------------------------------------------------------126151 Power Management127152 -------------------------------------------------------------------------- */
+85-7
drivers/acpi/fan.c
···3030#include <linux/proc_fs.h>3131#include <linux/seq_file.h>3232#include <asm/uaccess.h>3333-3333+#include <linux/thermal.h>3434#include <acpi/acpi_bus.h>3535#include <acpi/acpi_drivers.h>3636···6868 },6969};70707171+/* thermal cooling device callbacks */7272+static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf)7373+{7474+ /* ACPI fan device only support two states: ON/OFF */7575+ return sprintf(buf, "1\n");7676+}7777+7878+static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf)7979+{8080+ struct acpi_device *device = cdev->devdata;8181+ int state;8282+ int result;8383+8484+ if (!device)8585+ return -EINVAL;8686+8787+ result = acpi_bus_get_power(device->handle, &state);8888+ if (result)8989+ return result;9090+9191+ return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :9292+ (state == ACPI_STATE_D0 ? "1" : "unknown"));9393+}9494+9595+static int9696+fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)9797+{9898+ struct acpi_device *device = cdev->devdata;9999+ int result;100100+101101+ if (!device || (state != 0 && state != 1))102102+ return -EINVAL;103103+104104+ result = acpi_bus_set_power(device->handle,105105+ state ? ACPI_STATE_D0 : ACPI_STATE_D3);106106+107107+ return result;108108+}109109+110110+static struct thermal_cooling_device_ops fan_cooling_ops = {111111+ .get_max_state = fan_get_max_state,112112+ .get_cur_state = fan_get_cur_state,113113+ .set_cur_state = fan_set_cur_state,114114+};115115+71116/* --------------------------------------------------------------------------72117 FS Interface (/proc)73118 -------------------------------------------------------------------------- */119119+#ifdef CONFIG_ACPI_PROCFS7412075121static struct proc_dir_entry *acpi_fan_dir;76122···217171218172 return 0;219173}174174+#else175175+static int acpi_fan_add_fs(struct acpi_device *device)176176+{177177+ return 0;178178+}220179180180+static int acpi_fan_remove_fs(struct acpi_device *device)181181+{182182+ return 0;183183+}184184+#endif221185/* --------------------------------------------------------------------------222186 Driver Interface223187 -------------------------------------------------------------------------- */···235179static int acpi_fan_add(struct acpi_device *device)236180{237181 int result = 0;238238- struct acpi_fan *fan = NULL;239182 int state = 0;240240-183183+ struct thermal_cooling_device *cdev;241184242185 if (!device)243186 return -EINVAL;···254199 acpi_bus_set_power(device->handle, state);255200 device->flags.force_power_state = 0;256201202202+ cdev = thermal_cooling_device_register("Fan", device,203203+ &fan_cooling_ops);204204+ if (cdev)205205+ printk(KERN_INFO PREFIX206206+ "%s is registered as cooling_device%d\n",207207+ device->dev.bus_id, cdev->id);208208+ else209209+ goto end;210210+ acpi_driver_data(device) = cdev;211211+ result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj,212212+ "thermal_cooling");213213+ if (result)214214+ return result;215215+216216+ result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj,217217+ "device");218218+ if (result)219219+ return result;220220+257221 result = acpi_fan_add_fs(device);258222 if (result)259223 goto end;···282208 !device->power.state ? "on" : "off");283209284210 end:285285- if (result)286286- kfree(fan);287287-288211 return result;289212}290213291214static int acpi_fan_remove(struct acpi_device *device, int type)292215{293293- if (!device || !acpi_driver_data(device))216216+ struct thermal_cooling_device *cdev = acpi_driver_data(device);217217+218218+ if (!device || !cdev)294219 return -EINVAL;295220296221 acpi_fan_remove_fs(device);222222+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");223223+ sysfs_remove_link(&cdev->device.kobj, "device");224224+ thermal_cooling_device_unregister(cdev);297225298226 return 0;299227}···337261 int result = 0;338262339263264264+#ifdef CONFIG_ACPI_PROCFS340265 acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);341266 if (!acpi_fan_dir)342267 return -ENODEV;343268 acpi_fan_dir->owner = THIS_MODULE;269269+#endif344270345271 result = acpi_bus_register_driver(&acpi_fan_driver);346272 if (result < 0) {
+23
drivers/acpi/processor_core.c
···668668669669 acpi_processor_power_init(pr, device);670670671671+ pr->cdev = thermal_cooling_device_register("Processor", device,672672+ &processor_cooling_ops);673673+ if (pr->cdev)674674+ printk(KERN_INFO PREFIX675675+ "%s is registered as cooling_device%d\n",676676+ device->dev.bus_id, pr->cdev->id);677677+ else678678+ goto end;679679+680680+ result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj,681681+ "thermal_cooling");682682+ if (result)683683+ return result;684684+ result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj,685685+ "device");686686+ if (result)687687+ return result;688688+671689 if (pr->flags.throttling) {672690 printk(KERN_INFO PREFIX "%s [%s] (supports",673691 acpi_device_name(device), acpi_device_bid(device));···808790 acpi_processor_notify);809791810792 acpi_processor_remove_fs(device);793793+794794+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");795795+ sysfs_remove_link(&pr->cdev->device.kobj, "device");796796+ thermal_cooling_device_unregister(pr->cdev);797797+ pr->cdev = NULL;811798812799 processors[pr->id] = NULL;813800
+129-5
drivers/acpi/processor_thermal.c
···3232#include <linux/cpufreq.h>3333#include <linux/proc_fs.h>3434#include <linux/seq_file.h>3535+#include <linux/sysdev.h>35363637#include <asm/uaccess.h>3738···9493 * _any_ cpufreq driver and not only the acpi-cpufreq driver.9594 */96959696+#define CPUFREQ_THERMAL_MIN_STEP 09797+#define CPUFREQ_THERMAL_MAX_STEP 39898+9799static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];98100static unsigned int acpi_thermal_cpufreq_is_init = 0;99101···113109 if (!cpu_has_cpufreq(cpu))114110 return -ENODEV;115111116116- if (cpufreq_thermal_reduction_pctg[cpu] < 60) {117117- cpufreq_thermal_reduction_pctg[cpu] += 20;112112+ if (cpufreq_thermal_reduction_pctg[cpu] <113113+ CPUFREQ_THERMAL_MAX_STEP) {114114+ cpufreq_thermal_reduction_pctg[cpu]++;118115 cpufreq_update_policy(cpu);119116 return 0;120117 }···128123 if (!cpu_has_cpufreq(cpu))129124 return -ENODEV;130125131131- if (cpufreq_thermal_reduction_pctg[cpu] > 20)132132- cpufreq_thermal_reduction_pctg[cpu] -= 20;126126+ if (cpufreq_thermal_reduction_pctg[cpu] >127127+ (CPUFREQ_THERMAL_MIN_STEP + 1))128128+ cpufreq_thermal_reduction_pctg[cpu]--;133129 else134130 cpufreq_thermal_reduction_pctg[cpu] = 0;135131 cpufreq_update_policy(cpu);···149143150144 max_freq =151145 (policy->cpuinfo.max_freq *152152- (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;146146+ (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100;153147154148 cpufreq_verify_within_limits(policy, 0, max_freq);155149···160154static struct notifier_block acpi_thermal_cpufreq_notifier_block = {161155 .notifier_call = acpi_thermal_cpufreq_notifier,162156};157157+158158+static int cpufreq_get_max_state(unsigned int cpu)159159+{160160+ if (!cpu_has_cpufreq(cpu))161161+ return 0;162162+163163+ return CPUFREQ_THERMAL_MAX_STEP;164164+}165165+166166+static int cpufreq_get_cur_state(unsigned int cpu)167167+{168168+ if (!cpu_has_cpufreq(cpu))169169+ return 0;170170+171171+ return cpufreq_thermal_reduction_pctg[cpu];172172+}173173+174174+static int cpufreq_set_cur_state(unsigned int cpu, int state)175175+{176176+ if (!cpu_has_cpufreq(cpu))177177+ return 0;178178+179179+ cpufreq_thermal_reduction_pctg[cpu] = state;180180+ cpufreq_update_policy(cpu);181181+ return 0;182182+}163183164184void acpi_thermal_cpufreq_init(void)165185{···211179}212180213181#else /* ! CONFIG_CPU_FREQ */182182+static int cpufreq_get_max_state(unsigned int cpu)183183+{184184+ return 0;185185+}186186+187187+static int cpufreq_get_cur_state(unsigned int cpu)188188+{189189+ return 0;190190+}191191+192192+static int cpufreq_set_cur_state(unsigned int cpu, int state)193193+{194194+ return 0;195195+}214196215197static int acpi_thermal_cpufreq_increase(unsigned int cpu)216198{···355309356310 return 0;357311}312312+313313+/* thermal coolign device callbacks */314314+static int acpi_processor_max_state(struct acpi_processor *pr)315315+{316316+ int max_state = 0;317317+318318+ /*319319+ * There exists four states according to320320+ * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3321321+ */322322+ max_state += cpufreq_get_max_state(pr->id);323323+ if (pr->flags.throttling)324324+ max_state += (pr->throttling.state_count -1);325325+326326+ return max_state;327327+}328328+static int329329+processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)330330+{331331+ struct acpi_device *device = cdev->devdata;332332+ struct acpi_processor *pr = acpi_driver_data(device);333333+334334+ if (!device || !pr)335335+ return -EINVAL;336336+337337+ return sprintf(buf, "%d\n", acpi_processor_max_state(pr));338338+}339339+340340+static int341341+processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)342342+{343343+ struct acpi_device *device = cdev->devdata;344344+ struct acpi_processor *pr = acpi_driver_data(device);345345+ int cur_state;346346+347347+ if (!device || !pr)348348+ return -EINVAL;349349+350350+ cur_state = cpufreq_get_cur_state(pr->id);351351+ if (pr->flags.throttling)352352+ cur_state += pr->throttling.state;353353+354354+ return sprintf(buf, "%d\n", cur_state);355355+}356356+357357+static int358358+processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)359359+{360360+ struct acpi_device *device = cdev->devdata;361361+ struct acpi_processor *pr = acpi_driver_data(device);362362+ int result = 0;363363+ int max_pstate;364364+365365+ if (!device || !pr)366366+ return -EINVAL;367367+368368+ max_pstate = cpufreq_get_max_state(pr->id);369369+370370+ if (state > acpi_processor_max_state(pr))371371+ return -EINVAL;372372+373373+ if (state <= max_pstate) {374374+ if (pr->flags.throttling && pr->throttling.state)375375+ result = acpi_processor_set_throttling(pr, 0);376376+ cpufreq_set_cur_state(pr->id, state);377377+ } else {378378+ cpufreq_set_cur_state(pr->id, max_pstate);379379+ result = acpi_processor_set_throttling(pr,380380+ state - max_pstate);381381+ }382382+ return result;383383+}384384+385385+struct thermal_cooling_device_ops processor_cooling_ops = {386386+ .get_max_state = processor_get_max_state,387387+ .get_cur_state = processor_get_cur_state,388388+ .set_cur_state = processor_set_cur_state,389389+};358390359391/* /proc interface */360392
+497-159
drivers/acpi/thermal.c
···4343#include <linux/seq_file.h>4444#include <linux/reboot.h>4545#include <asm/uaccess.h>4646-4646+#include <linux/thermal.h>4747#include <acpi/acpi_bus.h>4848#include <acpi/acpi_drivers.h>4949···64646565#define ACPI_THERMAL_MAX_ACTIVE 106666#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 656767-6868-#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)6969-#define CELSIUS_TO_KELVIN(t) ((t+273)*10)70677168#define _COMPONENT ACPI_THERMAL_COMPONENT7269ACPI_MODULE_NAME("thermal");···192195 struct acpi_thermal_trips trips;193196 struct acpi_handle_list devices;194197 struct timer_list timer;198198+ struct thermal_zone_device *thermal_zone;199199+ int tz_enabled;195200 struct mutex lock;196201};197202···320321 return 0;321322}322323323323-static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)324324+#define ACPI_TRIPS_CRITICAL 0x01325325+#define ACPI_TRIPS_HOT 0x02326326+#define ACPI_TRIPS_PASSIVE 0x04327327+#define ACPI_TRIPS_ACTIVE 0x08328328+#define ACPI_TRIPS_DEVICES 0x10329329+330330+#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)331331+#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES332332+333333+#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \334334+ ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \335335+ ACPI_TRIPS_DEVICES)336336+337337+/*338338+ * This exception is thrown out in two cases:339339+ * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid340340+ * when re-evaluating the AML code.341341+ * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.342342+ * We need to re-bind the cooling devices of a thermal zone when this occurs.343343+ */344344+#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \345345+do { \346346+ if (flags != ACPI_TRIPS_INIT) \347347+ ACPI_EXCEPTION((AE_INFO, AE_ERROR, \348348+ "ACPI thermal trip point %s changed\n" \349349+ "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \350350+} while (0)351351+352352+static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)324353{325354 acpi_status status = AE_OK;326326- int i = 0;327327-328328-329329- if (!tz)330330- return -EINVAL;355355+ struct acpi_handle_list devices;356356+ int valid = 0;357357+ int i;331358332359 /* Critical Shutdown (required) */333333-334334- status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,335335- &tz->trips.critical.temperature);336336- if (ACPI_FAILURE(status)) {337337- tz->trips.critical.flags.valid = 0;338338- ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));339339- return -ENODEV;340340- } else {341341- tz->trips.critical.flags.valid = 1;342342- ACPI_DEBUG_PRINT((ACPI_DB_INFO,343343- "Found critical threshold [%lu]\n",344344- tz->trips.critical.temperature));345345- }346346-347347- if (tz->trips.critical.flags.valid == 1) {348348- if (crt == -1) {360360+ if (flag & ACPI_TRIPS_CRITICAL) {361361+ status = acpi_evaluate_integer(tz->device->handle,362362+ "_CRT", NULL, &tz->trips.critical.temperature);363363+ if (ACPI_FAILURE(status)) {349364 tz->trips.critical.flags.valid = 0;350350- } else if (crt > 0) {351351- unsigned long crt_k = CELSIUS_TO_KELVIN(crt);352352-353353- /*354354- * Allow override to lower critical threshold355355- */356356- if (crt_k < tz->trips.critical.temperature)357357- tz->trips.critical.temperature = crt_k;365365+ ACPI_EXCEPTION((AE_INFO, status,366366+ "No critical threshold"));367367+ return -ENODEV;368368+ } else {369369+ tz->trips.critical.flags.valid = 1;370370+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,371371+ "Found critical threshold [%lu]\n",372372+ tz->trips.critical.temperature));373373+ }374374+ if (tz->trips.critical.flags.valid == 1) {375375+ if (crt == -1) {376376+ tz->trips.critical.flags.valid = 0;377377+ } else if (crt > 0) {378378+ unsigned long crt_k = CELSIUS_TO_KELVIN(crt);379379+ /*380380+ * Allow override to lower critical threshold381381+ */382382+ if (crt_k < tz->trips.critical.temperature)383383+ tz->trips.critical.temperature = crt_k;384384+ }358385 }359386 }360387361388 /* Critical Sleep (optional) */362362-363363- status =364364- acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,365365- &tz->trips.hot.temperature);366366- if (ACPI_FAILURE(status)) {367367- tz->trips.hot.flags.valid = 0;368368- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));369369- } else {370370- tz->trips.hot.flags.valid = 1;371371- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",372372- tz->trips.hot.temperature));373373- }374374-375375- /* Passive: Processors (optional) */376376-377377- if (psv == -1) {378378- status = AE_SUPPORT;379379- } else if (psv > 0) {380380- tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);381381- status = AE_OK;382382- } else {389389+ if (flag & ACPI_TRIPS_HOT) {383390 status = acpi_evaluate_integer(tz->device->handle,384384- "_PSV", NULL, &tz->trips.passive.temperature);385385- }386386-387387- if (ACPI_FAILURE(status)) {388388- tz->trips.passive.flags.valid = 0;389389- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));390390- } else {391391- tz->trips.passive.flags.valid = 1;392392-393393- status =394394- acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,395395- &tz->trips.passive.tc1);396396- if (ACPI_FAILURE(status))397397- tz->trips.passive.flags.valid = 0;398398-399399- status =400400- acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,401401- &tz->trips.passive.tc2);402402- if (ACPI_FAILURE(status))403403- tz->trips.passive.flags.valid = 0;404404-405405- status =406406- acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,407407- &tz->trips.passive.tsp);408408- if (ACPI_FAILURE(status))409409- tz->trips.passive.flags.valid = 0;410410-411411- status =412412- acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,413413- &tz->trips.passive.devices);414414- if (ACPI_FAILURE(status))415415- tz->trips.passive.flags.valid = 0;416416-417417- if (!tz->trips.passive.flags.valid)418418- printk(KERN_WARNING PREFIX "Invalid passive threshold\n");419419- else391391+ "_HOT", NULL, &tz->trips.hot.temperature);392392+ if (ACPI_FAILURE(status)) {393393+ tz->trips.hot.flags.valid = 0;420394 ACPI_DEBUG_PRINT((ACPI_DB_INFO,421421- "Found passive threshold [%lu]\n",422422- tz->trips.passive.temperature));395395+ "No hot threshold\n"));396396+ } else {397397+ tz->trips.hot.flags.valid = 1;398398+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,399399+ "Found hot threshold [%lu]\n",400400+ tz->trips.critical.temperature));401401+ }423402 }424403425425- /* Active: Fans, etc. (optional) */404404+ /* Passive (optional) */405405+ if (flag & ACPI_TRIPS_PASSIVE) {406406+ valid = tz->trips.passive.flags.valid;407407+ if (psv == -1) {408408+ status = AE_SUPPORT;409409+ } else if (psv > 0) {410410+ tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);411411+ status = AE_OK;412412+ } else {413413+ status = acpi_evaluate_integer(tz->device->handle,414414+ "_PSV", NULL, &tz->trips.passive.temperature);415415+ }426416417417+ if (ACPI_FAILURE(status))418418+ tz->trips.passive.flags.valid = 0;419419+ else {420420+ tz->trips.passive.flags.valid = 1;421421+ if (flag == ACPI_TRIPS_INIT) {422422+ status = acpi_evaluate_integer(423423+ tz->device->handle, "_TC1",424424+ NULL, &tz->trips.passive.tc1);425425+ if (ACPI_FAILURE(status))426426+ tz->trips.passive.flags.valid = 0;427427+ status = acpi_evaluate_integer(428428+ tz->device->handle, "_TC2",429429+ NULL, &tz->trips.passive.tc2);430430+ if (ACPI_FAILURE(status))431431+ tz->trips.passive.flags.valid = 0;432432+ status = acpi_evaluate_integer(433433+ tz->device->handle, "_TSP",434434+ NULL, &tz->trips.passive.tsp);435435+ if (ACPI_FAILURE(status))436436+ tz->trips.passive.flags.valid = 0;437437+ }438438+ }439439+ }440440+ if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {441441+ memset(&devices, 0, sizeof(struct acpi_handle_list));442442+ status = acpi_evaluate_reference(tz->device->handle, "_PSL",443443+ NULL, &devices);444444+ if (ACPI_FAILURE(status))445445+ tz->trips.passive.flags.valid = 0;446446+ else447447+ tz->trips.passive.flags.valid = 1;448448+449449+ if (memcmp(&tz->trips.passive.devices, &devices,450450+ sizeof(struct acpi_handle_list))) {451451+ memcpy(&tz->trips.passive.devices, &devices,452452+ sizeof(struct acpi_handle_list));453453+ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");454454+ }455455+ }456456+ if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {457457+ if (valid != tz->trips.passive.flags.valid)458458+ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");459459+ }460460+461461+ /* Active (optional) */427462 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {428428-429463 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };464464+ valid = tz->trips.active[i].flags.valid;430465431466 if (act == -1)432432- break; /* disable all active trip points */467467+ break; /* disable all active trip points */433468434434- status = acpi_evaluate_integer(tz->device->handle,435435- name, NULL, &tz->trips.active[i].temperature);436436-437437- if (ACPI_FAILURE(status)) {438438- if (i == 0) /* no active trip points */469469+ if (flag & ACPI_TRIPS_ACTIVE) {470470+ status = acpi_evaluate_integer(tz->device->handle,471471+ name, NULL, &tz->trips.active[i].temperature);472472+ if (ACPI_FAILURE(status)) {473473+ tz->trips.active[i].flags.valid = 0;474474+ if (i == 0)475475+ break;476476+ if (act <= 0)477477+ break;478478+ if (i == 1)479479+ tz->trips.active[0].temperature =480480+ CELSIUS_TO_KELVIN(act);481481+ else482482+ /*483483+ * Don't allow override higher than484484+ * the next higher trip point485485+ */486486+ tz->trips.active[i - 1].temperature =487487+ (tz->trips.active[i - 2].temperature <488488+ CELSIUS_TO_KELVIN(act) ?489489+ tz->trips.active[i - 2].temperature :490490+ CELSIUS_TO_KELVIN(act));439491 break;440440- if (act <= 0) /* no override requested */441441- break;442442- if (i == 1) { /* 1 trip point */443443- tz->trips.active[0].temperature =444444- CELSIUS_TO_KELVIN(act);445445- } else { /* multiple trips */446446- /*447447- * Don't allow override higher than448448- * the next higher trip point449449- */450450- tz->trips.active[i - 1].temperature =451451- (tz->trips.active[i - 2].temperature <452452- CELSIUS_TO_KELVIN(act) ?453453- tz->trips.active[i - 2].temperature :454454- CELSIUS_TO_KELVIN(act));455455- }456456- break;492492+ } else493493+ tz->trips.active[i].flags.valid = 1;457494 }458495459496 name[2] = 'L';460460- status =461461- acpi_evaluate_reference(tz->device->handle, name, NULL,462462- &tz->trips.active[i].devices);463463- if (ACPI_SUCCESS(status)) {464464- tz->trips.active[i].flags.valid = 1;465465- ACPI_DEBUG_PRINT((ACPI_DB_INFO,466466- "Found active threshold [%d]:[%lu]\n",467467- i, tz->trips.active[i].temperature));468468- } else469469- ACPI_EXCEPTION((AE_INFO, status,470470- "Invalid active threshold [%d]", i));497497+ if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {498498+ memset(&devices, 0, sizeof(struct acpi_handle_list));499499+ status = acpi_evaluate_reference(tz->device->handle,500500+ name, NULL, &devices);501501+ if (ACPI_FAILURE(status))502502+ tz->trips.active[i].flags.valid = 0;503503+ else504504+ tz->trips.active[i].flags.valid = 1;505505+506506+ if (memcmp(&tz->trips.active[i].devices, &devices,507507+ sizeof(struct acpi_handle_list))) {508508+ memcpy(&tz->trips.active[i].devices, &devices,509509+ sizeof(struct acpi_handle_list));510510+ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");511511+ }512512+ }513513+ if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))514514+ if (valid != tz->trips.active[i].flags.valid)515515+ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");516516+517517+ if (!tz->trips.active[i].flags.valid)518518+ break;519519+ }520520+521521+ if (flag & ACPI_TRIPS_DEVICES) {522522+ memset(&devices, 0, sizeof(struct acpi_handle_list));523523+ status = acpi_evaluate_reference(tz->device->handle, "_TZD",524524+ NULL, &devices);525525+ if (memcmp(&tz->devices, &devices,526526+ sizeof(struct acpi_handle_list))) {527527+ memcpy(&tz->devices, &devices,528528+ sizeof(struct acpi_handle_list));529529+ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");530530+ }471531 }472532473533 return 0;474534}475535476476-static int acpi_thermal_get_devices(struct acpi_thermal *tz)536536+static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)477537{478478- acpi_status status = AE_OK;479479-480480-481481- if (!tz)482482- return -EINVAL;483483-484484- status =485485- acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);486486- if (ACPI_FAILURE(status))487487- return -ENODEV;488488-489489- return 0;538538+ return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);490539}491540492541static int acpi_thermal_critical(struct acpi_thermal *tz)···782735 if (result)783736 goto unlock;784737738738+ if (!tz->tz_enabled)739739+ goto unlock;740740+785741 memset(&tz->state, 0, sizeof(tz->state));786742787743 /*···877827 unlock:878828 mutex_unlock(&tz->lock);879829}830830+831831+/* sys I/F for generic thermal sysfs support */832832+static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)833833+{834834+ struct acpi_thermal *tz = thermal->devdata;835835+836836+ if (!tz)837837+ return -EINVAL;838838+839839+ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));840840+}841841+842842+static const char enabled[] = "kernel";843843+static const char disabled[] = "user";844844+static int thermal_get_mode(struct thermal_zone_device *thermal,845845+ char *buf)846846+{847847+ struct acpi_thermal *tz = thermal->devdata;848848+849849+ if (!tz)850850+ return -EINVAL;851851+852852+ return sprintf(buf, "%s\n", tz->tz_enabled ?853853+ enabled : disabled);854854+}855855+856856+static int thermal_set_mode(struct thermal_zone_device *thermal,857857+ const char *buf)858858+{859859+ struct acpi_thermal *tz = thermal->devdata;860860+ int enable;861861+862862+ if (!tz)863863+ return -EINVAL;864864+865865+ /*866866+ * enable/disable thermal management from ACPI thermal driver867867+ */868868+ if (!strncmp(buf, enabled, sizeof enabled - 1))869869+ enable = 1;870870+ else if (!strncmp(buf, disabled, sizeof disabled - 1))871871+ enable = 0;872872+ else873873+ return -EINVAL;874874+875875+ if (enable != tz->tz_enabled) {876876+ tz->tz_enabled = enable;877877+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,878878+ "%s ACPI thermal control\n",879879+ tz->tz_enabled ? enabled : disabled));880880+ acpi_thermal_check(tz);881881+ }882882+ return 0;883883+}884884+885885+static int thermal_get_trip_type(struct thermal_zone_device *thermal,886886+ int trip, char *buf)887887+{888888+ struct acpi_thermal *tz = thermal->devdata;889889+ int i;890890+891891+ if (!tz || trip < 0)892892+ return -EINVAL;893893+894894+ if (tz->trips.critical.flags.valid) {895895+ if (!trip)896896+ return sprintf(buf, "critical\n");897897+ trip--;898898+ }899899+900900+ if (tz->trips.hot.flags.valid) {901901+ if (!trip)902902+ return sprintf(buf, "hot\n");903903+ trip--;904904+ }905905+906906+ if (tz->trips.passive.flags.valid) {907907+ if (!trip)908908+ return sprintf(buf, "passive\n");909909+ trip--;910910+ }911911+912912+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&913913+ tz->trips.active[i].flags.valid; i++) {914914+ if (!trip)915915+ return sprintf(buf, "active%d\n", i);916916+ trip--;917917+ }918918+919919+ return -EINVAL;920920+}921921+922922+static int thermal_get_trip_temp(struct thermal_zone_device *thermal,923923+ int trip, char *buf)924924+{925925+ struct acpi_thermal *tz = thermal->devdata;926926+ int i;927927+928928+ if (!tz || trip < 0)929929+ return -EINVAL;930930+931931+ if (tz->trips.critical.flags.valid) {932932+ if (!trip)933933+ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(934934+ tz->trips.critical.temperature));935935+ trip--;936936+ }937937+938938+ if (tz->trips.hot.flags.valid) {939939+ if (!trip)940940+ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(941941+ tz->trips.hot.temperature));942942+ trip--;943943+ }944944+945945+ if (tz->trips.passive.flags.valid) {946946+ if (!trip)947947+ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(948948+ tz->trips.passive.temperature));949949+ trip--;950950+ }951951+952952+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&953953+ tz->trips.active[i].flags.valid; i++) {954954+ if (!trip)955955+ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(956956+ tz->trips.active[i].temperature));957957+ trip--;958958+ }959959+960960+ return -EINVAL;961961+}962962+963963+typedef int (*cb)(struct thermal_zone_device *, int,964964+ struct thermal_cooling_device *);965965+static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,966966+ struct thermal_cooling_device *cdev,967967+ cb action)968968+{969969+ struct acpi_device *device = cdev->devdata;970970+ struct acpi_thermal *tz = thermal->devdata;971971+ struct acpi_device *dev;972972+ acpi_status status;973973+ acpi_handle handle;974974+ int i;975975+ int j;976976+ int trip = -1;977977+ int result = 0;978978+979979+ if (tz->trips.critical.flags.valid)980980+ trip++;981981+982982+ if (tz->trips.hot.flags.valid)983983+ trip++;984984+985985+ if (tz->trips.passive.flags.valid) {986986+ trip++;987987+ for (i = 0; i < tz->trips.passive.devices.count;988988+ i++) {989989+ handle = tz->trips.passive.devices.handles[i];990990+ status = acpi_bus_get_device(handle, &dev);991991+ if (ACPI_SUCCESS(status) && (dev == device)) {992992+ result = action(thermal, trip, cdev);993993+ if (result)994994+ goto failed;995995+ }996996+ }997997+ }998998+999999+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {10001000+ if (!tz->trips.active[i].flags.valid)10011001+ break;10021002+ trip++;10031003+ for (j = 0;10041004+ j < tz->trips.active[i].devices.count;10051005+ j++) {10061006+ handle = tz->trips.active[i].devices.handles[j];10071007+ status = acpi_bus_get_device(handle, &dev);10081008+ if (ACPI_SUCCESS(status) && (dev == device)) {10091009+ result = action(thermal, trip, cdev);10101010+ if (result)10111011+ goto failed;10121012+ }10131013+ }10141014+ }10151015+10161016+ for (i = 0; i < tz->devices.count; i++) {10171017+ handle = tz->devices.handles[i];10181018+ status = acpi_bus_get_device(handle, &dev);10191019+ if (ACPI_SUCCESS(status) && (dev == device)) {10201020+ result = action(thermal, -1, cdev);10211021+ if (result)10221022+ goto failed;10231023+ }10241024+ }10251025+10261026+failed:10271027+ return result;10281028+}10291029+10301030+static int10311031+acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,10321032+ struct thermal_cooling_device *cdev)10331033+{10341034+ return acpi_thermal_cooling_device_cb(thermal, cdev,10351035+ thermal_zone_bind_cooling_device);10361036+}10371037+10381038+static int10391039+acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,10401040+ struct thermal_cooling_device *cdev)10411041+{10421042+ return acpi_thermal_cooling_device_cb(thermal, cdev,10431043+ thermal_zone_unbind_cooling_device);10441044+}10451045+10461046+static struct thermal_zone_device_ops acpi_thermal_zone_ops = {10471047+ .bind = acpi_thermal_bind_cooling_device,10481048+ .unbind = acpi_thermal_unbind_cooling_device,10491049+ .get_temp = thermal_get_temp,10501050+ .get_mode = thermal_get_mode,10511051+ .set_mode = thermal_set_mode,10521052+ .get_trip_type = thermal_get_trip_type,10531053+ .get_trip_temp = thermal_get_trip_temp,10541054+};10551055+10561056+static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)10571057+{10581058+ int trips = 0;10591059+ int result;10601060+ acpi_status status;10611061+ int i;10621062+10631063+ if (tz->trips.critical.flags.valid)10641064+ trips++;10651065+10661066+ if (tz->trips.hot.flags.valid)10671067+ trips++;10681068+10691069+ if (tz->trips.passive.flags.valid)10701070+ trips++;10711071+10721072+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&10731073+ tz->trips.active[i].flags.valid; i++, trips++);10741074+ tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",10751075+ trips, tz, &acpi_thermal_zone_ops);10761076+ if (!tz->thermal_zone)10771077+ return -ENODEV;10781078+10791079+ result = sysfs_create_link(&tz->device->dev.kobj,10801080+ &tz->thermal_zone->device.kobj, "thermal_zone");10811081+ if (result)10821082+ return result;10831083+10841084+ result = sysfs_create_link(&tz->thermal_zone->device.kobj,10851085+ &tz->device->dev.kobj, "device");10861086+ if (result)10871087+ return result;10881088+10891089+ status = acpi_attach_data(tz->device->handle,10901090+ acpi_bus_private_data_handler,10911091+ tz->thermal_zone);10921092+ if (ACPI_FAILURE(status)) {10931093+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,10941094+ "Error attaching device data\n"));10951095+ return -ENODEV;10961096+ }10971097+10981098+ tz->tz_enabled = 1;10991099+11001100+ printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",11011101+ tz->device->dev.bus_id, tz->thermal_zone->id);11021102+ return 0;11031103+}11041104+11051105+static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)11061106+{11071107+ sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");11081108+ sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");11091109+ thermal_zone_device_unregister(tz->thermal_zone);11101110+ tz->thermal_zone = NULL;11111111+ acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);11121112+}11131113+88011148811115/* --------------------------------------------------------------------------8821116 FS Interface (/proc)···15181184 acpi_thermal_check(tz);15191185 break;15201186 case ACPI_THERMAL_NOTIFY_THRESHOLDS:15211521- acpi_thermal_get_trip_points(tz);11871187+ acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);15221188 acpi_thermal_check(tz);15231189 acpi_bus_generate_proc_event(device, event, 0);15241190 acpi_bus_generate_netlink_event(device->pnp.device_class,15251191 device->dev.bus_id, event, 0);15261192 break;15271193 case ACPI_THERMAL_NOTIFY_DEVICES:15281528- if (tz->flags.devices)15291529- acpi_thermal_get_devices(tz);11941194+ acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);11951195+ acpi_thermal_check(tz);15301196 acpi_bus_generate_proc_event(device, event, 0);15311197 acpi_bus_generate_netlink_event(device->pnp.device_class,15321198 device->dev.bus_id, event, 0);···15691235 else15701236 acpi_thermal_get_polling_frequency(tz);1571123715721572- /* Get devices in this thermal zone [_TZD] (optional) */15731573- result = acpi_thermal_get_devices(tz);15741574- if (!result)15751575- tz->flags.devices = 1;15761576-15771238 return 0;15781239}15791240···15921263 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);15931264 acpi_driver_data(device) = tz;15941265 mutex_init(&tz->lock);12661266+12671267+15951268 result = acpi_thermal_get_info(tz);15961269 if (result)15971597- goto end;12701270+ goto free_memory;12711271+12721272+ result = acpi_thermal_register_thermal_zone(tz);12731273+ if (result)12741274+ goto free_memory;1598127515991276 result = acpi_thermal_add_fs(device);16001277 if (result)16011601- goto end;12781278+ goto unregister_thermal_zone;1602127916031280 init_timer(&tz->timer);16041281···16151280 acpi_thermal_notify, tz);16161281 if (ACPI_FAILURE(status)) {16171282 result = -ENODEV;16181618- goto end;12831283+ goto remove_fs;16191284 }1620128516211286 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",16221287 acpi_device_name(device), acpi_device_bid(device),16231288 KELVIN_TO_CELSIUS(tz->temperature));12891289+ goto end;1624129016251625- end:16261626- if (result) {16271627- acpi_thermal_remove_fs(device);16281628- kfree(tz);16291629- }16301630-12911291+remove_fs:12921292+ acpi_thermal_remove_fs(device);12931293+unregister_thermal_zone:12941294+ thermal_zone_device_unregister(tz->thermal_zone);12951295+free_memory:12961296+ kfree(tz);12971297+end:16311298 return result;16321299}16331300···16691332 }1670133316711334 acpi_thermal_remove_fs(device);13351335+ acpi_thermal_unregister_thermal_zone(tz);16721336 mutex_destroy(&tz->lock);16731337 kfree(tz);16741338 return 0;
+77-1
drivers/acpi/video.c
···3434#include <linux/seq_file.h>3535#include <linux/input.h>3636#include <linux/backlight.h>3737+#include <linux/thermal.h>3738#include <linux/video_output.h>3839#include <asm/uaccess.h>3940···180179 struct acpi_device *dev;181180 struct acpi_video_device_brightness *brightness;182181 struct backlight_device *backlight;182182+ struct thermal_cooling_device *cdev;183183 struct output_device *output_dev;184184};185185···344342 .set_state = acpi_video_output_set,345343 .get_status = acpi_video_output_get,346344};345345+346346+347347+/* thermal cooling device callbacks */348348+static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)349349+{350350+ struct acpi_device *device = cdev->devdata;351351+ struct acpi_video_device *video = acpi_driver_data(device);352352+353353+ return sprintf(buf, "%d\n", video->brightness->count - 3);354354+}355355+356356+static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)357357+{358358+ struct acpi_device *device = cdev->devdata;359359+ struct acpi_video_device *video = acpi_driver_data(device);360360+ unsigned long level;361361+ int state;362362+363363+ acpi_video_device_lcd_get_level_current(video, &level);364364+ for (state = 2; state < video->brightness->count; state++)365365+ if (level == video->brightness->levels[state])366366+ return sprintf(buf, "%d\n",367367+ video->brightness->count - state - 1);368368+369369+ return -EINVAL;370370+}371371+372372+static int373373+video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)374374+{375375+ struct acpi_device *device = cdev->devdata;376376+ struct acpi_video_device *video = acpi_driver_data(device);377377+ int level;378378+379379+ if ( state >= video->brightness->count - 2)380380+ return -EINVAL;381381+382382+ state = video->brightness->count - state;383383+ level = video->brightness->levels[state -1];384384+ return acpi_video_device_lcd_set_level(video, level);385385+}386386+387387+static struct thermal_cooling_device_ops video_cooling_ops = {388388+ .get_max_state = video_get_max_state,389389+ .get_cur_state = video_get_cur_state,390390+ .set_cur_state = video_set_cur_state,391391+};392392+347393/* --------------------------------------------------------------------------348394 Video Management349395 -------------------------------------------------------------------------- */···710660 kfree(obj);711661712662 if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){663663+ int result;713664 static int count = 0;714665 char *name;715666 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);···723672 device->backlight->props.max_brightness = device->brightness->count-3;724673 device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);725674 backlight_update_status(device->backlight);726726-727675 kfree(name);676676+677677+ device->cdev = thermal_cooling_device_register("LCD",678678+ device->dev, &video_cooling_ops);679679+ if (device->cdev) {680680+ printk(KERN_INFO PREFIX681681+ "%s is registered as cooling_device%d\n",682682+ device->dev->dev.bus_id, device->cdev->id);683683+ result = sysfs_create_link(&device->dev->dev.kobj,684684+ &device->cdev->device.kobj,685685+ "thermal_cooling");686686+ if (result)687687+ printk(KERN_ERR PREFIX "Create sysfs link\n");688688+ result = sysfs_create_link(&device->cdev->device.kobj,689689+ &device->dev->dev.kobj,690690+ "device");691691+ if (result)692692+ printk(KERN_ERR PREFIX "Create sysfs link\n");693693+ }728694 }729695 if (device->cap._DCS && device->cap._DSS){730696 static int count = 0;···18321764 ACPI_DEVICE_NOTIFY,18331765 acpi_video_device_notify);18341766 backlight_device_unregister(device->backlight);17671767+ if (device->cdev) {17681768+ sysfs_remove_link(&device->dev->dev.kobj,17691769+ "thermal_cooling");17701770+ sysfs_remove_link(&device->cdev->device.kobj,17711771+ "device");17721772+ thermal_cooling_device_unregister(device->cdev);17731773+ device->cdev = NULL;17741774+ }18351775 video_output_unregister(device->output_dev);1836177618371777 return 0;
+9
drivers/misc/Kconfig
···251251252252 If unsure, say N.253253254254+config INTEL_MENLOW255255+ tristate "Thermal Management driver for Intel menlow platform"256256+ depends on ACPI_THERMAL257257+ ---help---258258+ ACPI thermal management enhancement driver on259259+ Intel Menlow platform.260260+261261+ If unsure, say N.262262+254263endif # MISC_DEVICES
···11+/*22+ * intel_menlow.c - Intel menlow Driver for thermal management extension33+ *44+ * Copyright (C) 2008 Intel Corp55+ * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>66+ * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>77+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~88+ *99+ * This program is free software; you can redistribute it and/or modify1010+ * it under the terms of the GNU General Public License as published by1111+ * the Free Software Foundation; version 2 of the License.1212+ *1313+ * This program is distributed in the hope that it will be useful, but1414+ * WITHOUT ANY WARRANTY; without even the implied warranty of1515+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU1616+ * General Public License for more details.1717+ *1818+ * You should have received a copy of the GNU General Public License along1919+ * with this program; if not, write to the Free Software Foundation, Inc.,2020+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.2121+ *2222+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~2323+ *2424+ * This driver creates the sys I/F for programming the sensors.2525+ * It also implements the driver for intel menlow memory controller (hardware2626+ * id is INT0002) which makes use of the platform specific ACPI methods2727+ * to get/set bandwidth.2828+ */2929+3030+#include <linux/kernel.h>3131+#include <linux/module.h>3232+#include <linux/init.h>3333+#include <linux/types.h>3434+#include <linux/pci.h>3535+#include <linux/pm.h>3636+3737+#include <linux/thermal.h>3838+#include <acpi/acpi_bus.h>3939+#include <acpi/acpi_drivers.h>4040+4141+MODULE_AUTHOR("Thomas Sujith");4242+MODULE_AUTHOR("Zhang Rui");4343+MODULE_DESCRIPTION("Intel Menlow platform specific driver");4444+MODULE_LICENSE("GPL");4545+4646+/*4747+ * Memory controller device control4848+ */4949+5050+#define MEMORY_GET_BANDWIDTH "GTHS"5151+#define MEMORY_SET_BANDWIDTH "STHS"5252+#define MEMORY_ARG_CUR_BANDWIDTH 15353+#define MEMORY_ARG_MAX_BANDWIDTH 05454+5555+static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,5656+ unsigned long *max_state)5757+{5858+ struct acpi_device *device = cdev->devdata;5959+ acpi_handle handle = device->handle;6060+ unsigned long value;6161+ struct acpi_object_list arg_list;6262+ union acpi_object arg;6363+ acpi_status status = AE_OK;6464+6565+ arg_list.count = 1;6666+ arg_list.pointer = &arg;6767+ arg.type = ACPI_TYPE_INTEGER;6868+ arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;6969+ status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,7070+ &arg_list, &value);7171+ if (ACPI_FAILURE(status))7272+ return -EFAULT;7373+7474+ *max_state = value - 1;7575+ return 0;7676+}7777+7878+static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,7979+ char *buf)8080+{8181+ unsigned long value;8282+ if (memory_get_int_max_bandwidth(cdev, &value))8383+ return -EINVAL;8484+8585+ return sprintf(buf, "%ld\n", value);8686+}8787+8888+static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,8989+ char *buf)9090+{9191+ struct acpi_device *device = cdev->devdata;9292+ acpi_handle handle = device->handle;9393+ unsigned long value;9494+ struct acpi_object_list arg_list;9595+ union acpi_object arg;9696+ acpi_status status = AE_OK;9797+9898+ arg_list.count = 1;9999+ arg_list.pointer = &arg;100100+ arg.type = ACPI_TYPE_INTEGER;101101+ arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;102102+ status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,103103+ &arg_list, &value);104104+ if (ACPI_FAILURE(status))105105+ return -EFAULT;106106+107107+ return sprintf(buf, "%ld\n", value);108108+}109109+110110+static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,111111+ unsigned int state)112112+{113113+ struct acpi_device *device = cdev->devdata;114114+ acpi_handle handle = device->handle;115115+ struct acpi_object_list arg_list;116116+ union acpi_object arg;117117+ acpi_status status;118118+ int temp;119119+ unsigned long max_state;120120+121121+ if (memory_get_int_max_bandwidth(cdev, &max_state))122122+ return -EFAULT;123123+124124+ if (max_state < 0 || state > max_state)125125+ return -EINVAL;126126+127127+ arg_list.count = 1;128128+ arg_list.pointer = &arg;129129+ arg.type = ACPI_TYPE_INTEGER;130130+ arg.integer.value = state;131131+132132+ status =133133+ acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,134134+ (unsigned long *)&temp);135135+136136+ printk(KERN_INFO137137+ "Bandwidth value was %d: status is %d\n", state, status);138138+ if (ACPI_FAILURE(status))139139+ return -EFAULT;140140+141141+ return 0;142142+}143143+144144+static struct thermal_cooling_device_ops memory_cooling_ops = {145145+ .get_max_state = memory_get_max_bandwidth,146146+ .get_cur_state = memory_get_cur_bandwidth,147147+ .set_cur_state = memory_set_cur_bandwidth,148148+};149149+150150+/*151151+ * Memory Device Management152152+ */153153+static int intel_menlow_memory_add(struct acpi_device *device)154154+{155155+ int result = -ENODEV;156156+ acpi_status status = AE_OK;157157+ acpi_handle dummy;158158+ struct thermal_cooling_device *cdev;159159+160160+ if (!device)161161+ return -EINVAL;162162+163163+ status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);164164+ if (ACPI_FAILURE(status))165165+ goto end;166166+167167+ status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);168168+ if (ACPI_FAILURE(status))169169+ goto end;170170+171171+ cdev = thermal_cooling_device_register("Memory controller", device,172172+ &memory_cooling_ops);173173+ acpi_driver_data(device) = cdev;174174+ if (!cdev)175175+ result = -ENODEV;176176+ else {177177+ result = sysfs_create_link(&device->dev.kobj,178178+ &cdev->device.kobj, "thermal_cooling");179179+ if (result)180180+ goto unregister;181181+182182+ result = sysfs_create_link(&cdev->device.kobj,183183+ &device->dev.kobj, "device");184184+ if (result) {185185+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");186186+ goto unregister;187187+ }188188+ }189189+190190+ end:191191+ return result;192192+193193+ unregister:194194+ thermal_cooling_device_unregister(cdev);195195+ return result;196196+197197+}198198+199199+static int intel_menlow_memory_remove(struct acpi_device *device, int type)200200+{201201+ struct thermal_cooling_device *cdev = acpi_driver_data(device);202202+203203+ if (!device || !cdev)204204+ return -EINVAL;205205+206206+ sysfs_remove_link(&device->dev.kobj, "thermal_cooling");207207+ sysfs_remove_link(&cdev->device.kobj, "device");208208+ thermal_cooling_device_unregister(cdev);209209+210210+ return 0;211211+}212212+213213+const static struct acpi_device_id intel_menlow_memory_ids[] = {214214+ {"INT0002", 0},215215+ {"", 0},216216+};217217+218218+static struct acpi_driver intel_menlow_memory_driver = {219219+ .name = "intel_menlow_thermal_control",220220+ .ids = intel_menlow_memory_ids,221221+ .ops = {222222+ .add = intel_menlow_memory_add,223223+ .remove = intel_menlow_memory_remove,224224+ },225225+};226226+227227+/*228228+ * Sensor control on menlow platform229229+ */230230+231231+#define THERMAL_AUX0 0232232+#define THERMAL_AUX1 1233233+#define GET_AUX0 "GAX0"234234+#define GET_AUX1 "GAX1"235235+#define SET_AUX0 "SAX0"236236+#define SET_AUX1 "SAX1"237237+238238+struct intel_menlow_attribute {239239+ struct device_attribute attr;240240+ struct device *device;241241+ acpi_handle handle;242242+ struct list_head node;243243+};244244+245245+static LIST_HEAD(intel_menlow_attr_list);246246+static DEFINE_MUTEX(intel_menlow_attr_lock);247247+248248+/*249249+ * sensor_get_auxtrip - get the current auxtrip value from sensor250250+ * @name: Thermalzone name251251+ * @auxtype : AUX0/AUX1252252+ * @buf: syfs buffer253253+ */254254+static int sensor_get_auxtrip(acpi_handle handle, int index, int *value)255255+{256256+ acpi_status status;257257+258258+ if ((index != 0 && index != 1) || !value)259259+ return -EINVAL;260260+261261+ status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,262262+ NULL, (unsigned long *)value);263263+ if (ACPI_FAILURE(status))264264+ return -EIO;265265+266266+ return 0;267267+}268268+269269+/*270270+ * sensor_set_auxtrip - set the new auxtrip value to sensor271271+ * @name: Thermalzone name272272+ * @auxtype : AUX0/AUX1273273+ * @buf: syfs buffer274274+ */275275+static int sensor_set_auxtrip(acpi_handle handle, int index, int value)276276+{277277+ acpi_status status;278278+ union acpi_object arg = {279279+ ACPI_TYPE_INTEGER280280+ };281281+ struct acpi_object_list args = {282282+ 1, &arg283283+ };284284+ int temp;285285+286286+ if (index != 0 && index != 1)287287+ return -EINVAL;288288+289289+ status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,290290+ NULL, (unsigned long *)&temp);291291+ if (ACPI_FAILURE(status))292292+ return -EIO;293293+ if ((index && value < temp) || (!index && value > temp))294294+ return -EINVAL;295295+296296+ arg.integer.value = value;297297+ status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,298298+ &args, (unsigned long *)&temp);299299+ if (ACPI_FAILURE(status))300300+ return -EIO;301301+302302+ /* do we need to check the return value of SAX0/SAX1 ? */303303+304304+ return 0;305305+}306306+307307+#define to_intel_menlow_attr(_attr) \308308+ container_of(_attr, struct intel_menlow_attribute, attr)309309+310310+static ssize_t aux0_show(struct device *dev,311311+ struct device_attribute *dev_attr, char *buf)312312+{313313+ struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);314314+ int value;315315+ int result;316316+317317+ result = sensor_get_auxtrip(attr->handle, 0, &value);318318+319319+ return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));320320+}321321+322322+static ssize_t aux1_show(struct device *dev,323323+ struct device_attribute *dev_attr, char *buf)324324+{325325+ struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);326326+ int value;327327+ int result;328328+329329+ result = sensor_get_auxtrip(attr->handle, 1, &value);330330+331331+ return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));332332+}333333+334334+static ssize_t aux0_store(struct device *dev,335335+ struct device_attribute *dev_attr,336336+ const char *buf, size_t count)337337+{338338+ struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);339339+ int value;340340+ int result;341341+342342+ /*Sanity check; should be a positive integer */343343+ if (!sscanf(buf, "%d", &value))344344+ return -EINVAL;345345+346346+ if (value < 0)347347+ return -EINVAL;348348+349349+ result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));350350+ return result ? result : count;351351+}352352+353353+static ssize_t aux1_store(struct device *dev,354354+ struct device_attribute *dev_attr,355355+ const char *buf, size_t count)356356+{357357+ struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);358358+ int value;359359+ int result;360360+361361+ /*Sanity check; should be a positive integer */362362+ if (!sscanf(buf, "%d", &value))363363+ return -EINVAL;364364+365365+ if (value < 0)366366+ return -EINVAL;367367+368368+ result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));369369+ return result ? result : count;370370+}371371+372372+/* BIOS can enable/disable the thermal user application in dabney platform */373373+#define BIOS_ENABLED "\\_TZ.GSTS"374374+static ssize_t bios_enabled_show(struct device *dev,375375+ struct device_attribute *attr, char *buf)376376+{377377+ acpi_status status;378378+ unsigned long bios_enabled;379379+380380+ status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);381381+ if (ACPI_FAILURE(status))382382+ return -ENODEV;383383+384384+ return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");385385+}386386+387387+static int intel_menlow_add_one_attribute(char *name, int mode, void *show,388388+ void *store, struct device *dev,389389+ acpi_handle handle)390390+{391391+ struct intel_menlow_attribute *attr;392392+ int result;393393+394394+ attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);395395+ if (!attr)396396+ return -ENOMEM;397397+398398+ attr->attr.attr.name = name;399399+ attr->attr.attr.mode = mode;400400+ attr->attr.show = show;401401+ attr->attr.store = store;402402+ attr->device = dev;403403+ attr->handle = handle;404404+405405+ result = device_create_file(dev, &attr->attr);406406+ if (result)407407+ return result;408408+409409+ mutex_lock(&intel_menlow_attr_lock);410410+ list_add_tail(&attr->node, &intel_menlow_attr_list);411411+ mutex_unlock(&intel_menlow_attr_lock);412412+413413+ return 0;414414+}415415+416416+static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,417417+ void *context, void **rv)418418+{419419+ acpi_status status;420420+ acpi_handle dummy;421421+ struct thermal_zone_device *thermal;422422+ int result;423423+424424+ result = acpi_bus_get_private_data(handle, (void **)&thermal);425425+ if (result)426426+ return 0;427427+428428+ /* _TZ must have the AUX0/1 methods */429429+ status = acpi_get_handle(handle, GET_AUX0, &dummy);430430+ if (ACPI_FAILURE(status))431431+ goto not_found;432432+433433+ status = acpi_get_handle(handle, SET_AUX0, &dummy);434434+ if (ACPI_FAILURE(status))435435+ goto not_found;436436+437437+ result = intel_menlow_add_one_attribute("aux0", 0644,438438+ aux0_show, aux0_store,439439+ &thermal->device, handle);440440+ if (result)441441+ return AE_ERROR;442442+443443+ status = acpi_get_handle(handle, GET_AUX1, &dummy);444444+ if (ACPI_FAILURE(status))445445+ goto not_found;446446+447447+ status = acpi_get_handle(handle, SET_AUX1, &dummy);448448+ if (ACPI_FAILURE(status))449449+ goto not_found;450450+451451+ result = intel_menlow_add_one_attribute("aux1", 0644,452452+ aux1_show, aux1_store,453453+ &thermal->device, handle);454454+ if (result)455455+ return AE_ERROR;456456+457457+ /*458458+ * create the "dabney_enabled" attribute which means the user app459459+ * should be loaded or not460460+ */461461+462462+ result = intel_menlow_add_one_attribute("bios_enabled", 0444,463463+ bios_enabled_show, NULL,464464+ &thermal->device, handle);465465+ if (result)466466+ return AE_ERROR;467467+468468+ not_found:469469+ if (status == AE_NOT_FOUND)470470+ return AE_OK;471471+ else472472+ return status;473473+}474474+475475+static void intel_menlow_unregister_sensor(void)476476+{477477+ struct intel_menlow_attribute *pos, *next;478478+479479+ mutex_lock(&intel_menlow_attr_lock);480480+ list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {481481+ list_del(&pos->node);482482+ device_remove_file(pos->device, &pos->attr);483483+ kfree(pos);484484+ }485485+ mutex_unlock(&intel_menlow_attr_lock);486486+487487+ return;488488+}489489+490490+static int __init intel_menlow_module_init(void)491491+{492492+ int result = -ENODEV;493493+ acpi_status status;494494+ unsigned long enable;495495+496496+ if (acpi_disabled)497497+ return result;498498+499499+ /* Looking for the \_TZ.GSTS method */500500+ status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);501501+ if (ACPI_FAILURE(status) || !enable)502502+ return -ENODEV;503503+504504+ /* Looking for ACPI device MEM0 with hardware id INT0002 */505505+ result = acpi_bus_register_driver(&intel_menlow_memory_driver);506506+ if (result)507507+ return result;508508+509509+ /* Looking for sensors in each ACPI thermal zone */510510+ status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,511511+ ACPI_UINT32_MAX,512512+ intel_menlow_register_sensor, NULL, NULL);513513+ if (ACPI_FAILURE(status))514514+ return -ENODEV;515515+516516+ return 0;517517+}518518+519519+static void __exit intel_menlow_module_exit(void)520520+{521521+ acpi_bus_unregister_driver(&intel_menlow_memory_driver);522522+ intel_menlow_unregister_sensor();523523+}524524+525525+module_init(intel_menlow_module_init);526526+module_exit(intel_menlow_module_exit);
+15
drivers/thermal/Kconfig
···11+#22+# Generic thermal sysfs drivers configuration33+#44+55+menuconfig THERMAL66+ bool "Generic Thermal sysfs driver"77+ default y88+ help99+ Generic Thermal Sysfs driver offers a generic mechanism for1010+ thermal management. Usually it's made up of one or more thermal1111+ zone and cooling device.1212+ each thermal zone contains its own temperature, trip points,1313+ cooling devices.1414+ All platforms with ACPI thermal support can use this driver.1515+ If you want this support, you should say Y here
+5
drivers/thermal/Makefile
···11+#22+# Makefile for sensor chip drivers.33+#44+55+obj-$(CONFIG_THERMAL) += thermal.o
+714
drivers/thermal/thermal.c
···11+/*22+ * thermal.c - Generic Thermal Management Sysfs support.33+ *44+ * Copyright (C) 2008 Intel Corp55+ * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>66+ * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>77+ *88+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~99+ *1010+ * This program is free software; you can redistribute it and/or modify1111+ * it under the terms of the GNU General Public License as published by1212+ * the Free Software Foundation; version 2 of the License.1313+ *1414+ * This program is distributed in the hope that it will be useful, but1515+ * WITHOUT ANY WARRANTY; without even the implied warranty of1616+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU1717+ * General Public License for more details.1818+ *1919+ * You should have received a copy of the GNU General Public License along2020+ * with this program; if not, write to the Free Software Foundation, Inc.,2121+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.2222+ *2323+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~2424+ */2525+2626+#include <linux/module.h>2727+#include <linux/device.h>2828+#include <linux/err.h>2929+#include <linux/kdev_t.h>3030+#include <linux/idr.h>3131+#include <linux/thermal.h>3232+#include <linux/spinlock.h>3333+3434+MODULE_AUTHOR("Zhang Rui")3535+MODULE_DESCRIPTION("Generic thermal management sysfs support");3636+MODULE_LICENSE("GPL");3737+3838+#define PREFIX "Thermal: "3939+4040+struct thermal_cooling_device_instance {4141+ int id;4242+ char name[THERMAL_NAME_LENGTH];4343+ struct thermal_zone_device *tz;4444+ struct thermal_cooling_device *cdev;4545+ int trip;4646+ char attr_name[THERMAL_NAME_LENGTH];4747+ struct device_attribute attr;4848+ struct list_head node;4949+};5050+5151+static DEFINE_IDR(thermal_tz_idr);5252+static DEFINE_IDR(thermal_cdev_idr);5353+static DEFINE_MUTEX(thermal_idr_lock);5454+5555+static LIST_HEAD(thermal_tz_list);5656+static LIST_HEAD(thermal_cdev_list);5757+static DEFINE_MUTEX(thermal_list_lock);5858+5959+static int get_idr(struct idr *idr, struct mutex *lock, int *id)6060+{6161+ int err;6262+6363+ again:6464+ if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))6565+ return -ENOMEM;6666+6767+ if (lock)6868+ mutex_lock(lock);6969+ err = idr_get_new(idr, NULL, id);7070+ if (lock)7171+ mutex_unlock(lock);7272+ if (unlikely(err == -EAGAIN))7373+ goto again;7474+ else if (unlikely(err))7575+ return err;7676+7777+ *id = *id & MAX_ID_MASK;7878+ return 0;7979+}8080+8181+static void release_idr(struct idr *idr, struct mutex *lock, int id)8282+{8383+ if (lock)8484+ mutex_lock(lock);8585+ idr_remove(idr, id);8686+ if (lock)8787+ mutex_unlock(lock);8888+}8989+9090+/* sys I/F for thermal zone */9191+9292+#define to_thermal_zone(_dev) \9393+ container_of(_dev, struct thermal_zone_device, device)9494+9595+static ssize_t9696+type_show(struct device *dev, struct device_attribute *attr, char *buf)9797+{9898+ struct thermal_zone_device *tz = to_thermal_zone(dev);9999+100100+ return sprintf(buf, "%s\n", tz->type);101101+}102102+103103+static ssize_t104104+temp_show(struct device *dev, struct device_attribute *attr, char *buf)105105+{106106+ struct thermal_zone_device *tz = to_thermal_zone(dev);107107+108108+ if (!tz->ops->get_temp)109109+ return -EPERM;110110+111111+ return tz->ops->get_temp(tz, buf);112112+}113113+114114+static ssize_t115115+mode_show(struct device *dev, struct device_attribute *attr, char *buf)116116+{117117+ struct thermal_zone_device *tz = to_thermal_zone(dev);118118+119119+ if (!tz->ops->get_mode)120120+ return -EPERM;121121+122122+ return tz->ops->get_mode(tz, buf);123123+}124124+125125+static ssize_t126126+mode_store(struct device *dev, struct device_attribute *attr,127127+ const char *buf, size_t count)128128+{129129+ struct thermal_zone_device *tz = to_thermal_zone(dev);130130+ int result;131131+132132+ if (!tz->ops->set_mode)133133+ return -EPERM;134134+135135+ result = tz->ops->set_mode(tz, buf);136136+ if (result)137137+ return result;138138+139139+ return count;140140+}141141+142142+static ssize_t143143+trip_point_type_show(struct device *dev, struct device_attribute *attr,144144+ char *buf)145145+{146146+ struct thermal_zone_device *tz = to_thermal_zone(dev);147147+ int trip;148148+149149+ if (!tz->ops->get_trip_type)150150+ return -EPERM;151151+152152+ if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))153153+ return -EINVAL;154154+155155+ return tz->ops->get_trip_type(tz, trip, buf);156156+}157157+158158+static ssize_t159159+trip_point_temp_show(struct device *dev, struct device_attribute *attr,160160+ char *buf)161161+{162162+ struct thermal_zone_device *tz = to_thermal_zone(dev);163163+ int trip;164164+165165+ if (!tz->ops->get_trip_temp)166166+ return -EPERM;167167+168168+ if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))169169+ return -EINVAL;170170+171171+ return tz->ops->get_trip_temp(tz, trip, buf);172172+}173173+174174+static DEVICE_ATTR(type, 0444, type_show, NULL);175175+static DEVICE_ATTR(temp, 0444, temp_show, NULL);176176+static DEVICE_ATTR(mode, 0644, mode_show, mode_store);177177+178178+static struct device_attribute trip_point_attrs[] = {179179+ __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),180180+ __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),181181+ __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),182182+ __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),183183+ __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),184184+ __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),185185+ __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),186186+ __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),187187+ __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),188188+ __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),189189+ __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),190190+ __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),191191+ __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),192192+ __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),193193+ __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),194194+ __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),195195+ __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),196196+ __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),197197+ __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),198198+ __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),199199+};200200+201201+#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \202202+do { \203203+ result = device_create_file(_dev, \204204+ &trip_point_attrs[_index * 2]); \205205+ if (result) \206206+ break; \207207+ result = device_create_file(_dev, \208208+ &trip_point_attrs[_index * 2 + 1]); \209209+} while (0)210210+211211+#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \212212+do { \213213+ device_remove_file(_dev, &trip_point_attrs[_index * 2]); \214214+ device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \215215+} while (0)216216+217217+/* sys I/F for cooling device */218218+#define to_cooling_device(_dev) \219219+ container_of(_dev, struct thermal_cooling_device, device)220220+221221+static ssize_t222222+thermal_cooling_device_type_show(struct device *dev,223223+ struct device_attribute *attr, char *buf)224224+{225225+ struct thermal_cooling_device *cdev = to_cooling_device(dev);226226+227227+ return sprintf(buf, "%s\n", cdev->type);228228+}229229+230230+static ssize_t231231+thermal_cooling_device_max_state_show(struct device *dev,232232+ struct device_attribute *attr, char *buf)233233+{234234+ struct thermal_cooling_device *cdev = to_cooling_device(dev);235235+236236+ return cdev->ops->get_max_state(cdev, buf);237237+}238238+239239+static ssize_t240240+thermal_cooling_device_cur_state_show(struct device *dev,241241+ struct device_attribute *attr, char *buf)242242+{243243+ struct thermal_cooling_device *cdev = to_cooling_device(dev);244244+245245+ return cdev->ops->get_cur_state(cdev, buf);246246+}247247+248248+static ssize_t249249+thermal_cooling_device_cur_state_store(struct device *dev,250250+ struct device_attribute *attr,251251+ const char *buf, size_t count)252252+{253253+ struct thermal_cooling_device *cdev = to_cooling_device(dev);254254+ int state;255255+ int result;256256+257257+ if (!sscanf(buf, "%d\n", &state))258258+ return -EINVAL;259259+260260+ if (state < 0)261261+ return -EINVAL;262262+263263+ result = cdev->ops->set_cur_state(cdev, state);264264+ if (result)265265+ return result;266266+ return count;267267+}268268+269269+static struct device_attribute dev_attr_cdev_type =270270+ __ATTR(type, 0444, thermal_cooling_device_type_show, NULL);271271+static DEVICE_ATTR(max_state, 0444,272272+ thermal_cooling_device_max_state_show, NULL);273273+static DEVICE_ATTR(cur_state, 0644,274274+ thermal_cooling_device_cur_state_show,275275+ thermal_cooling_device_cur_state_store);276276+277277+static ssize_t278278+thermal_cooling_device_trip_point_show(struct device *dev,279279+ struct device_attribute *attr, char *buf)280280+{281281+ struct thermal_cooling_device_instance *instance;282282+283283+ instance =284284+ container_of(attr, struct thermal_cooling_device_instance, attr);285285+286286+ if (instance->trip == THERMAL_TRIPS_NONE)287287+ return sprintf(buf, "-1\n");288288+ else289289+ return sprintf(buf, "%d\n", instance->trip);290290+}291291+292292+/* Device management */293293+294294+/**295295+ * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone296296+ * this function is usually called in the thermal zone device .bind callback.297297+ * @tz: thermal zone device298298+ * @trip: indicates which trip point the cooling devices is299299+ * associated with in this thermal zone.300300+ * @cdev: thermal cooling device301301+ */302302+int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,303303+ int trip,304304+ struct thermal_cooling_device *cdev)305305+{306306+ struct thermal_cooling_device_instance *dev;307307+ struct thermal_cooling_device_instance *pos;308308+ int result;309309+310310+ if (trip >= tz->trips ||311311+ (trip < 0 && trip != THERMAL_TRIPS_NONE))312312+ return -EINVAL;313313+314314+ if (!tz || !cdev)315315+ return -EINVAL;316316+317317+ dev =318318+ kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);319319+ if (!dev)320320+ return -ENOMEM;321321+ dev->tz = tz;322322+ dev->cdev = cdev;323323+ dev->trip = trip;324324+ result = get_idr(&tz->idr, &tz->lock, &dev->id);325325+ if (result)326326+ goto free_mem;327327+328328+ sprintf(dev->name, "cdev%d", dev->id);329329+ result =330330+ sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);331331+ if (result)332332+ goto release_idr;333333+334334+ sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);335335+ dev->attr.attr.name = dev->attr_name;336336+ dev->attr.attr.mode = 0444;337337+ dev->attr.show = thermal_cooling_device_trip_point_show;338338+ result = device_create_file(&tz->device, &dev->attr);339339+ if (result)340340+ goto remove_symbol_link;341341+342342+ mutex_lock(&tz->lock);343343+ list_for_each_entry(pos, &tz->cooling_devices, node)344344+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {345345+ result = -EEXIST;346346+ break;347347+ }348348+ if (!result)349349+ list_add_tail(&dev->node, &tz->cooling_devices);350350+ mutex_unlock(&tz->lock);351351+352352+ if (!result)353353+ return 0;354354+355355+ device_remove_file(&tz->device, &dev->attr);356356+ remove_symbol_link:357357+ sysfs_remove_link(&tz->device.kobj, dev->name);358358+ release_idr:359359+ release_idr(&tz->idr, &tz->lock, dev->id);360360+ free_mem:361361+ kfree(dev);362362+ return result;363363+}364364+EXPORT_SYMBOL(thermal_zone_bind_cooling_device);365365+366366+/**367367+ * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone368368+ * this function is usually called in the thermal zone device .unbind callback.369369+ * @tz: thermal zone device370370+ * @trip: indicates which trip point the cooling devices is371371+ * associated with in this thermal zone.372372+ * @cdev: thermal cooling device373373+ */374374+int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,375375+ int trip,376376+ struct thermal_cooling_device *cdev)377377+{378378+ struct thermal_cooling_device_instance *pos, *next;379379+380380+ mutex_lock(&tz->lock);381381+ list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {382382+ if (pos->tz == tz && pos->trip == trip383383+ && pos->cdev == cdev) {384384+ list_del(&pos->node);385385+ mutex_unlock(&tz->lock);386386+ goto unbind;387387+ }388388+ }389389+ mutex_unlock(&tz->lock);390390+391391+ return -ENODEV;392392+393393+ unbind:394394+ device_remove_file(&tz->device, &pos->attr);395395+ sysfs_remove_link(&tz->device.kobj, pos->name);396396+ release_idr(&tz->idr, &tz->lock, pos->id);397397+ kfree(pos);398398+ return 0;399399+}400400+EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);401401+402402+static void thermal_release(struct device *dev)403403+{404404+ struct thermal_zone_device *tz;405405+ struct thermal_cooling_device *cdev;406406+407407+ if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {408408+ tz = to_thermal_zone(dev);409409+ kfree(tz);410410+ } else {411411+ cdev = to_cooling_device(dev);412412+ kfree(cdev);413413+ }414414+}415415+416416+static struct class thermal_class = {417417+ .name = "thermal",418418+ .dev_release = thermal_release,419419+};420420+421421+/**422422+ * thermal_cooling_device_register - register a new thermal cooling device423423+ * @type: the thermal cooling device type.424424+ * @devdata: device private data.425425+ * @ops: standard thermal cooling devices callbacks.426426+ */427427+struct thermal_cooling_device *thermal_cooling_device_register(char *type,428428+ void *devdata, struct thermal_cooling_device_ops *ops)429429+{430430+ struct thermal_cooling_device *cdev;431431+ struct thermal_zone_device *pos;432432+ int result;433433+434434+ if (strlen(type) >= THERMAL_NAME_LENGTH)435435+ return NULL;436436+437437+ if (!ops || !ops->get_max_state || !ops->get_cur_state ||438438+ !ops->set_cur_state)439439+ return NULL;440440+441441+ cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);442442+ if (!cdev)443443+ return NULL;444444+445445+ result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);446446+ if (result) {447447+ kfree(cdev);448448+ return NULL;449449+ }450450+451451+ strcpy(cdev->type, type);452452+ cdev->ops = ops;453453+ cdev->device.class = &thermal_class;454454+ cdev->devdata = devdata;455455+ sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);456456+ result = device_register(&cdev->device);457457+ if (result) {458458+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);459459+ kfree(cdev);460460+ return NULL;461461+ }462462+463463+ /* sys I/F */464464+ if (type) {465465+ result = device_create_file(&cdev->device,466466+ &dev_attr_cdev_type);467467+ if (result)468468+ goto unregister;469469+ }470470+471471+ result = device_create_file(&cdev->device, &dev_attr_max_state);472472+ if (result)473473+ goto unregister;474474+475475+ result = device_create_file(&cdev->device, &dev_attr_cur_state);476476+ if (result)477477+ goto unregister;478478+479479+ mutex_lock(&thermal_list_lock);480480+ list_add(&cdev->node, &thermal_cdev_list);481481+ list_for_each_entry(pos, &thermal_tz_list, node) {482482+ if (!pos->ops->bind)483483+ continue;484484+ result = pos->ops->bind(pos, cdev);485485+ if (result)486486+ break;487487+488488+ }489489+ mutex_unlock(&thermal_list_lock);490490+491491+ if (!result)492492+ return cdev;493493+494494+ unregister:495495+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);496496+ device_unregister(&cdev->device);497497+ return NULL;498498+}499499+EXPORT_SYMBOL(thermal_cooling_device_register);500500+501501+/**502502+ * thermal_cooling_device_unregister - removes the registered thermal cooling device503503+ *504504+ * @cdev: the thermal cooling device to remove.505505+ *506506+ * thermal_cooling_device_unregister() must be called when the device is no507507+ * longer needed.508508+ */509509+void thermal_cooling_device_unregister(struct510510+ thermal_cooling_device511511+ *cdev)512512+{513513+ struct thermal_zone_device *tz;514514+ struct thermal_cooling_device *pos = NULL;515515+516516+ if (!cdev)517517+ return;518518+519519+ mutex_lock(&thermal_list_lock);520520+ list_for_each_entry(pos, &thermal_cdev_list, node)521521+ if (pos == cdev)522522+ break;523523+ if (pos != cdev) {524524+ /* thermal cooling device not found */525525+ mutex_unlock(&thermal_list_lock);526526+ return;527527+ }528528+ list_del(&cdev->node);529529+ list_for_each_entry(tz, &thermal_tz_list, node) {530530+ if (!tz->ops->unbind)531531+ continue;532532+ tz->ops->unbind(tz, cdev);533533+ }534534+ mutex_unlock(&thermal_list_lock);535535+ if (cdev->type[0])536536+ device_remove_file(&cdev->device,537537+ &dev_attr_cdev_type);538538+ device_remove_file(&cdev->device, &dev_attr_max_state);539539+ device_remove_file(&cdev->device, &dev_attr_cur_state);540540+541541+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);542542+ device_unregister(&cdev->device);543543+ return;544544+}545545+EXPORT_SYMBOL(thermal_cooling_device_unregister);546546+547547+/**548548+ * thermal_zone_device_register - register a new thermal zone device549549+ * @type: the thermal zone device type550550+ * @trips: the number of trip points the thermal zone support551551+ * @devdata: private device data552552+ * @ops: standard thermal zone device callbacks553553+ *554554+ * thermal_zone_device_unregister() must be called when the device is no555555+ * longer needed.556556+ */557557+struct thermal_zone_device *thermal_zone_device_register(char *type,558558+ int trips, void *devdata,559559+ struct thermal_zone_device_ops *ops)560560+{561561+ struct thermal_zone_device *tz;562562+ struct thermal_cooling_device *pos;563563+ int result;564564+ int count;565565+566566+ if (strlen(type) >= THERMAL_NAME_LENGTH)567567+ return NULL;568568+569569+ if (trips > THERMAL_MAX_TRIPS || trips < 0)570570+ return NULL;571571+572572+ if (!ops || !ops->get_temp)573573+ return NULL;574574+575575+ tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);576576+ if (!tz)577577+ return NULL;578578+579579+ INIT_LIST_HEAD(&tz->cooling_devices);580580+ idr_init(&tz->idr);581581+ mutex_init(&tz->lock);582582+ result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);583583+ if (result) {584584+ kfree(tz);585585+ return NULL;586586+ }587587+588588+ strcpy(tz->type, type);589589+ tz->ops = ops;590590+ tz->device.class = &thermal_class;591591+ tz->devdata = devdata;592592+ tz->trips = trips;593593+ sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);594594+ result = device_register(&tz->device);595595+ if (result) {596596+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);597597+ kfree(tz);598598+ return NULL;599599+ }600600+601601+ /* sys I/F */602602+ if (type) {603603+ result = device_create_file(&tz->device, &dev_attr_type);604604+ if (result)605605+ goto unregister;606606+ }607607+608608+ result = device_create_file(&tz->device, &dev_attr_temp);609609+ if (result)610610+ goto unregister;611611+612612+ if (ops->get_mode) {613613+ result = device_create_file(&tz->device, &dev_attr_mode);614614+ if (result)615615+ goto unregister;616616+ }617617+618618+ for (count = 0; count < trips; count++) {619619+ TRIP_POINT_ATTR_ADD(&tz->device, count, result);620620+ if (result)621621+ goto unregister;622622+ }623623+624624+ mutex_lock(&thermal_list_lock);625625+ list_add_tail(&tz->node, &thermal_tz_list);626626+ if (ops->bind)627627+ list_for_each_entry(pos, &thermal_cdev_list, node) {628628+ result = ops->bind(tz, pos);629629+ if (result)630630+ break;631631+ }632632+ mutex_unlock(&thermal_list_lock);633633+634634+ if (!result)635635+ return tz;636636+637637+ unregister:638638+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);639639+ device_unregister(&tz->device);640640+ return NULL;641641+}642642+EXPORT_SYMBOL(thermal_zone_device_register);643643+644644+/**645645+ * thermal_device_unregister - removes the registered thermal zone device646646+ *647647+ * @tz: the thermal zone device to remove648648+ */649649+void thermal_zone_device_unregister(struct thermal_zone_device *tz)650650+{651651+ struct thermal_cooling_device *cdev;652652+ struct thermal_zone_device *pos = NULL;653653+ int count;654654+655655+ if (!tz)656656+ return;657657+658658+ mutex_lock(&thermal_list_lock);659659+ list_for_each_entry(pos, &thermal_tz_list, node)660660+ if (pos == tz)661661+ break;662662+ if (pos != tz) {663663+ /* thermal zone device not found */664664+ mutex_unlock(&thermal_list_lock);665665+ return;666666+ }667667+ list_del(&tz->node);668668+ if (tz->ops->unbind)669669+ list_for_each_entry(cdev, &thermal_cdev_list, node)670670+ tz->ops->unbind(tz, cdev);671671+ mutex_unlock(&thermal_list_lock);672672+673673+ if (tz->type[0])674674+ device_remove_file(&tz->device, &dev_attr_type);675675+ device_remove_file(&tz->device, &dev_attr_temp);676676+ if (tz->ops->get_mode)677677+ device_remove_file(&tz->device, &dev_attr_mode);678678+679679+ for (count = 0; count < tz->trips; count++)680680+ TRIP_POINT_ATTR_REMOVE(&tz->device, count);681681+682682+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);683683+ idr_destroy(&tz->idr);684684+ mutex_destroy(&tz->lock);685685+ device_unregister(&tz->device);686686+ return;687687+}688688+EXPORT_SYMBOL(thermal_zone_device_unregister);689689+690690+static int __init thermal_init(void)691691+{692692+ int result = 0;693693+694694+ result = class_register(&thermal_class);695695+ if (result) {696696+ idr_destroy(&thermal_tz_idr);697697+ idr_destroy(&thermal_cdev_idr);698698+ mutex_destroy(&thermal_idr_lock);699699+ mutex_destroy(&thermal_list_lock);700700+ }701701+ return result;702702+}703703+704704+static void __exit thermal_exit(void)705705+{706706+ class_unregister(&thermal_class);707707+ idr_destroy(&thermal_tz_idr);708708+ idr_destroy(&thermal_cdev_idr);709709+ mutex_destroy(&thermal_idr_lock);710710+ mutex_destroy(&thermal_list_lock);711711+}712712+713713+subsys_initcall(thermal_init);714714+module_exit(thermal_exit);
···44#include <linux/kernel.h>55#include <linux/cpu.h>66#include <linux/cpuidle.h>77-77+#include <linux/thermal.h>88#include <asm/acpi.h>991010#define ACPI_PROCESSOR_BUSY_METRIC 10···219219 struct acpi_processor_performance *performance;220220 struct acpi_processor_throttling throttling;221221 struct acpi_processor_limit limit;222222-222222+ struct thermal_cooling_device *cdev;223223 /* the _PDC objects for this processor, if any */224224 struct acpi_object_list *pdc;225225};···331331/* in processor_thermal.c */332332int acpi_processor_get_limit_info(struct acpi_processor *pr);333333extern struct file_operations acpi_processor_limit_fops;334334-334334+extern struct thermal_cooling_device_ops processor_cooling_ops;335335#ifdef CONFIG_CPU_FREQ336336void acpi_thermal_cpufreq_init(void);337337void acpi_thermal_cpufreq_exit(void);
+94
include/linux/thermal.h
···11+/*22+ * thermal.h ($Revision: 0 $)33+ *44+ * Copyright (C) 2008 Intel Corp55+ * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>66+ * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>77+ *88+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~99+ * This program is free software; you can redistribute it and/or modify1010+ * it under the terms of the GNU General Public License as published by1111+ * the Free Software Foundation; version 2 of the License.1212+ *1313+ * This program is distributed in the hope that it will be useful, but1414+ * WITHOUT ANY WARRANTY; without even the implied warranty of1515+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU1616+ * General Public License for more details.1717+ *1818+ * You should have received a copy of the GNU General Public License along1919+ * with this program; if not, write to the Free Software Foundation, Inc.,2020+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.2121+ *2222+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~2323+ */2424+2525+#ifndef __THERMAL_H__2626+#define __THERMAL_H__2727+2828+#include <linux/idr.h>2929+#include <linux/device.h>3030+3131+struct thermal_zone_device;3232+struct thermal_cooling_device;3333+3434+struct thermal_zone_device_ops {3535+ int (*bind) (struct thermal_zone_device *,3636+ struct thermal_cooling_device *);3737+ int (*unbind) (struct thermal_zone_device *,3838+ struct thermal_cooling_device *);3939+ int (*get_temp) (struct thermal_zone_device *, char *);4040+ int (*get_mode) (struct thermal_zone_device *, char *);4141+ int (*set_mode) (struct thermal_zone_device *, const char *);4242+ int (*get_trip_type) (struct thermal_zone_device *, int, char *);4343+ int (*get_trip_temp) (struct thermal_zone_device *, int, char *);4444+};4545+4646+struct thermal_cooling_device_ops {4747+ int (*get_max_state) (struct thermal_cooling_device *, char *);4848+ int (*get_cur_state) (struct thermal_cooling_device *, char *);4949+ int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);5050+};5151+5252+#define THERMAL_TRIPS_NONE -15353+#define THERMAL_MAX_TRIPS 105454+#define THERMAL_NAME_LENGTH 205555+struct thermal_cooling_device {5656+ int id;5757+ char type[THERMAL_NAME_LENGTH];5858+ struct device device;5959+ void *devdata;6060+ struct thermal_cooling_device_ops *ops;6161+ struct list_head node;6262+};6363+6464+#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \6565+ ((long)t-2732+5)/10 : ((long)t-2732-5)/10)6666+#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)6767+6868+struct thermal_zone_device {6969+ int id;7070+ char type[THERMAL_NAME_LENGTH];7171+ struct device device;7272+ void *devdata;7373+ int trips;7474+ struct thermal_zone_device_ops *ops;7575+ struct list_head cooling_devices;7676+ struct idr idr;7777+ struct mutex lock; /* protect cooling devices list */7878+ struct list_head node;7979+};8080+8181+struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,8282+ struct thermal_zone_device_ops *);8383+void thermal_zone_device_unregister(struct thermal_zone_device *);8484+8585+int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,8686+ struct thermal_cooling_device *);8787+int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,8888+ struct thermal_cooling_device *);8989+9090+struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,9191+ struct thermal_cooling_device_ops *);9292+void thermal_cooling_device_unregister(struct thermal_cooling_device *);9393+9494+#endif /* __THERMAL_H__ */