Commit 26b6f2236615649a0ae6a0de2e9e71a2f9ffeba7

Authored by Len Brown

Merge branches 'release' and 'menlo' into release

Conflicts:

	drivers/acpi/video.c

Signed-off-by: Len Brown <len.brown@intel.com>

Showing 19 changed files Side-by-side Diff

Documentation/thermal/sysfs-api.txt
  1 +Generic Thermal Sysfs driver How To
  2 +=========================
  3 +
  4 +Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com>
  5 +
  6 +Updated: 2 January 2008
  7 +
  8 +Copyright (c) 2008 Intel Corporation
  9 +
  10 +
  11 +0. Introduction
  12 +
  13 +The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors)
  14 +and thermal cooling devices (fan, processor...) to register with the thermal management
  15 +solution and to be a part of it.
  16 +
  17 +This how-to focusses on enabling new thermal zone and cooling devices to participate
  18 +in thermal management.
  19 +This solution is platform independent and any type of thermal zone devices and
  20 +cooling devices should be able to make use of the infrastructure.
  21 +
  22 +The main task of the thermal sysfs driver is to expose thermal zone attributes as well
  23 +as cooling device attributes to the user space.
  24 +An intelligent thermal management application can make decisions based on inputs
  25 +from thermal zone attributes (the current temperature and trip point temperature)
  26 +and throttle appropriate devices.
  27 +
  28 +[0-*] denotes any positive number starting from 0
  29 +[1-*] denotes any positive number starting from 1
  30 +
  31 +1. thermal sysfs driver interface functions
  32 +
  33 +1.1 thermal zone device interface
  34 +1.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips,
  35 + void *devdata, struct thermal_zone_device_ops *ops)
  36 +
  37 + This interface function adds a new thermal zone device (sensor) to
  38 + /sys/class/thermal folder as thermal_zone[0-*].
  39 + It tries to bind all the thermal cooling devices registered at the same time.
  40 +
  41 + name: the thermal zone name.
  42 + trips: the total number of trip points this thermal zone supports.
  43 + devdata: device private data
  44 + ops: thermal zone device callbacks.
  45 + .bind: bind the thermal zone device with a thermal cooling device.
  46 + .unbind: unbing the thermal zone device with a thermal cooling device.
  47 + .get_temp: get the current temperature of the thermal zone.
  48 + .get_mode: get the current mode (user/kernel) of the thermal zone.
  49 + "kernel" means thermal management is done in kernel.
  50 + "user" will prevent kernel thermal driver actions upon trip points
  51 + so that user applications can take charge of thermal management.
  52 + .set_mode: set the mode (user/kernel) of the thermal zone.
  53 + .get_trip_type: get the type of certain trip point.
  54 + .get_trip_temp: get the temperature above which the certain trip point
  55 + will be fired.
  56 +
  57 +1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
  58 +
  59 + This interface function removes the thermal zone device.
  60 + It deletes the corresponding entry form /sys/class/thermal folder and unbind all
  61 + the thermal cooling devices it uses.
  62 +
  63 +1.2 thermal cooling device interface
  64 +1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
  65 + void *devdata, struct thermal_cooling_device_ops *)
  66 +
  67 + This interface function adds a new thermal cooling device (fan/processor/...) to
  68 + /sys/class/thermal/ folder as cooling_device[0-*].
  69 + It tries to bind itself to all the thermal zone devices register at the same time.
  70 + name: the cooling device name.
  71 + devdata: device private data.
  72 + ops: thermal cooling devices callbacks.
  73 + .get_max_state: get the Maximum throttle state of the cooling device.
  74 + .get_cur_state: get the Current throttle state of the cooling device.
  75 + .set_cur_state: set the Current throttle state of the cooling device.
  76 +
  77 +1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
  78 +
  79 + This interface function remove the thermal cooling device.
  80 + It deletes the corresponding entry form /sys/class/thermal folder and unbind
  81 + itself from all the thermal zone devices using it.
  82 +
  83 +1.3 interface for binding a thermal zone device with a thermal cooling device
  84 +1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
  85 + int trip, struct thermal_cooling_device *cdev);
  86 +
  87 + This interface function bind a thermal cooling device to the certain trip point
  88 + of a thermal zone device.
  89 + This function is usually called in the thermal zone device .bind callback.
  90 + tz: the thermal zone device
  91 + cdev: thermal cooling device
  92 + trip: indicates which trip point the cooling devices is associated with
  93 + in this thermal zone.
  94 +
  95 +1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
  96 + int trip, struct thermal_cooling_device *cdev);
  97 +
  98 + This interface function unbind a thermal cooling device from the certain trip point
  99 + of a thermal zone device.
  100 + This function is usually called in the thermal zone device .unbind callback.
  101 + tz: the thermal zone device
  102 + cdev: thermal cooling device
  103 + trip: indicates which trip point the cooling devices is associated with
  104 + in this thermal zone.
  105 +
  106 +2. sysfs attributes structure
  107 +
  108 +RO read only value
  109 +RW read/write value
  110 +
  111 +All thermal sysfs attributes will be represented under /sys/class/thermal
  112 +/sys/class/thermal/
  113 +
  114 +Thermal zone device sys I/F, created once it's registered:
  115 +|thermal_zone[0-*]:
  116 + |-----type: Type of the thermal zone
  117 + |-----temp: Current temperature
  118 + |-----mode: Working mode of the thermal zone
  119 + |-----trip_point_[0-*]_temp: Trip point temperature
  120 + |-----trip_point_[0-*]_type: Trip point type
  121 +
  122 +Thermal cooling device sys I/F, created once it's registered:
  123 +|cooling_device[0-*]:
  124 + |-----type : Type of the cooling device(processor/fan/...)
  125 + |-----max_state: Maximum cooling state of the cooling device
  126 + |-----cur_state: Current cooling state of the cooling device
  127 +
  128 +
  129 +These two dynamic attributes are created/removed in pairs.
  130 +They represent the relationship between a thermal zone and its associated cooling device.
  131 +They are created/removed for each
  132 +thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection.
  133 +
  134 +|thermal_zone[0-*]
  135 + |-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone
  136 + |-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with
  137 +
  138 +
  139 +***************************
  140 +* Thermal zone attributes *
  141 +***************************
  142 +
  143 +type Strings which represent the thermal zone type.
  144 + This is given by thermal zone driver as part of registration.
  145 + Eg: "ACPI thermal zone" indicates it's a ACPI thermal device
  146 + RO
  147 + Optional
  148 +
  149 +temp Current temperature as reported by thermal zone (sensor)
  150 + Unit: degree celsius
  151 + RO
  152 + Required
  153 +
  154 +mode One of the predifned values in [kernel, user]
  155 + This file gives information about the algorithm
  156 + that is currently managing the thermal zone.
  157 + It can be either default kernel based algorithm
  158 + or user space application.
  159 + RW
  160 + Optional
  161 + kernel = Thermal management in kernel thermal zone driver.
  162 + user = Preventing kernel thermal zone driver actions upon
  163 + trip points so that user application can take full
  164 + charge of the thermal management.
  165 +
  166 +trip_point_[0-*]_temp The temperature above which trip point will be fired
  167 + Unit: degree celsius
  168 + RO
  169 + Optional
  170 +
  171 +trip_point_[0-*]_type Strings which indicate the type of the trip point
  172 + Eg. it can be one of critical, hot, passive,
  173 + active[0-*] for ACPI thermal zone.
  174 + RO
  175 + Optional
  176 +
  177 +cdev[0-*] Sysfs link to the thermal cooling device node where the sys I/F
  178 + for cooling device throttling control represents.
  179 + RO
  180 + Optional
  181 +
  182 +cdev[0-*]_trip_point The trip point with which cdev[0-*] is assocated in this thermal zone
  183 + -1 means the cooling device is not associated with any trip point.
  184 + RO
  185 + Optional
  186 +
  187 +******************************
  188 +* Cooling device attributes *
  189 +******************************
  190 +
  191 +type String which represents the type of device
  192 + eg: For generic ACPI: this should be "Fan",
  193 + "Processor" or "LCD"
  194 + eg. For memory controller device on intel_menlow platform:
  195 + this should be "Memory controller"
  196 + RO
  197 + Optional
  198 +
  199 +max_state The maximum permissible cooling state of this cooling device.
  200 + RO
  201 + Required
  202 +
  203 +cur_state The current cooling state of this cooling device.
  204 + the value can any integer numbers between 0 and max_state,
  205 + cur_state == 0 means no cooling
  206 + cur_state == max_state means the maximum cooling.
  207 + RW
  208 + Required
  209 +
  210 +3. A simple implementation
  211 +
  212 +ACPI thermal zone may support multiple trip points like critical/hot/passive/active.
  213 +If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time,
  214 +it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all.
  215 +It has one processor and one fan, which are both registered as thermal_cooling_device.
  216 +If the processor is listed in _PSL method, and the fan is listed in _AL0 method,
  217 +the sys I/F structure will be built like this:
  218 +
  219 +/sys/class/thermal:
  220 +
  221 +|thermal_zone1:
  222 + |-----type: ACPI thermal zone
  223 + |-----temp: 37
  224 + |-----mode: kernel
  225 + |-----trip_point_0_temp: 100
  226 + |-----trip_point_0_type: critical
  227 + |-----trip_point_1_temp: 80
  228 + |-----trip_point_1_type: passive
  229 + |-----trip_point_2_temp: 70
  230 + |-----trip_point_2_type: active[0]
  231 + |-----trip_point_3_temp: 60
  232 + |-----trip_point_3_type: active[1]
  233 + |-----cdev0: --->/sys/class/thermal/cooling_device0
  234 + |-----cdev0_trip_point: 1 /* cdev0 can be used for passive */
  235 + |-----cdev1: --->/sys/class/thermal/cooling_device3
  236 + |-----cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/
  237 +
  238 +|cooling_device0:
  239 + |-----type: Processor
  240 + |-----max_state: 8
  241 + |-----cur_state: 0
  242 +
  243 +|cooling_device3:
  244 + |-----type: Fan
  245 + |-----max_state: 2
  246 + |-----cur_state: 0
... ... @@ -60,6 +60,8 @@
60 60  
61 61 source "drivers/hwmon/Kconfig"
62 62  
  63 +source "drivers/thermal/Kconfig"
  64 +
63 65 source "drivers/watchdog/Kconfig"
64 66  
65 67 source "drivers/ssb/Kconfig"
... ... @@ -65,6 +65,7 @@
65 65 obj-$(CONFIG_W1) += w1/
66 66 obj-$(CONFIG_POWER_SUPPLY) += power/
67 67 obj-$(CONFIG_HWMON) += hwmon/
  68 +obj-$(CONFIG_THERMAL) += thermal/
68 69 obj-$(CONFIG_WATCHDOG) += watchdog/
69 70 obj-$(CONFIG_PHONE) += telephony/
70 71 obj-$(CONFIG_MD) += md/
drivers/acpi/Kconfig
... ... @@ -186,6 +186,7 @@
186 186 config ACPI_THERMAL
187 187 tristate "Thermal Zone"
188 188 depends on ACPI_PROCESSOR
  189 + select THERMAL
189 190 default y
190 191 help
191 192 This driver adds support for ACPI thermal zones. Most mobile and
... ... @@ -122,6 +122,31 @@
122 122  
123 123 EXPORT_SYMBOL(acpi_bus_get_status);
124 124  
  125 +void acpi_bus_private_data_handler(acpi_handle handle,
  126 + u32 function, void *context)
  127 +{
  128 + return;
  129 +}
  130 +EXPORT_SYMBOL(acpi_bus_private_data_handler);
  131 +
  132 +int acpi_bus_get_private_data(acpi_handle handle, void **data)
  133 +{
  134 + acpi_status status = AE_OK;
  135 +
  136 + if (!*data)
  137 + return -EINVAL;
  138 +
  139 + status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
  140 + if (ACPI_FAILURE(status) || !*data) {
  141 + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
  142 + handle));
  143 + return -ENODEV;
  144 + }
  145 +
  146 + return 0;
  147 +}
  148 +EXPORT_SYMBOL(acpi_bus_get_private_data);
  149 +
125 150 /* --------------------------------------------------------------------------
126 151 Power Management
127 152 -------------------------------------------------------------------------- */
... ... @@ -30,7 +30,7 @@
30 30 #include <linux/proc_fs.h>
31 31 #include <linux/seq_file.h>
32 32 #include <asm/uaccess.h>
33   -
  33 +#include <linux/thermal.h>
34 34 #include <acpi/acpi_bus.h>
35 35 #include <acpi/acpi_drivers.h>
36 36  
37 37  
... ... @@ -68,9 +68,55 @@
68 68 },
69 69 };
70 70  
  71 +/* thermal cooling device callbacks */
  72 +static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf)
  73 +{
  74 + /* ACPI fan device only support two states: ON/OFF */
  75 + return sprintf(buf, "1\n");
  76 +}
  77 +
  78 +static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
  79 +{
  80 + struct acpi_device *device = cdev->devdata;
  81 + int state;
  82 + int result;
  83 +
  84 + if (!device)
  85 + return -EINVAL;
  86 +
  87 + result = acpi_bus_get_power(device->handle, &state);
  88 + if (result)
  89 + return result;
  90 +
  91 + return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
  92 + (state == ACPI_STATE_D0 ? "1" : "unknown"));
  93 +}
  94 +
  95 +static int
  96 +fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
  97 +{
  98 + struct acpi_device *device = cdev->devdata;
  99 + int result;
  100 +
  101 + if (!device || (state != 0 && state != 1))
  102 + return -EINVAL;
  103 +
  104 + result = acpi_bus_set_power(device->handle,
  105 + state ? ACPI_STATE_D0 : ACPI_STATE_D3);
  106 +
  107 + return result;
  108 +}
  109 +
  110 +static struct thermal_cooling_device_ops fan_cooling_ops = {
  111 + .get_max_state = fan_get_max_state,
  112 + .get_cur_state = fan_get_cur_state,
  113 + .set_cur_state = fan_set_cur_state,
  114 +};
  115 +
71 116 /* --------------------------------------------------------------------------
72 117 FS Interface (/proc)
73 118 -------------------------------------------------------------------------- */
  119 +#ifdef CONFIG_ACPI_PROCFS
74 120  
75 121 static struct proc_dir_entry *acpi_fan_dir;
76 122  
77 123  
... ... @@ -171,7 +217,17 @@
171 217  
172 218 return 0;
173 219 }
  220 +#else
  221 +static int acpi_fan_add_fs(struct acpi_device *device)
  222 +{
  223 + return 0;
  224 +}
174 225  
  226 +static int acpi_fan_remove_fs(struct acpi_device *device)
  227 +{
  228 + return 0;
  229 +}
  230 +#endif
175 231 /* --------------------------------------------------------------------------
176 232 Driver Interface
177 233 -------------------------------------------------------------------------- */
178 234  
179 235  
... ... @@ -179,10 +235,9 @@
179 235 static int acpi_fan_add(struct acpi_device *device)
180 236 {
181 237 int result = 0;
182   - struct acpi_fan *fan = NULL;
183 238 int state = 0;
  239 + struct thermal_cooling_device *cdev;
184 240  
185   -
186 241 if (!device)
187 242 return -EINVAL;
188 243  
... ... @@ -199,6 +254,25 @@
199 254 acpi_bus_set_power(device->handle, state);
200 255 device->flags.force_power_state = 0;
201 256  
  257 + cdev = thermal_cooling_device_register("Fan", device,
  258 + &fan_cooling_ops);
  259 + if (cdev)
  260 + printk(KERN_INFO PREFIX
  261 + "%s is registered as cooling_device%d\n",
  262 + device->dev.bus_id, cdev->id);
  263 + else
  264 + goto end;
  265 + acpi_driver_data(device) = cdev;
  266 + result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj,
  267 + "thermal_cooling");
  268 + if (result)
  269 + return result;
  270 +
  271 + result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj,
  272 + "device");
  273 + if (result)
  274 + return result;
  275 +
202 276 result = acpi_fan_add_fs(device);
203 277 if (result)
204 278 goto end;
205 279  
206 280  
... ... @@ -208,18 +282,20 @@
208 282 !device->power.state ? "on" : "off");
209 283  
210 284 end:
211   - if (result)
212   - kfree(fan);
213   -
214 285 return result;
215 286 }
216 287  
217 288 static int acpi_fan_remove(struct acpi_device *device, int type)
218 289 {
219   - if (!device || !acpi_driver_data(device))
  290 + struct thermal_cooling_device *cdev = acpi_driver_data(device);
  291 +
  292 + if (!device || !cdev)
220 293 return -EINVAL;
221 294  
222 295 acpi_fan_remove_fs(device);
  296 + sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
  297 + sysfs_remove_link(&cdev->device.kobj, "device");
  298 + thermal_cooling_device_unregister(cdev);
223 299  
224 300 return 0;
225 301 }
226 302  
... ... @@ -261,10 +337,12 @@
261 337 int result = 0;
262 338  
263 339  
  340 +#ifdef CONFIG_ACPI_PROCFS
264 341 acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
265 342 if (!acpi_fan_dir)
266 343 return -ENODEV;
267 344 acpi_fan_dir->owner = THIS_MODULE;
  345 +#endif
268 346  
269 347 result = acpi_bus_register_driver(&acpi_fan_driver);
270 348 if (result < 0) {
drivers/acpi/processor_core.c
... ... @@ -668,6 +668,24 @@
668 668  
669 669 acpi_processor_power_init(pr, device);
670 670  
  671 + pr->cdev = thermal_cooling_device_register("Processor", device,
  672 + &processor_cooling_ops);
  673 + if (pr->cdev)
  674 + printk(KERN_INFO PREFIX
  675 + "%s is registered as cooling_device%d\n",
  676 + device->dev.bus_id, pr->cdev->id);
  677 + else
  678 + goto end;
  679 +
  680 + result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj,
  681 + "thermal_cooling");
  682 + if (result)
  683 + return result;
  684 + result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj,
  685 + "device");
  686 + if (result)
  687 + return result;
  688 +
671 689 if (pr->flags.throttling) {
672 690 printk(KERN_INFO PREFIX "%s [%s] (supports",
673 691 acpi_device_name(device), acpi_device_bid(device));
... ... @@ -790,6 +808,11 @@
790 808 acpi_processor_notify);
791 809  
792 810 acpi_processor_remove_fs(device);
  811 +
  812 + sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
  813 + sysfs_remove_link(&pr->cdev->device.kobj, "device");
  814 + thermal_cooling_device_unregister(pr->cdev);
  815 + pr->cdev = NULL;
793 816  
794 817 processors[pr->id] = NULL;
795 818  
drivers/acpi/processor_thermal.c
... ... @@ -32,6 +32,7 @@
32 32 #include <linux/cpufreq.h>
33 33 #include <linux/proc_fs.h>
34 34 #include <linux/seq_file.h>
  35 +#include <linux/sysdev.h>
35 36  
36 37 #include <asm/uaccess.h>
37 38  
... ... @@ -93,6 +94,9 @@
93 94 * _any_ cpufreq driver and not only the acpi-cpufreq driver.
94 95 */
95 96  
  97 +#define CPUFREQ_THERMAL_MIN_STEP 0
  98 +#define CPUFREQ_THERMAL_MAX_STEP 3
  99 +
96 100 static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
97 101 static unsigned int acpi_thermal_cpufreq_is_init = 0;
98 102  
... ... @@ -109,8 +113,9 @@
109 113 if (!cpu_has_cpufreq(cpu))
110 114 return -ENODEV;
111 115  
112   - if (cpufreq_thermal_reduction_pctg[cpu] < 60) {
113   - cpufreq_thermal_reduction_pctg[cpu] += 20;
  116 + if (cpufreq_thermal_reduction_pctg[cpu] <
  117 + CPUFREQ_THERMAL_MAX_STEP) {
  118 + cpufreq_thermal_reduction_pctg[cpu]++;
114 119 cpufreq_update_policy(cpu);
115 120 return 0;
116 121 }
... ... @@ -123,8 +128,9 @@
123 128 if (!cpu_has_cpufreq(cpu))
124 129 return -ENODEV;
125 130  
126   - if (cpufreq_thermal_reduction_pctg[cpu] > 20)
127   - cpufreq_thermal_reduction_pctg[cpu] -= 20;
  131 + if (cpufreq_thermal_reduction_pctg[cpu] >
  132 + (CPUFREQ_THERMAL_MIN_STEP + 1))
  133 + cpufreq_thermal_reduction_pctg[cpu]--;
128 134 else
129 135 cpufreq_thermal_reduction_pctg[cpu] = 0;
130 136 cpufreq_update_policy(cpu);
... ... @@ -143,7 +149,7 @@
143 149  
144 150 max_freq =
145 151 (policy->cpuinfo.max_freq *
146   - (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;
  152 + (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100;
147 153  
148 154 cpufreq_verify_within_limits(policy, 0, max_freq);
149 155  
... ... @@ -155,6 +161,32 @@
155 161 .notifier_call = acpi_thermal_cpufreq_notifier,
156 162 };
157 163  
  164 +static int cpufreq_get_max_state(unsigned int cpu)
  165 +{
  166 + if (!cpu_has_cpufreq(cpu))
  167 + return 0;
  168 +
  169 + return CPUFREQ_THERMAL_MAX_STEP;
  170 +}
  171 +
  172 +static int cpufreq_get_cur_state(unsigned int cpu)
  173 +{
  174 + if (!cpu_has_cpufreq(cpu))
  175 + return 0;
  176 +
  177 + return cpufreq_thermal_reduction_pctg[cpu];
  178 +}
  179 +
  180 +static int cpufreq_set_cur_state(unsigned int cpu, int state)
  181 +{
  182 + if (!cpu_has_cpufreq(cpu))
  183 + return 0;
  184 +
  185 + cpufreq_thermal_reduction_pctg[cpu] = state;
  186 + cpufreq_update_policy(cpu);
  187 + return 0;
  188 +}
  189 +
158 190 void acpi_thermal_cpufreq_init(void)
159 191 {
160 192 int i;
161 193  
... ... @@ -179,7 +211,21 @@
179 211 }
180 212  
181 213 #else /* ! CONFIG_CPU_FREQ */
  214 +static int cpufreq_get_max_state(unsigned int cpu)
  215 +{
  216 + return 0;
  217 +}
182 218  
  219 +static int cpufreq_get_cur_state(unsigned int cpu)
  220 +{
  221 + return 0;
  222 +}
  223 +
  224 +static int cpufreq_set_cur_state(unsigned int cpu, int state)
  225 +{
  226 + return 0;
  227 +}
  228 +
183 229 static int acpi_thermal_cpufreq_increase(unsigned int cpu)
184 230 {
185 231 return -ENODEV;
... ... @@ -309,6 +355,84 @@
309 355  
310 356 return 0;
311 357 }
  358 +
  359 +/* thermal coolign device callbacks */
  360 +static int acpi_processor_max_state(struct acpi_processor *pr)
  361 +{
  362 + int max_state = 0;
  363 +
  364 + /*
  365 + * There exists four states according to
  366 + * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3
  367 + */
  368 + max_state += cpufreq_get_max_state(pr->id);
  369 + if (pr->flags.throttling)
  370 + max_state += (pr->throttling.state_count -1);
  371 +
  372 + return max_state;
  373 +}
  374 +static int
  375 +processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
  376 +{
  377 + struct acpi_device *device = cdev->devdata;
  378 + struct acpi_processor *pr = acpi_driver_data(device);
  379 +
  380 + if (!device || !pr)
  381 + return -EINVAL;
  382 +
  383 + return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
  384 +}
  385 +
  386 +static int
  387 +processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
  388 +{
  389 + struct acpi_device *device = cdev->devdata;
  390 + struct acpi_processor *pr = acpi_driver_data(device);
  391 + int cur_state;
  392 +
  393 + if (!device || !pr)
  394 + return -EINVAL;
  395 +
  396 + cur_state = cpufreq_get_cur_state(pr->id);
  397 + if (pr->flags.throttling)
  398 + cur_state += pr->throttling.state;
  399 +
  400 + return sprintf(buf, "%d\n", cur_state);
  401 +}
  402 +
  403 +static int
  404 +processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
  405 +{
  406 + struct acpi_device *device = cdev->devdata;
  407 + struct acpi_processor *pr = acpi_driver_data(device);
  408 + int result = 0;
  409 + int max_pstate;
  410 +
  411 + if (!device || !pr)
  412 + return -EINVAL;
  413 +
  414 + max_pstate = cpufreq_get_max_state(pr->id);
  415 +
  416 + if (state > acpi_processor_max_state(pr))
  417 + return -EINVAL;
  418 +
  419 + if (state <= max_pstate) {
  420 + if (pr->flags.throttling && pr->throttling.state)
  421 + result = acpi_processor_set_throttling(pr, 0);
  422 + cpufreq_set_cur_state(pr->id, state);
  423 + } else {
  424 + cpufreq_set_cur_state(pr->id, max_pstate);
  425 + result = acpi_processor_set_throttling(pr,
  426 + state - max_pstate);
  427 + }
  428 + return result;
  429 +}
  430 +
  431 +struct thermal_cooling_device_ops processor_cooling_ops = {
  432 + .get_max_state = processor_get_max_state,
  433 + .get_cur_state = processor_get_cur_state,
  434 + .set_cur_state = processor_set_cur_state,
  435 +};
312 436  
313 437 /* /proc interface */
314 438  
drivers/acpi/thermal.c
... ... @@ -43,7 +43,7 @@
43 43 #include <linux/seq_file.h>
44 44 #include <linux/reboot.h>
45 45 #include <asm/uaccess.h>
46   -
  46 +#include <linux/thermal.h>
47 47 #include <acpi/acpi_bus.h>
48 48 #include <acpi/acpi_drivers.h>
49 49  
... ... @@ -65,9 +65,6 @@
65 65 #define ACPI_THERMAL_MAX_ACTIVE 10
66 66 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
67 67  
68   -#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
69   -#define CELSIUS_TO_KELVIN(t) ((t+273)*10)
70   -
71 68 #define _COMPONENT ACPI_THERMAL_COMPONENT
72 69 ACPI_MODULE_NAME("thermal");
73 70  
... ... @@ -195,6 +192,8 @@
195 192 struct acpi_thermal_trips trips;
196 193 struct acpi_handle_list devices;
197 194 struct timer_list timer;
  195 + struct thermal_zone_device *thermal_zone;
  196 + int tz_enabled;
198 197 struct mutex lock;
199 198 };
200 199  
201 200  
202 201  
203 202  
204 203  
205 204  
206 205  
207 206  
208 207  
209 208  
210 209  
211 210  
212 211  
213 212  
214 213  
215 214  
216 215  
217 216  
218 217  
219 218  
220 219  
221 220  
222 221  
223 222  
224 223  
225 224  
... ... @@ -321,173 +320,221 @@
321 320 return 0;
322 321 }
323 322  
324   -static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
325   -{
326   - acpi_status status = AE_OK;
327   - int i = 0;
  323 +#define ACPI_TRIPS_CRITICAL 0x01
  324 +#define ACPI_TRIPS_HOT 0x02
  325 +#define ACPI_TRIPS_PASSIVE 0x04
  326 +#define ACPI_TRIPS_ACTIVE 0x08
  327 +#define ACPI_TRIPS_DEVICES 0x10
328 328  
  329 +#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
  330 +#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
329 331  
330   - if (!tz)
331   - return -EINVAL;
  332 +#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
  333 + ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
  334 + ACPI_TRIPS_DEVICES)
332 335  
333   - /* Critical Shutdown (required) */
  336 +/*
  337 + * This exception is thrown out in two cases:
  338 + * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
  339 + * when re-evaluating the AML code.
  340 + * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
  341 + * We need to re-bind the cooling devices of a thermal zone when this occurs.
  342 + */
  343 +#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \
  344 +do { \
  345 + if (flags != ACPI_TRIPS_INIT) \
  346 + ACPI_EXCEPTION((AE_INFO, AE_ERROR, \
  347 + "ACPI thermal trip point %s changed\n" \
  348 + "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
  349 +} while (0)
334 350  
335   - status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,
336   - &tz->trips.critical.temperature);
337   - if (ACPI_FAILURE(status)) {
338   - tz->trips.critical.flags.valid = 0;
339   - ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));
340   - return -ENODEV;
341   - } else {
342   - tz->trips.critical.flags.valid = 1;
343   - ACPI_DEBUG_PRINT((ACPI_DB_INFO,
344   - "Found critical threshold [%lu]\n",
345   - tz->trips.critical.temperature));
346   - }
  351 +static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
  352 +{
  353 + acpi_status status = AE_OK;
  354 + struct acpi_handle_list devices;
  355 + int valid = 0;
  356 + int i;
347 357  
348   - if (tz->trips.critical.flags.valid == 1) {
349   - if (crt == -1) {
  358 + /* Critical Shutdown (required) */
  359 + if (flag & ACPI_TRIPS_CRITICAL) {
  360 + status = acpi_evaluate_integer(tz->device->handle,
  361 + "_CRT", NULL, &tz->trips.critical.temperature);
  362 + if (ACPI_FAILURE(status)) {
350 363 tz->trips.critical.flags.valid = 0;
351   - } else if (crt > 0) {
352   - unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
353   -
354   - /*
355   - * Allow override to lower critical threshold
356   - */
357   - if (crt_k < tz->trips.critical.temperature)
358   - tz->trips.critical.temperature = crt_k;
  364 + ACPI_EXCEPTION((AE_INFO, status,
  365 + "No critical threshold"));
  366 + return -ENODEV;
  367 + } else {
  368 + tz->trips.critical.flags.valid = 1;
  369 + ACPI_DEBUG_PRINT((ACPI_DB_INFO,
  370 + "Found critical threshold [%lu]\n",
  371 + tz->trips.critical.temperature));
359 372 }
  373 + if (tz->trips.critical.flags.valid == 1) {
  374 + if (crt == -1) {
  375 + tz->trips.critical.flags.valid = 0;
  376 + } else if (crt > 0) {
  377 + unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
  378 + /*
  379 + * Allow override to lower critical threshold
  380 + */
  381 + if (crt_k < tz->trips.critical.temperature)
  382 + tz->trips.critical.temperature = crt_k;
  383 + }
  384 + }
360 385 }
361 386  
362 387 /* Critical Sleep (optional) */
363   -
364   - status =
365   - acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
366   - &tz->trips.hot.temperature);
367   - if (ACPI_FAILURE(status)) {
368   - tz->trips.hot.flags.valid = 0;
369   - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
370   - } else {
371   - tz->trips.hot.flags.valid = 1;
372   - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
373   - tz->trips.hot.temperature));
374   - }
375   -
376   - /* Passive: Processors (optional) */
377   -
378   - if (psv == -1) {
379   - status = AE_SUPPORT;
380   - } else if (psv > 0) {
381   - tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
382   - status = AE_OK;
383   - } else {
  388 + if (flag & ACPI_TRIPS_HOT) {
384 389 status = acpi_evaluate_integer(tz->device->handle,
385   - "_PSV", NULL, &tz->trips.passive.temperature);
  390 + "_HOT", NULL, &tz->trips.hot.temperature);
  391 + if (ACPI_FAILURE(status)) {
  392 + tz->trips.hot.flags.valid = 0;
  393 + ACPI_DEBUG_PRINT((ACPI_DB_INFO,
  394 + "No hot threshold\n"));
  395 + } else {
  396 + tz->trips.hot.flags.valid = 1;
  397 + ACPI_DEBUG_PRINT((ACPI_DB_INFO,
  398 + "Found hot threshold [%lu]\n",
  399 + tz->trips.critical.temperature));
  400 + }
386 401 }
387 402  
388   - if (ACPI_FAILURE(status)) {
389   - tz->trips.passive.flags.valid = 0;
390   - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
391   - } else {
392   - tz->trips.passive.flags.valid = 1;
  403 + /* Passive (optional) */
  404 + if (flag & ACPI_TRIPS_PASSIVE) {
  405 + valid = tz->trips.passive.flags.valid;
  406 + if (psv == -1) {
  407 + status = AE_SUPPORT;
  408 + } else if (psv > 0) {
  409 + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
  410 + status = AE_OK;
  411 + } else {
  412 + status = acpi_evaluate_integer(tz->device->handle,
  413 + "_PSV", NULL, &tz->trips.passive.temperature);
  414 + }
393 415  
394   - status =
395   - acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,
396   - &tz->trips.passive.tc1);
397 416 if (ACPI_FAILURE(status))
398 417 tz->trips.passive.flags.valid = 0;
399   -
400   - status =
401   - acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
402   - &tz->trips.passive.tc2);
  418 + else {
  419 + tz->trips.passive.flags.valid = 1;
  420 + if (flag == ACPI_TRIPS_INIT) {
  421 + status = acpi_evaluate_integer(
  422 + tz->device->handle, "_TC1",
  423 + NULL, &tz->trips.passive.tc1);
  424 + if (ACPI_FAILURE(status))
  425 + tz->trips.passive.flags.valid = 0;
  426 + status = acpi_evaluate_integer(
  427 + tz->device->handle, "_TC2",
  428 + NULL, &tz->trips.passive.tc2);
  429 + if (ACPI_FAILURE(status))
  430 + tz->trips.passive.flags.valid = 0;
  431 + status = acpi_evaluate_integer(
  432 + tz->device->handle, "_TSP",
  433 + NULL, &tz->trips.passive.tsp);
  434 + if (ACPI_FAILURE(status))
  435 + tz->trips.passive.flags.valid = 0;
  436 + }
  437 + }
  438 + }
  439 + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
  440 + memset(&devices, 0, sizeof(struct acpi_handle_list));
  441 + status = acpi_evaluate_reference(tz->device->handle, "_PSL",
  442 + NULL, &devices);
403 443 if (ACPI_FAILURE(status))
404 444 tz->trips.passive.flags.valid = 0;
405   -
406   - status =
407   - acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
408   - &tz->trips.passive.tsp);
409   - if (ACPI_FAILURE(status))
410   - tz->trips.passive.flags.valid = 0;
411   -
412   - status =
413   - acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,
414   - &tz->trips.passive.devices);
415   - if (ACPI_FAILURE(status))
416   - tz->trips.passive.flags.valid = 0;
417   -
418   - if (!tz->trips.passive.flags.valid)
419   - printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
420 445 else
421   - ACPI_DEBUG_PRINT((ACPI_DB_INFO,
422   - "Found passive threshold [%lu]\n",
423   - tz->trips.passive.temperature));
  446 + tz->trips.passive.flags.valid = 1;
  447 +
  448 + if (memcmp(&tz->trips.passive.devices, &devices,
  449 + sizeof(struct acpi_handle_list))) {
  450 + memcpy(&tz->trips.passive.devices, &devices,
  451 + sizeof(struct acpi_handle_list));
  452 + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
  453 + }
424 454 }
  455 + if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
  456 + if (valid != tz->trips.passive.flags.valid)
  457 + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
  458 + }
425 459  
426   - /* Active: Fans, etc. (optional) */
427   -
  460 + /* Active (optional) */
428 461 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
429   -
430 462 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
  463 + valid = tz->trips.active[i].flags.valid;
431 464  
432 465 if (act == -1)
433   - break; /* disable all active trip points */
  466 + break; /* disable all active trip points */
434 467  
435   - status = acpi_evaluate_integer(tz->device->handle,
436   - name, NULL, &tz->trips.active[i].temperature);
437   -
438   - if (ACPI_FAILURE(status)) {
439   - if (i == 0) /* no active trip points */
  468 + if (flag & ACPI_TRIPS_ACTIVE) {
  469 + status = acpi_evaluate_integer(tz->device->handle,
  470 + name, NULL, &tz->trips.active[i].temperature);
  471 + if (ACPI_FAILURE(status)) {
  472 + tz->trips.active[i].flags.valid = 0;
  473 + if (i == 0)
  474 + break;
  475 + if (act <= 0)
  476 + break;
  477 + if (i == 1)
  478 + tz->trips.active[0].temperature =
  479 + CELSIUS_TO_KELVIN(act);
  480 + else
  481 + /*
  482 + * Don't allow override higher than
  483 + * the next higher trip point
  484 + */
  485 + tz->trips.active[i - 1].temperature =
  486 + (tz->trips.active[i - 2].temperature <
  487 + CELSIUS_TO_KELVIN(act) ?
  488 + tz->trips.active[i - 2].temperature :
  489 + CELSIUS_TO_KELVIN(act));
440 490 break;
441   - if (act <= 0) /* no override requested */
442   - break;
443   - if (i == 1) { /* 1 trip point */
444   - tz->trips.active[0].temperature =
445   - CELSIUS_TO_KELVIN(act);
446   - } else { /* multiple trips */
447   - /*
448   - * Don't allow override higher than
449   - * the next higher trip point
450   - */
451   - tz->trips.active[i - 1].temperature =
452   - (tz->trips.active[i - 2].temperature <
453   - CELSIUS_TO_KELVIN(act) ?
454   - tz->trips.active[i - 2].temperature :
455   - CELSIUS_TO_KELVIN(act));
456   - }
457   - break;
  491 + } else
  492 + tz->trips.active[i].flags.valid = 1;
458 493 }
459 494  
460 495 name[2] = 'L';
461   - status =
462   - acpi_evaluate_reference(tz->device->handle, name, NULL,
463   - &tz->trips.active[i].devices);
464   - if (ACPI_SUCCESS(status)) {
465   - tz->trips.active[i].flags.valid = 1;
466   - ACPI_DEBUG_PRINT((ACPI_DB_INFO,
467   - "Found active threshold [%d]:[%lu]\n",
468   - i, tz->trips.active[i].temperature));
469   - } else
470   - ACPI_EXCEPTION((AE_INFO, status,
471   - "Invalid active threshold [%d]", i));
  496 + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
  497 + memset(&devices, 0, sizeof(struct acpi_handle_list));
  498 + status = acpi_evaluate_reference(tz->device->handle,
  499 + name, NULL, &devices);
  500 + if (ACPI_FAILURE(status))
  501 + tz->trips.active[i].flags.valid = 0;
  502 + else
  503 + tz->trips.active[i].flags.valid = 1;
  504 +
  505 + if (memcmp(&tz->trips.active[i].devices, &devices,
  506 + sizeof(struct acpi_handle_list))) {
  507 + memcpy(&tz->trips.active[i].devices, &devices,
  508 + sizeof(struct acpi_handle_list));
  509 + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
  510 + }
  511 + }
  512 + if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
  513 + if (valid != tz->trips.active[i].flags.valid)
  514 + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
  515 +
  516 + if (!tz->trips.active[i].flags.valid)
  517 + break;
472 518 }
473 519  
  520 + if (flag & ACPI_TRIPS_DEVICES) {
  521 + memset(&devices, 0, sizeof(struct acpi_handle_list));
  522 + status = acpi_evaluate_reference(tz->device->handle, "_TZD",
  523 + NULL, &devices);
  524 + if (memcmp(&tz->devices, &devices,
  525 + sizeof(struct acpi_handle_list))) {
  526 + memcpy(&tz->devices, &devices,
  527 + sizeof(struct acpi_handle_list));
  528 + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
  529 + }
  530 + }
  531 +
474 532 return 0;
475 533 }
476 534  
477   -static int acpi_thermal_get_devices(struct acpi_thermal *tz)
  535 +static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
478 536 {
479   - acpi_status status = AE_OK;
480   -
481   -
482   - if (!tz)
483   - return -EINVAL;
484   -
485   - status =
486   - acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
487   - if (ACPI_FAILURE(status))
488   - return -ENODEV;
489   -
490   - return 0;
  537 + return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
491 538 }
492 539  
493 540 static int acpi_thermal_critical(struct acpi_thermal *tz)
... ... @@ -735,6 +782,9 @@
735 782 if (result)
736 783 goto unlock;
737 784  
  785 + if (!tz->tz_enabled)
  786 + goto unlock;
  787 +
738 788 memset(&tz->state, 0, sizeof(tz->state));
739 789  
740 790 /*
... ... @@ -828,6 +878,290 @@
828 878 mutex_unlock(&tz->lock);
829 879 }
830 880  
  881 +/* sys I/F for generic thermal sysfs support */
  882 +static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
  883 +{
  884 + struct acpi_thermal *tz = thermal->devdata;
  885 +
  886 + if (!tz)
  887 + return -EINVAL;
  888 +
  889 + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));
  890 +}
  891 +
  892 +static const char enabled[] = "kernel";
  893 +static const char disabled[] = "user";
  894 +static int thermal_get_mode(struct thermal_zone_device *thermal,
  895 + char *buf)
  896 +{
  897 + struct acpi_thermal *tz = thermal->devdata;
  898 +
  899 + if (!tz)
  900 + return -EINVAL;
  901 +
  902 + return sprintf(buf, "%s\n", tz->tz_enabled ?
  903 + enabled : disabled);
  904 +}
  905 +
  906 +static int thermal_set_mode(struct thermal_zone_device *thermal,
  907 + const char *buf)
  908 +{
  909 + struct acpi_thermal *tz = thermal->devdata;
  910 + int enable;
  911 +
  912 + if (!tz)
  913 + return -EINVAL;
  914 +
  915 + /*
  916 + * enable/disable thermal management from ACPI thermal driver
  917 + */
  918 + if (!strncmp(buf, enabled, sizeof enabled - 1))
  919 + enable = 1;
  920 + else if (!strncmp(buf, disabled, sizeof disabled - 1))
  921 + enable = 0;
  922 + else
  923 + return -EINVAL;
  924 +
  925 + if (enable != tz->tz_enabled) {
  926 + tz->tz_enabled = enable;
  927 + ACPI_DEBUG_PRINT((ACPI_DB_INFO,
  928 + "%s ACPI thermal control\n",
  929 + tz->tz_enabled ? enabled : disabled));
  930 + acpi_thermal_check(tz);
  931 + }
  932 + return 0;
  933 +}
  934 +
  935 +static int thermal_get_trip_type(struct thermal_zone_device *thermal,
  936 + int trip, char *buf)
  937 +{
  938 + struct acpi_thermal *tz = thermal->devdata;
  939 + int i;
  940 +
  941 + if (!tz || trip < 0)
  942 + return -EINVAL;
  943 +
  944 + if (tz->trips.critical.flags.valid) {
  945 + if (!trip)
  946 + return sprintf(buf, "critical\n");
  947 + trip--;
  948 + }
  949 +
  950 + if (tz->trips.hot.flags.valid) {
  951 + if (!trip)
  952 + return sprintf(buf, "hot\n");
  953 + trip--;
  954 + }
  955 +
  956 + if (tz->trips.passive.flags.valid) {
  957 + if (!trip)
  958 + return sprintf(buf, "passive\n");
  959 + trip--;
  960 + }
  961 +
  962 + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
  963 + tz->trips.active[i].flags.valid; i++) {
  964 + if (!trip)
  965 + return sprintf(buf, "active%d\n", i);
  966 + trip--;
  967 + }
  968 +
  969 + return -EINVAL;
  970 +}
  971 +
  972 +static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
  973 + int trip, char *buf)
  974 +{
  975 + struct acpi_thermal *tz = thermal->devdata;
  976 + int i;
  977 +
  978 + if (!tz || trip < 0)
  979 + return -EINVAL;
  980 +
  981 + if (tz->trips.critical.flags.valid) {
  982 + if (!trip)
  983 + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
  984 + tz->trips.critical.temperature));
  985 + trip--;
  986 + }
  987 +
  988 + if (tz->trips.hot.flags.valid) {
  989 + if (!trip)
  990 + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
  991 + tz->trips.hot.temperature));
  992 + trip--;
  993 + }
  994 +
  995 + if (tz->trips.passive.flags.valid) {
  996 + if (!trip)
  997 + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
  998 + tz->trips.passive.temperature));
  999 + trip--;
  1000 + }
  1001 +
  1002 + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
  1003 + tz->trips.active[i].flags.valid; i++) {
  1004 + if (!trip)
  1005 + return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
  1006 + tz->trips.active[i].temperature));
  1007 + trip--;
  1008 + }
  1009 +
  1010 + return -EINVAL;
  1011 +}
  1012 +
  1013 +typedef int (*cb)(struct thermal_zone_device *, int,
  1014 + struct thermal_cooling_device *);
  1015 +static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
  1016 + struct thermal_cooling_device *cdev,
  1017 + cb action)
  1018 +{
  1019 + struct acpi_device *device = cdev->devdata;
  1020 + struct acpi_thermal *tz = thermal->devdata;
  1021 + struct acpi_device *dev;
  1022 + acpi_status status;
  1023 + acpi_handle handle;
  1024 + int i;
  1025 + int j;
  1026 + int trip = -1;
  1027 + int result = 0;
  1028 +
  1029 + if (tz->trips.critical.flags.valid)
  1030 + trip++;
  1031 +
  1032 + if (tz->trips.hot.flags.valid)
  1033 + trip++;
  1034 +
  1035 + if (tz->trips.passive.flags.valid) {
  1036 + trip++;
  1037 + for (i = 0; i < tz->trips.passive.devices.count;
  1038 + i++) {
  1039 + handle = tz->trips.passive.devices.handles[i];
  1040 + status = acpi_bus_get_device(handle, &dev);
  1041 + if (ACPI_SUCCESS(status) && (dev == device)) {
  1042 + result = action(thermal, trip, cdev);
  1043 + if (result)
  1044 + goto failed;
  1045 + }
  1046 + }
  1047 + }
  1048 +
  1049 + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
  1050 + if (!tz->trips.active[i].flags.valid)
  1051 + break;
  1052 + trip++;
  1053 + for (j = 0;
  1054 + j < tz->trips.active[i].devices.count;
  1055 + j++) {
  1056 + handle = tz->trips.active[i].devices.handles[j];
  1057 + status = acpi_bus_get_device(handle, &dev);
  1058 + if (ACPI_SUCCESS(status) && (dev == device)) {
  1059 + result = action(thermal, trip, cdev);
  1060 + if (result)
  1061 + goto failed;
  1062 + }
  1063 + }
  1064 + }
  1065 +
  1066 + for (i = 0; i < tz->devices.count; i++) {
  1067 + handle = tz->devices.handles[i];
  1068 + status = acpi_bus_get_device(handle, &dev);
  1069 + if (ACPI_SUCCESS(status) && (dev == device)) {
  1070 + result = action(thermal, -1, cdev);
  1071 + if (result)
  1072 + goto failed;
  1073 + }
  1074 + }
  1075 +
  1076 +failed:
  1077 + return result;
  1078 +}
  1079 +
  1080 +static int
  1081 +acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
  1082 + struct thermal_cooling_device *cdev)
  1083 +{
  1084 + return acpi_thermal_cooling_device_cb(thermal, cdev,
  1085 + thermal_zone_bind_cooling_device);
  1086 +}
  1087 +
  1088 +static int
  1089 +acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
  1090 + struct thermal_cooling_device *cdev)
  1091 +{
  1092 + return acpi_thermal_cooling_device_cb(thermal, cdev,
  1093 + thermal_zone_unbind_cooling_device);
  1094 +}
  1095 +
  1096 +static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
  1097 + .bind = acpi_thermal_bind_cooling_device,
  1098 + .unbind = acpi_thermal_unbind_cooling_device,
  1099 + .get_temp = thermal_get_temp,
  1100 + .get_mode = thermal_get_mode,
  1101 + .set_mode = thermal_set_mode,
  1102 + .get_trip_type = thermal_get_trip_type,
  1103 + .get_trip_temp = thermal_get_trip_temp,
  1104 +};
  1105 +
  1106 +static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
  1107 +{
  1108 + int trips = 0;
  1109 + int result;
  1110 + acpi_status status;
  1111 + int i;
  1112 +
  1113 + if (tz->trips.critical.flags.valid)
  1114 + trips++;
  1115 +
  1116 + if (tz->trips.hot.flags.valid)
  1117 + trips++;
  1118 +
  1119 + if (tz->trips.passive.flags.valid)
  1120 + trips++;
  1121 +
  1122 + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
  1123 + tz->trips.active[i].flags.valid; i++, trips++);
  1124 + tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
  1125 + trips, tz, &acpi_thermal_zone_ops);
  1126 + if (!tz->thermal_zone)
  1127 + return -ENODEV;
  1128 +
  1129 + result = sysfs_create_link(&tz->device->dev.kobj,
  1130 + &tz->thermal_zone->device.kobj, "thermal_zone");
  1131 + if (result)
  1132 + return result;
  1133 +
  1134 + result = sysfs_create_link(&tz->thermal_zone->device.kobj,
  1135 + &tz->device->dev.kobj, "device");
  1136 + if (result)
  1137 + return result;
  1138 +
  1139 + status = acpi_attach_data(tz->device->handle,
  1140 + acpi_bus_private_data_handler,
  1141 + tz->thermal_zone);
  1142 + if (ACPI_FAILURE(status)) {
  1143 + ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  1144 + "Error attaching device data\n"));
  1145 + return -ENODEV;
  1146 + }
  1147 +
  1148 + tz->tz_enabled = 1;
  1149 +
  1150 + printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
  1151 + tz->device->dev.bus_id, tz->thermal_zone->id);
  1152 + return 0;
  1153 +}
  1154 +
  1155 +static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
  1156 +{
  1157 + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
  1158 + sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
  1159 + thermal_zone_device_unregister(tz->thermal_zone);
  1160 + tz->thermal_zone = NULL;
  1161 + acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
  1162 +}
  1163 +
  1164 +
831 1165 /* --------------------------------------------------------------------------
832 1166 FS Interface (/proc)
833 1167 -------------------------------------------------------------------------- */
834 1168  
... ... @@ -1184,15 +1518,15 @@
1184 1518 acpi_thermal_check(tz);
1185 1519 break;
1186 1520 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
1187   - acpi_thermal_get_trip_points(tz);
  1521 + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
1188 1522 acpi_thermal_check(tz);
1189 1523 acpi_bus_generate_proc_event(device, event, 0);
1190 1524 acpi_bus_generate_netlink_event(device->pnp.device_class,
1191 1525 device->dev.bus_id, event, 0);
1192 1526 break;
1193 1527 case ACPI_THERMAL_NOTIFY_DEVICES:
1194   - if (tz->flags.devices)
1195   - acpi_thermal_get_devices(tz);
  1528 + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
  1529 + acpi_thermal_check(tz);
1196 1530 acpi_bus_generate_proc_event(device, event, 0);
1197 1531 acpi_bus_generate_netlink_event(device->pnp.device_class,
1198 1532 device->dev.bus_id, event, 0);
... ... @@ -1235,11 +1569,6 @@
1235 1569 else
1236 1570 acpi_thermal_get_polling_frequency(tz);
1237 1571  
1238   - /* Get devices in this thermal zone [_TZD] (optional) */
1239   - result = acpi_thermal_get_devices(tz);
1240   - if (!result)
1241   - tz->flags.devices = 1;
1242   -
1243 1572 return 0;
1244 1573 }
1245 1574  
1246 1575  
1247 1576  
1248 1577  
... ... @@ -1263,13 +1592,19 @@
1263 1592 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
1264 1593 acpi_driver_data(device) = tz;
1265 1594 mutex_init(&tz->lock);
  1595 +
  1596 +
1266 1597 result = acpi_thermal_get_info(tz);
1267 1598 if (result)
1268   - goto end;
  1599 + goto free_memory;
1269 1600  
  1601 + result = acpi_thermal_register_thermal_zone(tz);
  1602 + if (result)
  1603 + goto free_memory;
  1604 +
1270 1605 result = acpi_thermal_add_fs(device);
1271 1606 if (result)
1272   - goto end;
  1607 + goto unregister_thermal_zone;
1273 1608  
1274 1609 init_timer(&tz->timer);
1275 1610  
1276 1611  
1277 1612  
... ... @@ -1280,19 +1615,21 @@
1280 1615 acpi_thermal_notify, tz);
1281 1616 if (ACPI_FAILURE(status)) {
1282 1617 result = -ENODEV;
1283   - goto end;
  1618 + goto remove_fs;
1284 1619 }
1285 1620  
1286 1621 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
1287 1622 acpi_device_name(device), acpi_device_bid(device),
1288 1623 KELVIN_TO_CELSIUS(tz->temperature));
  1624 + goto end;
1289 1625  
1290   - end:
1291   - if (result) {
1292   - acpi_thermal_remove_fs(device);
1293   - kfree(tz);
1294   - }
1295   -
  1626 +remove_fs:
  1627 + acpi_thermal_remove_fs(device);
  1628 +unregister_thermal_zone:
  1629 + thermal_zone_device_unregister(tz->thermal_zone);
  1630 +free_memory:
  1631 + kfree(tz);
  1632 +end:
1296 1633 return result;
1297 1634 }
1298 1635  
... ... @@ -1332,6 +1669,7 @@
1332 1669 }
1333 1670  
1334 1671 acpi_thermal_remove_fs(device);
  1672 + acpi_thermal_unregister_thermal_zone(tz);
1335 1673 mutex_destroy(&tz->lock);
1336 1674 kfree(tz);
1337 1675 return 0;
drivers/acpi/video.c
... ... @@ -34,6 +34,7 @@
34 34 #include <linux/seq_file.h>
35 35 #include <linux/input.h>
36 36 #include <linux/backlight.h>
  37 +#include <linux/thermal.h>
37 38 #include <linux/video_output.h>
38 39 #include <asm/uaccess.h>
39 40  
... ... @@ -179,6 +180,7 @@
179 180 struct acpi_device *dev;
180 181 struct acpi_video_device_brightness *brightness;
181 182 struct backlight_device *backlight;
  183 + struct thermal_cooling_device *cdev;
182 184 struct output_device *output_dev;
183 185 };
184 186  
... ... @@ -342,6 +344,54 @@
342 344 .set_state = acpi_video_output_set,
343 345 .get_status = acpi_video_output_get,
344 346 };
  347 +
  348 +
  349 +/* thermal cooling device callbacks */
  350 +static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
  351 +{
  352 + struct acpi_device *device = cdev->devdata;
  353 + struct acpi_video_device *video = acpi_driver_data(device);
  354 +
  355 + return sprintf(buf, "%d\n", video->brightness->count - 3);
  356 +}
  357 +
  358 +static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
  359 +{
  360 + struct acpi_device *device = cdev->devdata;
  361 + struct acpi_video_device *video = acpi_driver_data(device);
  362 + unsigned long level;
  363 + int state;
  364 +
  365 + acpi_video_device_lcd_get_level_current(video, &level);
  366 + for (state = 2; state < video->brightness->count; state++)
  367 + if (level == video->brightness->levels[state])
  368 + return sprintf(buf, "%d\n",
  369 + video->brightness->count - state - 1);
  370 +
  371 + return -EINVAL;
  372 +}
  373 +
  374 +static int
  375 +video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
  376 +{
  377 + struct acpi_device *device = cdev->devdata;
  378 + struct acpi_video_device *video = acpi_driver_data(device);
  379 + int level;
  380 +
  381 + if ( state >= video->brightness->count - 2)
  382 + return -EINVAL;
  383 +
  384 + state = video->brightness->count - state;
  385 + level = video->brightness->levels[state -1];
  386 + return acpi_video_device_lcd_set_level(video, level);
  387 +}
  388 +
  389 +static struct thermal_cooling_device_ops video_cooling_ops = {
  390 + .get_max_state = video_get_max_state,
  391 + .get_cur_state = video_get_cur_state,
  392 + .set_cur_state = video_set_cur_state,
  393 +};
  394 +
345 395 /* --------------------------------------------------------------------------
346 396 Video Management
347 397 -------------------------------------------------------------------------- */
... ... @@ -660,6 +710,7 @@
660 710 kfree(obj);
661 711  
662 712 if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
  713 + int result;
663 714 static int count = 0;
664 715 char *name;
665 716 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
666 717  
... ... @@ -672,8 +723,25 @@
672 723 device->backlight->props.max_brightness = device->brightness->count-3;
673 724 device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);
674 725 backlight_update_status(device->backlight);
675   -
676 726 kfree(name);
  727 +
  728 + device->cdev = thermal_cooling_device_register("LCD",
  729 + device->dev, &video_cooling_ops);
  730 + if (device->cdev) {
  731 + printk(KERN_INFO PREFIX
  732 + "%s is registered as cooling_device%d\n",
  733 + device->dev->dev.bus_id, device->cdev->id);
  734 + result = sysfs_create_link(&device->dev->dev.kobj,
  735 + &device->cdev->device.kobj,
  736 + "thermal_cooling");
  737 + if (result)
  738 + printk(KERN_ERR PREFIX "Create sysfs link\n");
  739 + result = sysfs_create_link(&device->cdev->device.kobj,
  740 + &device->dev->dev.kobj,
  741 + "device");
  742 + if (result)
  743 + printk(KERN_ERR PREFIX "Create sysfs link\n");
  744 + }
677 745 }
678 746 if (device->cap._DCS && device->cap._DSS){
679 747 static int count = 0;
... ... @@ -1764,6 +1832,14 @@
1764 1832 ACPI_DEVICE_NOTIFY,
1765 1833 acpi_video_device_notify);
1766 1834 backlight_device_unregister(device->backlight);
  1835 + if (device->cdev) {
  1836 + sysfs_remove_link(&device->dev->dev.kobj,
  1837 + "thermal_cooling");
  1838 + sysfs_remove_link(&device->cdev->device.kobj,
  1839 + "device");
  1840 + thermal_cooling_device_unregister(device->cdev);
  1841 + device->cdev = NULL;
  1842 + }
1767 1843 video_output_unregister(device->output_dev);
1768 1844  
1769 1845 return 0;
drivers/misc/Kconfig
... ... @@ -251,5 +251,14 @@
251 251  
252 252 If unsure, say N.
253 253  
  254 +config INTEL_MENLOW
  255 + tristate "Thermal Management driver for Intel menlow platform"
  256 + depends on ACPI_THERMAL
  257 + ---help---
  258 + ACPI thermal management enhancement driver on
  259 + Intel Menlow platform.
  260 +
  261 + If unsure, say N.
  262 +
254 263 endif # MISC_DEVICES
drivers/misc/Makefile
... ... @@ -17,4 +17,5 @@
17 17 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
18 18 obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
19 19 obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
  20 +obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
drivers/misc/intel_menlow.c
  1 +/*
  2 + * intel_menlow.c - Intel menlow Driver for thermal management extension
  3 + *
  4 + * Copyright (C) 2008 Intel Corp
  5 + * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
  6 + * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
  7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License as published by
  11 + * the Free Software Foundation; version 2 of the License.
  12 + *
  13 + * This program is distributed in the hope that it will be useful, but
  14 + * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 + * General Public License for more details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License along
  19 + * with this program; if not, write to the Free Software Foundation, Inc.,
  20 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21 + *
  22 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23 + *
  24 + * This driver creates the sys I/F for programming the sensors.
  25 + * It also implements the driver for intel menlow memory controller (hardware
  26 + * id is INT0002) which makes use of the platform specific ACPI methods
  27 + * to get/set bandwidth.
  28 + */
  29 +
  30 +#include <linux/kernel.h>
  31 +#include <linux/module.h>
  32 +#include <linux/init.h>
  33 +#include <linux/types.h>
  34 +#include <linux/pci.h>
  35 +#include <linux/pm.h>
  36 +
  37 +#include <linux/thermal.h>
  38 +#include <acpi/acpi_bus.h>
  39 +#include <acpi/acpi_drivers.h>
  40 +
  41 +MODULE_AUTHOR("Thomas Sujith");
  42 +MODULE_AUTHOR("Zhang Rui");
  43 +MODULE_DESCRIPTION("Intel Menlow platform specific driver");
  44 +MODULE_LICENSE("GPL");
  45 +
  46 +/*
  47 + * Memory controller device control
  48 + */
  49 +
  50 +#define MEMORY_GET_BANDWIDTH "GTHS"
  51 +#define MEMORY_SET_BANDWIDTH "STHS"
  52 +#define MEMORY_ARG_CUR_BANDWIDTH 1
  53 +#define MEMORY_ARG_MAX_BANDWIDTH 0
  54 +
  55 +static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
  56 + unsigned long *max_state)
  57 +{
  58 + struct acpi_device *device = cdev->devdata;
  59 + acpi_handle handle = device->handle;
  60 + unsigned long value;
  61 + struct acpi_object_list arg_list;
  62 + union acpi_object arg;
  63 + acpi_status status = AE_OK;
  64 +
  65 + arg_list.count = 1;
  66 + arg_list.pointer = &arg;
  67 + arg.type = ACPI_TYPE_INTEGER;
  68 + arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
  69 + status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
  70 + &arg_list, &value);
  71 + if (ACPI_FAILURE(status))
  72 + return -EFAULT;
  73 +
  74 + *max_state = value - 1;
  75 + return 0;
  76 +}
  77 +
  78 +static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
  79 + char *buf)
  80 +{
  81 + unsigned long value;
  82 + if (memory_get_int_max_bandwidth(cdev, &value))
  83 + return -EINVAL;
  84 +
  85 + return sprintf(buf, "%ld\n", value);
  86 +}
  87 +
  88 +static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
  89 + char *buf)
  90 +{
  91 + struct acpi_device *device = cdev->devdata;
  92 + acpi_handle handle = device->handle;
  93 + unsigned long value;
  94 + struct acpi_object_list arg_list;
  95 + union acpi_object arg;
  96 + acpi_status status = AE_OK;
  97 +
  98 + arg_list.count = 1;
  99 + arg_list.pointer = &arg;
  100 + arg.type = ACPI_TYPE_INTEGER;
  101 + arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
  102 + status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
  103 + &arg_list, &value);
  104 + if (ACPI_FAILURE(status))
  105 + return -EFAULT;
  106 +
  107 + return sprintf(buf, "%ld\n", value);
  108 +}
  109 +
  110 +static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
  111 + unsigned int state)
  112 +{
  113 + struct acpi_device *device = cdev->devdata;
  114 + acpi_handle handle = device->handle;
  115 + struct acpi_object_list arg_list;
  116 + union acpi_object arg;
  117 + acpi_status status;
  118 + int temp;
  119 + unsigned long max_state;
  120 +
  121 + if (memory_get_int_max_bandwidth(cdev, &max_state))
  122 + return -EFAULT;
  123 +
  124 + if (max_state < 0 || state > max_state)
  125 + return -EINVAL;
  126 +
  127 + arg_list.count = 1;
  128 + arg_list.pointer = &arg;
  129 + arg.type = ACPI_TYPE_INTEGER;
  130 + arg.integer.value = state;
  131 +
  132 + status =
  133 + acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
  134 + (unsigned long *)&temp);
  135 +
  136 + printk(KERN_INFO
  137 + "Bandwidth value was %d: status is %d\n", state, status);
  138 + if (ACPI_FAILURE(status))
  139 + return -EFAULT;
  140 +
  141 + return 0;
  142 +}
  143 +
  144 +static struct thermal_cooling_device_ops memory_cooling_ops = {
  145 + .get_max_state = memory_get_max_bandwidth,
  146 + .get_cur_state = memory_get_cur_bandwidth,
  147 + .set_cur_state = memory_set_cur_bandwidth,
  148 +};
  149 +
  150 +/*
  151 + * Memory Device Management
  152 + */
  153 +static int intel_menlow_memory_add(struct acpi_device *device)
  154 +{
  155 + int result = -ENODEV;
  156 + acpi_status status = AE_OK;
  157 + acpi_handle dummy;
  158 + struct thermal_cooling_device *cdev;
  159 +
  160 + if (!device)
  161 + return -EINVAL;
  162 +
  163 + status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
  164 + if (ACPI_FAILURE(status))
  165 + goto end;
  166 +
  167 + status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
  168 + if (ACPI_FAILURE(status))
  169 + goto end;
  170 +
  171 + cdev = thermal_cooling_device_register("Memory controller", device,
  172 + &memory_cooling_ops);
  173 + acpi_driver_data(device) = cdev;
  174 + if (!cdev)
  175 + result = -ENODEV;
  176 + else {
  177 + result = sysfs_create_link(&device->dev.kobj,
  178 + &cdev->device.kobj, "thermal_cooling");
  179 + if (result)
  180 + goto unregister;
  181 +
  182 + result = sysfs_create_link(&cdev->device.kobj,
  183 + &device->dev.kobj, "device");
  184 + if (result) {
  185 + sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
  186 + goto unregister;
  187 + }
  188 + }
  189 +
  190 + end:
  191 + return result;
  192 +
  193 + unregister:
  194 + thermal_cooling_device_unregister(cdev);
  195 + return result;
  196 +
  197 +}
  198 +
  199 +static int intel_menlow_memory_remove(struct acpi_device *device, int type)
  200 +{
  201 + struct thermal_cooling_device *cdev = acpi_driver_data(device);
  202 +
  203 + if (!device || !cdev)
  204 + return -EINVAL;
  205 +
  206 + sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
  207 + sysfs_remove_link(&cdev->device.kobj, "device");
  208 + thermal_cooling_device_unregister(cdev);
  209 +
  210 + return 0;
  211 +}
  212 +
  213 +const static struct acpi_device_id intel_menlow_memory_ids[] = {
  214 + {"INT0002", 0},
  215 + {"", 0},
  216 +};
  217 +
  218 +static struct acpi_driver intel_menlow_memory_driver = {
  219 + .name = "intel_menlow_thermal_control",
  220 + .ids = intel_menlow_memory_ids,
  221 + .ops = {
  222 + .add = intel_menlow_memory_add,
  223 + .remove = intel_menlow_memory_remove,
  224 + },
  225 +};
  226 +
  227 +/*
  228 + * Sensor control on menlow platform
  229 + */
  230 +
  231 +#define THERMAL_AUX0 0
  232 +#define THERMAL_AUX1 1
  233 +#define GET_AUX0 "GAX0"
  234 +#define GET_AUX1 "GAX1"
  235 +#define SET_AUX0 "SAX0"
  236 +#define SET_AUX1 "SAX1"
  237 +
  238 +struct intel_menlow_attribute {
  239 + struct device_attribute attr;
  240 + struct device *device;
  241 + acpi_handle handle;
  242 + struct list_head node;
  243 +};
  244 +
  245 +static LIST_HEAD(intel_menlow_attr_list);
  246 +static DEFINE_MUTEX(intel_menlow_attr_lock);
  247 +
  248 +/*
  249 + * sensor_get_auxtrip - get the current auxtrip value from sensor
  250 + * @name: Thermalzone name
  251 + * @auxtype : AUX0/AUX1
  252 + * @buf: syfs buffer
  253 + */
  254 +static int sensor_get_auxtrip(acpi_handle handle, int index, int *value)
  255 +{
  256 + acpi_status status;
  257 +
  258 + if ((index != 0 && index != 1) || !value)
  259 + return -EINVAL;
  260 +
  261 + status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
  262 + NULL, (unsigned long *)value);
  263 + if (ACPI_FAILURE(status))
  264 + return -EIO;
  265 +
  266 + return 0;
  267 +}
  268 +
  269 +/*
  270 + * sensor_set_auxtrip - set the new auxtrip value to sensor
  271 + * @name: Thermalzone name
  272 + * @auxtype : AUX0/AUX1
  273 + * @buf: syfs buffer
  274 + */
  275 +static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
  276 +{
  277 + acpi_status status;
  278 + union acpi_object arg = {
  279 + ACPI_TYPE_INTEGER
  280 + };
  281 + struct acpi_object_list args = {
  282 + 1, &arg
  283 + };
  284 + int temp;
  285 +
  286 + if (index != 0 && index != 1)
  287 + return -EINVAL;
  288 +
  289 + status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
  290 + NULL, (unsigned long *)&temp);
  291 + if (ACPI_FAILURE(status))
  292 + return -EIO;
  293 + if ((index && value < temp) || (!index && value > temp))
  294 + return -EINVAL;
  295 +
  296 + arg.integer.value = value;
  297 + status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
  298 + &args, (unsigned long *)&temp);
  299 + if (ACPI_FAILURE(status))
  300 + return -EIO;
  301 +
  302 + /* do we need to check the return value of SAX0/SAX1 ? */
  303 +
  304 + return 0;
  305 +}
  306 +
  307 +#define to_intel_menlow_attr(_attr) \
  308 + container_of(_attr, struct intel_menlow_attribute, attr)
  309 +
  310 +static ssize_t aux0_show(struct device *dev,
  311 + struct device_attribute *dev_attr, char *buf)
  312 +{
  313 + struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
  314 + int value;
  315 + int result;
  316 +
  317 + result = sensor_get_auxtrip(attr->handle, 0, &value);
  318 +
  319 + return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
  320 +}
  321 +
  322 +static ssize_t aux1_show(struct device *dev,
  323 + struct device_attribute *dev_attr, char *buf)
  324 +{
  325 + struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
  326 + int value;
  327 + int result;
  328 +
  329 + result = sensor_get_auxtrip(attr->handle, 1, &value);
  330 +
  331 + return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
  332 +}
  333 +
  334 +static ssize_t aux0_store(struct device *dev,
  335 + struct device_attribute *dev_attr,
  336 + const char *buf, size_t count)
  337 +{
  338 + struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
  339 + int value;
  340 + int result;
  341 +
  342 + /*Sanity check; should be a positive integer */
  343 + if (!sscanf(buf, "%d", &value))
  344 + return -EINVAL;
  345 +
  346 + if (value < 0)
  347 + return -EINVAL;
  348 +
  349 + result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
  350 + return result ? result : count;
  351 +}
  352 +
  353 +static ssize_t aux1_store(struct device *dev,
  354 + struct device_attribute *dev_attr,
  355 + const char *buf, size_t count)
  356 +{
  357 + struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
  358 + int value;
  359 + int result;
  360 +
  361 + /*Sanity check; should be a positive integer */
  362 + if (!sscanf(buf, "%d", &value))
  363 + return -EINVAL;
  364 +
  365 + if (value < 0)
  366 + return -EINVAL;
  367 +
  368 + result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
  369 + return result ? result : count;
  370 +}
  371 +
  372 +/* BIOS can enable/disable the thermal user application in dabney platform */
  373 +#define BIOS_ENABLED "\\_TZ.GSTS"
  374 +static ssize_t bios_enabled_show(struct device *dev,
  375 + struct device_attribute *attr, char *buf)
  376 +{
  377 + acpi_status status;
  378 + unsigned long bios_enabled;
  379 +
  380 + status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
  381 + if (ACPI_FAILURE(status))
  382 + return -ENODEV;
  383 +
  384 + return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
  385 +}
  386 +
  387 +static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
  388 + void *store, struct device *dev,
  389 + acpi_handle handle)
  390 +{
  391 + struct intel_menlow_attribute *attr;
  392 + int result;
  393 +
  394 + attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
  395 + if (!attr)
  396 + return -ENOMEM;
  397 +
  398 + attr->attr.attr.name = name;
  399 + attr->attr.attr.mode = mode;
  400 + attr->attr.show = show;
  401 + attr->attr.store = store;
  402 + attr->device = dev;
  403 + attr->handle = handle;
  404 +
  405 + result = device_create_file(dev, &attr->attr);
  406 + if (result)
  407 + return result;
  408 +
  409 + mutex_lock(&intel_menlow_attr_lock);
  410 + list_add_tail(&attr->node, &intel_menlow_attr_list);
  411 + mutex_unlock(&intel_menlow_attr_lock);
  412 +
  413 + return 0;
  414 +}
  415 +
  416 +static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
  417 + void *context, void **rv)
  418 +{
  419 + acpi_status status;
  420 + acpi_handle dummy;
  421 + struct thermal_zone_device *thermal;
  422 + int result;
  423 +
  424 + result = acpi_bus_get_private_data(handle, (void **)&thermal);
  425 + if (result)
  426 + return 0;
  427 +
  428 + /* _TZ must have the AUX0/1 methods */
  429 + status = acpi_get_handle(handle, GET_AUX0, &dummy);
  430 + if (ACPI_FAILURE(status))
  431 + goto not_found;
  432 +
  433 + status = acpi_get_handle(handle, SET_AUX0, &dummy);
  434 + if (ACPI_FAILURE(status))
  435 + goto not_found;
  436 +
  437 + result = intel_menlow_add_one_attribute("aux0", 0644,
  438 + aux0_show, aux0_store,
  439 + &thermal->device, handle);
  440 + if (result)
  441 + return AE_ERROR;
  442 +
  443 + status = acpi_get_handle(handle, GET_AUX1, &dummy);
  444 + if (ACPI_FAILURE(status))
  445 + goto not_found;
  446 +
  447 + status = acpi_get_handle(handle, SET_AUX1, &dummy);
  448 + if (ACPI_FAILURE(status))
  449 + goto not_found;
  450 +
  451 + result = intel_menlow_add_one_attribute("aux1", 0644,
  452 + aux1_show, aux1_store,
  453 + &thermal->device, handle);
  454 + if (result)
  455 + return AE_ERROR;
  456 +
  457 + /*
  458 + * create the "dabney_enabled" attribute which means the user app
  459 + * should be loaded or not
  460 + */
  461 +
  462 + result = intel_menlow_add_one_attribute("bios_enabled", 0444,
  463 + bios_enabled_show, NULL,
  464 + &thermal->device, handle);
  465 + if (result)
  466 + return AE_ERROR;
  467 +
  468 + not_found:
  469 + if (status == AE_NOT_FOUND)
  470 + return AE_OK;
  471 + else
  472 + return status;
  473 +}
  474 +
  475 +static void intel_menlow_unregister_sensor(void)
  476 +{
  477 + struct intel_menlow_attribute *pos, *next;
  478 +
  479 + mutex_lock(&intel_menlow_attr_lock);
  480 + list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
  481 + list_del(&pos->node);
  482 + device_remove_file(pos->device, &pos->attr);
  483 + kfree(pos);
  484 + }
  485 + mutex_unlock(&intel_menlow_attr_lock);
  486 +
  487 + return;
  488 +}
  489 +
  490 +static int __init intel_menlow_module_init(void)
  491 +{
  492 + int result = -ENODEV;
  493 + acpi_status status;
  494 + unsigned long enable;
  495 +
  496 + if (acpi_disabled)
  497 + return result;
  498 +
  499 + /* Looking for the \_TZ.GSTS method */
  500 + status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
  501 + if (ACPI_FAILURE(status) || !enable)
  502 + return -ENODEV;
  503 +
  504 + /* Looking for ACPI device MEM0 with hardware id INT0002 */
  505 + result = acpi_bus_register_driver(&intel_menlow_memory_driver);
  506 + if (result)
  507 + return result;
  508 +
  509 + /* Looking for sensors in each ACPI thermal zone */
  510 + status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
  511 + ACPI_UINT32_MAX,
  512 + intel_menlow_register_sensor, NULL, NULL);
  513 + if (ACPI_FAILURE(status))
  514 + return -ENODEV;
  515 +
  516 + return 0;
  517 +}
  518 +
  519 +static void __exit intel_menlow_module_exit(void)
  520 +{
  521 + acpi_bus_unregister_driver(&intel_menlow_memory_driver);
  522 + intel_menlow_unregister_sensor();
  523 +}
  524 +
  525 +module_init(intel_menlow_module_init);
  526 +module_exit(intel_menlow_module_exit);
drivers/thermal/Kconfig
  1 +#
  2 +# Generic thermal sysfs drivers configuration
  3 +#
  4 +
  5 +menuconfig THERMAL
  6 + bool "Generic Thermal sysfs driver"
  7 + default y
  8 + help
  9 + Generic Thermal Sysfs driver offers a generic mechanism for
  10 + thermal management. Usually it's made up of one or more thermal
  11 + zone and cooling device.
  12 + each thermal zone contains its own temperature, trip points,
  13 + cooling devices.
  14 + All platforms with ACPI thermal support can use this driver.
  15 + If you want this support, you should say Y here
drivers/thermal/Makefile
  1 +#
  2 +# Makefile for sensor chip drivers.
  3 +#
  4 +
  5 +obj-$(CONFIG_THERMAL) += thermal.o
drivers/thermal/thermal.c
  1 +/*
  2 + * thermal.c - Generic Thermal Management Sysfs support.
  3 + *
  4 + * Copyright (C) 2008 Intel Corp
  5 + * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
  6 + * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
  7 + *
  8 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9 + *
  10 + * This program is free software; you can redistribute it and/or modify
  11 + * it under the terms of the GNU General Public License as published by
  12 + * the Free Software Foundation; version 2 of the License.
  13 + *
  14 + * This program is distributed in the hope that it will be useful, but
  15 + * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17 + * General Public License for more details.
  18 + *
  19 + * You should have received a copy of the GNU General Public License along
  20 + * with this program; if not, write to the Free Software Foundation, Inc.,
  21 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  22 + *
  23 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24 + */
  25 +
  26 +#include <linux/module.h>
  27 +#include <linux/device.h>
  28 +#include <linux/err.h>
  29 +#include <linux/kdev_t.h>
  30 +#include <linux/idr.h>
  31 +#include <linux/thermal.h>
  32 +#include <linux/spinlock.h>
  33 +
  34 +MODULE_AUTHOR("Zhang Rui")
  35 +MODULE_DESCRIPTION("Generic thermal management sysfs support");
  36 +MODULE_LICENSE("GPL");
  37 +
  38 +#define PREFIX "Thermal: "
  39 +
  40 +struct thermal_cooling_device_instance {
  41 + int id;
  42 + char name[THERMAL_NAME_LENGTH];
  43 + struct thermal_zone_device *tz;
  44 + struct thermal_cooling_device *cdev;
  45 + int trip;
  46 + char attr_name[THERMAL_NAME_LENGTH];
  47 + struct device_attribute attr;
  48 + struct list_head node;
  49 +};
  50 +
  51 +static DEFINE_IDR(thermal_tz_idr);
  52 +static DEFINE_IDR(thermal_cdev_idr);
  53 +static DEFINE_MUTEX(thermal_idr_lock);
  54 +
  55 +static LIST_HEAD(thermal_tz_list);
  56 +static LIST_HEAD(thermal_cdev_list);
  57 +static DEFINE_MUTEX(thermal_list_lock);
  58 +
  59 +static int get_idr(struct idr *idr, struct mutex *lock, int *id)
  60 +{
  61 + int err;
  62 +
  63 + again:
  64 + if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
  65 + return -ENOMEM;
  66 +
  67 + if (lock)
  68 + mutex_lock(lock);
  69 + err = idr_get_new(idr, NULL, id);
  70 + if (lock)
  71 + mutex_unlock(lock);
  72 + if (unlikely(err == -EAGAIN))
  73 + goto again;
  74 + else if (unlikely(err))
  75 + return err;
  76 +
  77 + *id = *id & MAX_ID_MASK;
  78 + return 0;
  79 +}
  80 +
  81 +static void release_idr(struct idr *idr, struct mutex *lock, int id)
  82 +{
  83 + if (lock)
  84 + mutex_lock(lock);
  85 + idr_remove(idr, id);
  86 + if (lock)
  87 + mutex_unlock(lock);
  88 +}
  89 +
  90 +/* sys I/F for thermal zone */
  91 +
  92 +#define to_thermal_zone(_dev) \
  93 + container_of(_dev, struct thermal_zone_device, device)
  94 +
  95 +static ssize_t
  96 +type_show(struct device *dev, struct device_attribute *attr, char *buf)
  97 +{
  98 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  99 +
  100 + return sprintf(buf, "%s\n", tz->type);
  101 +}
  102 +
  103 +static ssize_t
  104 +temp_show(struct device *dev, struct device_attribute *attr, char *buf)
  105 +{
  106 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  107 +
  108 + if (!tz->ops->get_temp)
  109 + return -EPERM;
  110 +
  111 + return tz->ops->get_temp(tz, buf);
  112 +}
  113 +
  114 +static ssize_t
  115 +mode_show(struct device *dev, struct device_attribute *attr, char *buf)
  116 +{
  117 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  118 +
  119 + if (!tz->ops->get_mode)
  120 + return -EPERM;
  121 +
  122 + return tz->ops->get_mode(tz, buf);
  123 +}
  124 +
  125 +static ssize_t
  126 +mode_store(struct device *dev, struct device_attribute *attr,
  127 + const char *buf, size_t count)
  128 +{
  129 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  130 + int result;
  131 +
  132 + if (!tz->ops->set_mode)
  133 + return -EPERM;
  134 +
  135 + result = tz->ops->set_mode(tz, buf);
  136 + if (result)
  137 + return result;
  138 +
  139 + return count;
  140 +}
  141 +
  142 +static ssize_t
  143 +trip_point_type_show(struct device *dev, struct device_attribute *attr,
  144 + char *buf)
  145 +{
  146 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  147 + int trip;
  148 +
  149 + if (!tz->ops->get_trip_type)
  150 + return -EPERM;
  151 +
  152 + if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
  153 + return -EINVAL;
  154 +
  155 + return tz->ops->get_trip_type(tz, trip, buf);
  156 +}
  157 +
  158 +static ssize_t
  159 +trip_point_temp_show(struct device *dev, struct device_attribute *attr,
  160 + char *buf)
  161 +{
  162 + struct thermal_zone_device *tz = to_thermal_zone(dev);
  163 + int trip;
  164 +
  165 + if (!tz->ops->get_trip_temp)
  166 + return -EPERM;
  167 +
  168 + if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
  169 + return -EINVAL;
  170 +
  171 + return tz->ops->get_trip_temp(tz, trip, buf);
  172 +}
  173 +
  174 +static DEVICE_ATTR(type, 0444, type_show, NULL);
  175 +static DEVICE_ATTR(temp, 0444, temp_show, NULL);
  176 +static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
  177 +
  178 +static struct device_attribute trip_point_attrs[] = {
  179 + __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
  180 + __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
  181 + __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
  182 + __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
  183 + __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
  184 + __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
  185 + __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
  186 + __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
  187 + __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
  188 + __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
  189 + __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
  190 + __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
  191 + __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
  192 + __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
  193 + __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
  194 + __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
  195 + __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
  196 + __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
  197 + __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
  198 + __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
  199 +};
  200 +
  201 +#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
  202 +do { \
  203 + result = device_create_file(_dev, \
  204 + &trip_point_attrs[_index * 2]); \
  205 + if (result) \
  206 + break; \
  207 + result = device_create_file(_dev, \
  208 + &trip_point_attrs[_index * 2 + 1]); \
  209 +} while (0)
  210 +
  211 +#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
  212 +do { \
  213 + device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
  214 + device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
  215 +} while (0)
  216 +
  217 +/* sys I/F for cooling device */
  218 +#define to_cooling_device(_dev) \
  219 + container_of(_dev, struct thermal_cooling_device, device)
  220 +
  221 +static ssize_t
  222 +thermal_cooling_device_type_show(struct device *dev,
  223 + struct device_attribute *attr, char *buf)
  224 +{
  225 + struct thermal_cooling_device *cdev = to_cooling_device(dev);
  226 +
  227 + return sprintf(buf, "%s\n", cdev->type);
  228 +}
  229 +
  230 +static ssize_t
  231 +thermal_cooling_device_max_state_show(struct device *dev,
  232 + struct device_attribute *attr, char *buf)
  233 +{
  234 + struct thermal_cooling_device *cdev = to_cooling_device(dev);
  235 +
  236 + return cdev->ops->get_max_state(cdev, buf);
  237 +}
  238 +
  239 +static ssize_t
  240 +thermal_cooling_device_cur_state_show(struct device *dev,
  241 + struct device_attribute *attr, char *buf)
  242 +{
  243 + struct thermal_cooling_device *cdev = to_cooling_device(dev);
  244 +
  245 + return cdev->ops->get_cur_state(cdev, buf);
  246 +}
  247 +
  248 +static ssize_t
  249 +thermal_cooling_device_cur_state_store(struct device *dev,
  250 + struct device_attribute *attr,
  251 + const char *buf, size_t count)
  252 +{
  253 + struct thermal_cooling_device *cdev = to_cooling_device(dev);
  254 + int state;
  255 + int result;
  256 +
  257 + if (!sscanf(buf, "%d\n", &state))
  258 + return -EINVAL;
  259 +
  260 + if (state < 0)
  261 + return -EINVAL;
  262 +
  263 + result = cdev->ops->set_cur_state(cdev, state);
  264 + if (result)
  265 + return result;
  266 + return count;
  267 +}
  268 +
  269 +static struct device_attribute dev_attr_cdev_type =
  270 + __ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
  271 +static DEVICE_ATTR(max_state, 0444,
  272 + thermal_cooling_device_max_state_show, NULL);
  273 +static DEVICE_ATTR(cur_state, 0644,
  274 + thermal_cooling_device_cur_state_show,
  275 + thermal_cooling_device_cur_state_store);
  276 +
  277 +static ssize_t
  278 +thermal_cooling_device_trip_point_show(struct device *dev,
  279 + struct device_attribute *attr, char *buf)
  280 +{
  281 + struct thermal_cooling_device_instance *instance;
  282 +
  283 + instance =
  284 + container_of(attr, struct thermal_cooling_device_instance, attr);
  285 +
  286 + if (instance->trip == THERMAL_TRIPS_NONE)
  287 + return sprintf(buf, "-1\n");
  288 + else
  289 + return sprintf(buf, "%d\n", instance->trip);
  290 +}
  291 +
  292 +/* Device management */
  293 +
  294 +/**
  295 + * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
  296 + * this function is usually called in the thermal zone device .bind callback.
  297 + * @tz: thermal zone device
  298 + * @trip: indicates which trip point the cooling devices is
  299 + * associated with in this thermal zone.
  300 + * @cdev: thermal cooling device
  301 + */
  302 +int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
  303 + int trip,
  304 + struct thermal_cooling_device *cdev)
  305 +{
  306 + struct thermal_cooling_device_instance *dev;
  307 + struct thermal_cooling_device_instance *pos;
  308 + int result;
  309 +
  310 + if (trip >= tz->trips ||
  311 + (trip < 0 && trip != THERMAL_TRIPS_NONE))
  312 + return -EINVAL;
  313 +
  314 + if (!tz || !cdev)
  315 + return -EINVAL;
  316 +
  317 + dev =
  318 + kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
  319 + if (!dev)
  320 + return -ENOMEM;
  321 + dev->tz = tz;
  322 + dev->cdev = cdev;
  323 + dev->trip = trip;
  324 + result = get_idr(&tz->idr, &tz->lock, &dev->id);
  325 + if (result)
  326 + goto free_mem;
  327 +
  328 + sprintf(dev->name, "cdev%d", dev->id);
  329 + result =
  330 + sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
  331 + if (result)
  332 + goto release_idr;
  333 +
  334 + sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
  335 + dev->attr.attr.name = dev->attr_name;
  336 + dev->attr.attr.mode = 0444;
  337 + dev->attr.show = thermal_cooling_device_trip_point_show;
  338 + result = device_create_file(&tz->device, &dev->attr);
  339 + if (result)
  340 + goto remove_symbol_link;
  341 +
  342 + mutex_lock(&tz->lock);
  343 + list_for_each_entry(pos, &tz->cooling_devices, node)
  344 + if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
  345 + result = -EEXIST;
  346 + break;
  347 + }
  348 + if (!result)
  349 + list_add_tail(&dev->node, &tz->cooling_devices);
  350 + mutex_unlock(&tz->lock);
  351 +
  352 + if (!result)
  353 + return 0;
  354 +
  355 + device_remove_file(&tz->device, &dev->attr);
  356 + remove_symbol_link:
  357 + sysfs_remove_link(&tz->device.kobj, dev->name);
  358 + release_idr:
  359 + release_idr(&tz->idr, &tz->lock, dev->id);
  360 + free_mem:
  361 + kfree(dev);
  362 + return result;
  363 +}
  364 +EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
  365 +
  366 +/**
  367 + * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
  368 + * this function is usually called in the thermal zone device .unbind callback.
  369 + * @tz: thermal zone device
  370 + * @trip: indicates which trip point the cooling devices is
  371 + * associated with in this thermal zone.
  372 + * @cdev: thermal cooling device
  373 + */
  374 +int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
  375 + int trip,
  376 + struct thermal_cooling_device *cdev)
  377 +{
  378 + struct thermal_cooling_device_instance *pos, *next;
  379 +
  380 + mutex_lock(&tz->lock);
  381 + list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
  382 + if (pos->tz == tz && pos->trip == trip
  383 + && pos->cdev == cdev) {
  384 + list_del(&pos->node);
  385 + mutex_unlock(&tz->lock);
  386 + goto unbind;
  387 + }
  388 + }
  389 + mutex_unlock(&tz->lock);
  390 +
  391 + return -ENODEV;
  392 +
  393 + unbind:
  394 + device_remove_file(&tz->device, &pos->attr);
  395 + sysfs_remove_link(&tz->device.kobj, pos->name);
  396 + release_idr(&tz->idr, &tz->lock, pos->id);
  397 + kfree(pos);
  398 + return 0;
  399 +}
  400 +EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
  401 +
  402 +static void thermal_release(struct device *dev)
  403 +{
  404 + struct thermal_zone_device *tz;
  405 + struct thermal_cooling_device *cdev;
  406 +
  407 + if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {
  408 + tz = to_thermal_zone(dev);
  409 + kfree(tz);
  410 + } else {
  411 + cdev = to_cooling_device(dev);
  412 + kfree(cdev);
  413 + }
  414 +}
  415 +
  416 +static struct class thermal_class = {
  417 + .name = "thermal",
  418 + .dev_release = thermal_release,
  419 +};
  420 +
  421 +/**
  422 + * thermal_cooling_device_register - register a new thermal cooling device
  423 + * @type: the thermal cooling device type.
  424 + * @devdata: device private data.
  425 + * @ops: standard thermal cooling devices callbacks.
  426 + */
  427 +struct thermal_cooling_device *thermal_cooling_device_register(char *type,
  428 + void *devdata, struct thermal_cooling_device_ops *ops)
  429 +{
  430 + struct thermal_cooling_device *cdev;
  431 + struct thermal_zone_device *pos;
  432 + int result;
  433 +
  434 + if (strlen(type) >= THERMAL_NAME_LENGTH)
  435 + return NULL;
  436 +
  437 + if (!ops || !ops->get_max_state || !ops->get_cur_state ||
  438 + !ops->set_cur_state)
  439 + return NULL;
  440 +
  441 + cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
  442 + if (!cdev)
  443 + return NULL;
  444 +
  445 + result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
  446 + if (result) {
  447 + kfree(cdev);
  448 + return NULL;
  449 + }
  450 +
  451 + strcpy(cdev->type, type);
  452 + cdev->ops = ops;
  453 + cdev->device.class = &thermal_class;
  454 + cdev->devdata = devdata;
  455 + sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);
  456 + result = device_register(&cdev->device);
  457 + if (result) {
  458 + release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
  459 + kfree(cdev);
  460 + return NULL;
  461 + }
  462 +
  463 + /* sys I/F */
  464 + if (type) {
  465 + result = device_create_file(&cdev->device,
  466 + &dev_attr_cdev_type);
  467 + if (result)
  468 + goto unregister;
  469 + }
  470 +
  471 + result = device_create_file(&cdev->device, &dev_attr_max_state);
  472 + if (result)
  473 + goto unregister;
  474 +
  475 + result = device_create_file(&cdev->device, &dev_attr_cur_state);
  476 + if (result)
  477 + goto unregister;
  478 +
  479 + mutex_lock(&thermal_list_lock);
  480 + list_add(&cdev->node, &thermal_cdev_list);
  481 + list_for_each_entry(pos, &thermal_tz_list, node) {
  482 + if (!pos->ops->bind)
  483 + continue;
  484 + result = pos->ops->bind(pos, cdev);
  485 + if (result)
  486 + break;
  487 +
  488 + }
  489 + mutex_unlock(&thermal_list_lock);
  490 +
  491 + if (!result)
  492 + return cdev;
  493 +
  494 + unregister:
  495 + release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
  496 + device_unregister(&cdev->device);
  497 + return NULL;
  498 +}
  499 +EXPORT_SYMBOL(thermal_cooling_device_register);
  500 +
  501 +/**
  502 + * thermal_cooling_device_unregister - removes the registered thermal cooling device
  503 + *
  504 + * @cdev: the thermal cooling device to remove.
  505 + *
  506 + * thermal_cooling_device_unregister() must be called when the device is no
  507 + * longer needed.
  508 + */
  509 +void thermal_cooling_device_unregister(struct
  510 + thermal_cooling_device
  511 + *cdev)
  512 +{
  513 + struct thermal_zone_device *tz;
  514 + struct thermal_cooling_device *pos = NULL;
  515 +
  516 + if (!cdev)
  517 + return;
  518 +
  519 + mutex_lock(&thermal_list_lock);
  520 + list_for_each_entry(pos, &thermal_cdev_list, node)
  521 + if (pos == cdev)
  522 + break;
  523 + if (pos != cdev) {
  524 + /* thermal cooling device not found */
  525 + mutex_unlock(&thermal_list_lock);
  526 + return;
  527 + }
  528 + list_del(&cdev->node);
  529 + list_for_each_entry(tz, &thermal_tz_list, node) {
  530 + if (!tz->ops->unbind)
  531 + continue;
  532 + tz->ops->unbind(tz, cdev);
  533 + }
  534 + mutex_unlock(&thermal_list_lock);
  535 + if (cdev->type[0])
  536 + device_remove_file(&cdev->device,
  537 + &dev_attr_cdev_type);
  538 + device_remove_file(&cdev->device, &dev_attr_max_state);
  539 + device_remove_file(&cdev->device, &dev_attr_cur_state);
  540 +
  541 + release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
  542 + device_unregister(&cdev->device);
  543 + return;
  544 +}
  545 +EXPORT_SYMBOL(thermal_cooling_device_unregister);
  546 +
  547 +/**
  548 + * thermal_zone_device_register - register a new thermal zone device
  549 + * @type: the thermal zone device type
  550 + * @trips: the number of trip points the thermal zone support
  551 + * @devdata: private device data
  552 + * @ops: standard thermal zone device callbacks
  553 + *
  554 + * thermal_zone_device_unregister() must be called when the device is no
  555 + * longer needed.
  556 + */
  557 +struct thermal_zone_device *thermal_zone_device_register(char *type,
  558 + int trips, void *devdata,
  559 + struct thermal_zone_device_ops *ops)
  560 +{
  561 + struct thermal_zone_device *tz;
  562 + struct thermal_cooling_device *pos;
  563 + int result;
  564 + int count;
  565 +
  566 + if (strlen(type) >= THERMAL_NAME_LENGTH)
  567 + return NULL;
  568 +
  569 + if (trips > THERMAL_MAX_TRIPS || trips < 0)
  570 + return NULL;
  571 +
  572 + if (!ops || !ops->get_temp)
  573 + return NULL;
  574 +
  575 + tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
  576 + if (!tz)
  577 + return NULL;
  578 +
  579 + INIT_LIST_HEAD(&tz->cooling_devices);
  580 + idr_init(&tz->idr);
  581 + mutex_init(&tz->lock);
  582 + result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
  583 + if (result) {
  584 + kfree(tz);
  585 + return NULL;
  586 + }
  587 +
  588 + strcpy(tz->type, type);
  589 + tz->ops = ops;
  590 + tz->device.class = &thermal_class;
  591 + tz->devdata = devdata;
  592 + tz->trips = trips;
  593 + sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
  594 + result = device_register(&tz->device);
  595 + if (result) {
  596 + release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
  597 + kfree(tz);
  598 + return NULL;
  599 + }
  600 +
  601 + /* sys I/F */
  602 + if (type) {
  603 + result = device_create_file(&tz->device, &dev_attr_type);
  604 + if (result)
  605 + goto unregister;
  606 + }
  607 +
  608 + result = device_create_file(&tz->device, &dev_attr_temp);
  609 + if (result)
  610 + goto unregister;
  611 +
  612 + if (ops->get_mode) {
  613 + result = device_create_file(&tz->device, &dev_attr_mode);
  614 + if (result)
  615 + goto unregister;
  616 + }
  617 +
  618 + for (count = 0; count < trips; count++) {
  619 + TRIP_POINT_ATTR_ADD(&tz->device, count, result);
  620 + if (result)
  621 + goto unregister;
  622 + }
  623 +
  624 + mutex_lock(&thermal_list_lock);
  625 + list_add_tail(&tz->node, &thermal_tz_list);
  626 + if (ops->bind)
  627 + list_for_each_entry(pos, &thermal_cdev_list, node) {
  628 + result = ops->bind(tz, pos);
  629 + if (result)
  630 + break;
  631 + }
  632 + mutex_unlock(&thermal_list_lock);
  633 +
  634 + if (!result)
  635 + return tz;
  636 +
  637 + unregister:
  638 + release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
  639 + device_unregister(&tz->device);
  640 + return NULL;
  641 +}
  642 +EXPORT_SYMBOL(thermal_zone_device_register);
  643 +
  644 +/**
  645 + * thermal_device_unregister - removes the registered thermal zone device
  646 + *
  647 + * @tz: the thermal zone device to remove
  648 + */
  649 +void thermal_zone_device_unregister(struct thermal_zone_device *tz)
  650 +{
  651 + struct thermal_cooling_device *cdev;
  652 + struct thermal_zone_device *pos = NULL;
  653 + int count;
  654 +
  655 + if (!tz)
  656 + return;
  657 +
  658 + mutex_lock(&thermal_list_lock);
  659 + list_for_each_entry(pos, &thermal_tz_list, node)
  660 + if (pos == tz)
  661 + break;
  662 + if (pos != tz) {
  663 + /* thermal zone device not found */
  664 + mutex_unlock(&thermal_list_lock);
  665 + return;
  666 + }
  667 + list_del(&tz->node);
  668 + if (tz->ops->unbind)
  669 + list_for_each_entry(cdev, &thermal_cdev_list, node)
  670 + tz->ops->unbind(tz, cdev);
  671 + mutex_unlock(&thermal_list_lock);
  672 +
  673 + if (tz->type[0])
  674 + device_remove_file(&tz->device, &dev_attr_type);
  675 + device_remove_file(&tz->device, &dev_attr_temp);
  676 + if (tz->ops->get_mode)
  677 + device_remove_file(&tz->device, &dev_attr_mode);
  678 +
  679 + for (count = 0; count < tz->trips; count++)
  680 + TRIP_POINT_ATTR_REMOVE(&tz->device, count);
  681 +
  682 + release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
  683 + idr_destroy(&tz->idr);
  684 + mutex_destroy(&tz->lock);
  685 + device_unregister(&tz->device);
  686 + return;
  687 +}
  688 +EXPORT_SYMBOL(thermal_zone_device_unregister);
  689 +
  690 +static int __init thermal_init(void)
  691 +{
  692 + int result = 0;
  693 +
  694 + result = class_register(&thermal_class);
  695 + if (result) {
  696 + idr_destroy(&thermal_tz_idr);
  697 + idr_destroy(&thermal_cdev_idr);
  698 + mutex_destroy(&thermal_idr_lock);
  699 + mutex_destroy(&thermal_list_lock);
  700 + }
  701 + return result;
  702 +}
  703 +
  704 +static void __exit thermal_exit(void)
  705 +{
  706 + class_unregister(&thermal_class);
  707 + idr_destroy(&thermal_tz_idr);
  708 + idr_destroy(&thermal_cdev_idr);
  709 + mutex_destroy(&thermal_idr_lock);
  710 + mutex_destroy(&thermal_list_lock);
  711 +}
  712 +
  713 +subsys_initcall(thermal_init);
  714 +module_exit(thermal_exit);
include/acpi/acpi_bus.h
... ... @@ -321,6 +321,8 @@
321 321  
322 322 extern struct kobject *acpi_kobj;
323 323 extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int);
  324 +void acpi_bus_private_data_handler(acpi_handle, u32, void *);
  325 +int acpi_bus_get_private_data(acpi_handle, void **);
324 326 /*
325 327 * External Functions
326 328 */
include/acpi/processor.h
... ... @@ -4,7 +4,7 @@
4 4 #include <linux/kernel.h>
5 5 #include <linux/cpu.h>
6 6 #include <linux/cpuidle.h>
7   -
  7 +#include <linux/thermal.h>
8 8 #include <asm/acpi.h>
9 9  
10 10 #define ACPI_PROCESSOR_BUSY_METRIC 10
... ... @@ -219,7 +219,7 @@
219 219 struct acpi_processor_performance *performance;
220 220 struct acpi_processor_throttling throttling;
221 221 struct acpi_processor_limit limit;
222   -
  222 + struct thermal_cooling_device *cdev;
223 223 /* the _PDC objects for this processor, if any */
224 224 struct acpi_object_list *pdc;
225 225 };
... ... @@ -331,7 +331,7 @@
331 331 /* in processor_thermal.c */
332 332 int acpi_processor_get_limit_info(struct acpi_processor *pr);
333 333 extern struct file_operations acpi_processor_limit_fops;
334   -
  334 +extern struct thermal_cooling_device_ops processor_cooling_ops;
335 335 #ifdef CONFIG_CPU_FREQ
336 336 void acpi_thermal_cpufreq_init(void);
337 337 void acpi_thermal_cpufreq_exit(void);
include/linux/thermal.h
  1 +/*
  2 + * thermal.h ($Revision: 0 $)
  3 + *
  4 + * Copyright (C) 2008 Intel Corp
  5 + * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
  6 + * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
  7 + *
  8 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License as published by
  11 + * the Free Software Foundation; version 2 of the License.
  12 + *
  13 + * This program is distributed in the hope that it will be useful, but
  14 + * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 + * General Public License for more details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License along
  19 + * with this program; if not, write to the Free Software Foundation, Inc.,
  20 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21 + *
  22 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23 + */
  24 +
  25 +#ifndef __THERMAL_H__
  26 +#define __THERMAL_H__
  27 +
  28 +#include <linux/idr.h>
  29 +#include <linux/device.h>
  30 +
  31 +struct thermal_zone_device;
  32 +struct thermal_cooling_device;
  33 +
  34 +struct thermal_zone_device_ops {
  35 + int (*bind) (struct thermal_zone_device *,
  36 + struct thermal_cooling_device *);
  37 + int (*unbind) (struct thermal_zone_device *,
  38 + struct thermal_cooling_device *);
  39 + int (*get_temp) (struct thermal_zone_device *, char *);
  40 + int (*get_mode) (struct thermal_zone_device *, char *);
  41 + int (*set_mode) (struct thermal_zone_device *, const char *);
  42 + int (*get_trip_type) (struct thermal_zone_device *, int, char *);
  43 + int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
  44 +};
  45 +
  46 +struct thermal_cooling_device_ops {
  47 + int (*get_max_state) (struct thermal_cooling_device *, char *);
  48 + int (*get_cur_state) (struct thermal_cooling_device *, char *);
  49 + int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
  50 +};
  51 +
  52 +#define THERMAL_TRIPS_NONE -1
  53 +#define THERMAL_MAX_TRIPS 10
  54 +#define THERMAL_NAME_LENGTH 20
  55 +struct thermal_cooling_device {
  56 + int id;
  57 + char type[THERMAL_NAME_LENGTH];
  58 + struct device device;
  59 + void *devdata;
  60 + struct thermal_cooling_device_ops *ops;
  61 + struct list_head node;
  62 +};
  63 +
  64 +#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
  65 + ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
  66 +#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
  67 +
  68 +struct thermal_zone_device {
  69 + int id;
  70 + char type[THERMAL_NAME_LENGTH];
  71 + struct device device;
  72 + void *devdata;
  73 + int trips;
  74 + struct thermal_zone_device_ops *ops;
  75 + struct list_head cooling_devices;
  76 + struct idr idr;
  77 + struct mutex lock; /* protect cooling devices list */
  78 + struct list_head node;
  79 +};
  80 +
  81 +struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
  82 + struct thermal_zone_device_ops *);
  83 +void thermal_zone_device_unregister(struct thermal_zone_device *);
  84 +
  85 +int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
  86 + struct thermal_cooling_device *);
  87 +int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
  88 + struct thermal_cooling_device *);
  89 +
  90 +struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
  91 + struct thermal_cooling_device_ops *);
  92 +void thermal_cooling_device_unregister(struct thermal_cooling_device *);
  93 +
  94 +#endif /* __THERMAL_H__ */