diff options
author | Zhang Rui <rui.zhang@intel.com> | 2008-01-17 02:51:23 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-01 23:18:19 -0500 |
commit | d9460fd227ed2ce52941b6a12ad4de05c195f6aa (patch) | |
tree | 1fb877cf41c8599e0fc9c1fbf902b88baba60708 | |
parent | 05a83d972293f39a66bc2aa409a5e7996bba585d (diff) |
ACPI: register ACPI Processor as generic thermal cooling device
Register ACPI processor as thermal cooling devices.
A combination of processor T-state and P-state are used for thermal throttling.
the processor will reduce the frequency first and then set the T-state.
we use cpufreq_thermal_reduction_pctg to calculate the cpufreq limit,
and call cpufreq_verify_with_limit to set the cpufreq limit.
if cpufreq driver is loaded, then we have four cooling state for cpufreq control.
cooling state 0: cpufreq limit == max_freq
cooling state 1: cpufreq limit == max_freq * 80%
cooling state 2: cpufreq limit == max_freq * 60%
cooling state 3: cpufreq limit == max_freq * 40%
after the cpufreq limit is set to 40 percentage of the max_freq,
we use T-state for cooling.
eg. a processor has P-state support, and it has 8 T-state (T0-T7),
the max_state of the proceesor is 10:
state cpufreq-limit T-state
0: max_freq T0
1: max_freq * 80% T0
2: max_freq * 60% T0
3: max_freq * 40% T0
4: max_freq * 40% T1
5: max_freq * 40% T2
6: max_freq * 40% T3
7: max_freq * 40% T4
8: max_freq * 40% T5
9: max_freq * 40% T6
10: max_freq * 40% T7
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Thomas Sujith <sujith.thomas@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | drivers/acpi/processor_core.c | 23 | ||||
-rw-r--r-- | drivers/acpi/processor_thermal.c | 134 | ||||
-rw-r--r-- | include/acpi/processor.h | 6 |
3 files changed, 155 insertions, 8 deletions
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e48ee4f8749f..5668c5e8ae19 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c | |||
@@ -668,6 +668,24 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) | |||
668 | 668 | ||
669 | acpi_processor_power_init(pr, device); | 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 | if (pr->flags.throttling) { | 689 | if (pr->flags.throttling) { |
672 | printk(KERN_INFO PREFIX "%s [%s] (supports", | 690 | printk(KERN_INFO PREFIX "%s [%s] (supports", |
673 | acpi_device_name(device), acpi_device_bid(device)); | 691 | acpi_device_name(device), acpi_device_bid(device)); |
@@ -791,6 +809,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type) | |||
791 | 809 | ||
792 | acpi_processor_remove_fs(device); | 810 | acpi_processor_remove_fs(device); |
793 | 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; | ||
816 | |||
794 | processors[pr->id] = NULL; | 817 | processors[pr->id] = NULL; |
795 | 818 | ||
796 | kfree(pr); | 819 | kfree(pr); |
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 06e6f3fb8825..9cb43f52f7b6 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/cpufreq.h> | 32 | #include <linux/cpufreq.h> |
33 | #include <linux/proc_fs.h> | 33 | #include <linux/proc_fs.h> |
34 | #include <linux/seq_file.h> | 34 | #include <linux/seq_file.h> |
35 | #include <linux/sysdev.h> | ||
35 | 36 | ||
36 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
37 | 38 | ||
@@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr) | |||
93 | * _any_ cpufreq driver and not only the acpi-cpufreq driver. | 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 | static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; | 100 | static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; |
97 | static unsigned int acpi_thermal_cpufreq_is_init = 0; | 101 | static unsigned int acpi_thermal_cpufreq_is_init = 0; |
98 | 102 | ||
@@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase(unsigned int cpu) | |||
109 | if (!cpu_has_cpufreq(cpu)) | 113 | if (!cpu_has_cpufreq(cpu)) |
110 | return -ENODEV; | 114 | return -ENODEV; |
111 | 115 | ||
112 | if (cpufreq_thermal_reduction_pctg[cpu] < 60) { | 116 | if (cpufreq_thermal_reduction_pctg[cpu] < |
113 | cpufreq_thermal_reduction_pctg[cpu] += 20; | 117 | CPUFREQ_THERMAL_MAX_STEP) { |
118 | cpufreq_thermal_reduction_pctg[cpu]++; | ||
114 | cpufreq_update_policy(cpu); | 119 | cpufreq_update_policy(cpu); |
115 | return 0; | 120 | return 0; |
116 | } | 121 | } |
@@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu) | |||
123 | if (!cpu_has_cpufreq(cpu)) | 128 | if (!cpu_has_cpufreq(cpu)) |
124 | return -ENODEV; | 129 | return -ENODEV; |
125 | 130 | ||
126 | if (cpufreq_thermal_reduction_pctg[cpu] > 20) | 131 | if (cpufreq_thermal_reduction_pctg[cpu] > |
127 | cpufreq_thermal_reduction_pctg[cpu] -= 20; | 132 | (CPUFREQ_THERMAL_MIN_STEP + 1)) |
133 | cpufreq_thermal_reduction_pctg[cpu]--; | ||
128 | else | 134 | else |
129 | cpufreq_thermal_reduction_pctg[cpu] = 0; | 135 | cpufreq_thermal_reduction_pctg[cpu] = 0; |
130 | cpufreq_update_policy(cpu); | 136 | cpufreq_update_policy(cpu); |
@@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, | |||
143 | 149 | ||
144 | max_freq = | 150 | max_freq = |
145 | (policy->cpuinfo.max_freq * | 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 | cpufreq_verify_within_limits(policy, 0, max_freq); | 154 | cpufreq_verify_within_limits(policy, 0, max_freq); |
149 | 155 | ||
@@ -155,6 +161,32 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = { | |||
155 | .notifier_call = acpi_thermal_cpufreq_notifier, | 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 | void acpi_thermal_cpufreq_init(void) | 190 | void acpi_thermal_cpufreq_init(void) |
159 | { | 191 | { |
160 | int i; | 192 | int i; |
@@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void) | |||
179 | } | 211 | } |
180 | 212 | ||
181 | #else /* ! CONFIG_CPU_FREQ */ | 213 | #else /* ! CONFIG_CPU_FREQ */ |
214 | static int cpufreq_get_max_state(unsigned int cpu) | ||
215 | { | ||
216 | return 0; | ||
217 | } | ||
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 | } | ||
182 | 228 | ||
183 | static int acpi_thermal_cpufreq_increase(unsigned int cpu) | 229 | static int acpi_thermal_cpufreq_increase(unsigned int cpu) |
184 | { | 230 | { |
@@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct acpi_processor *pr) | |||
310 | return 0; | 356 | return 0; |
311 | } | 357 | } |
312 | 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 | }; | ||
436 | |||
313 | /* /proc interface */ | 437 | /* /proc interface */ |
314 | 438 | ||
315 | static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) | 439 | static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) |
diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 76411b1fc4fd..0787635a11aa 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
5 | #include <linux/cpu.h> | 5 | #include <linux/cpu.h> |
6 | #include <linux/cpuidle.h> | 6 | #include <linux/cpuidle.h> |
7 | 7 | #include <linux/thermal.h> | |
8 | #include <asm/acpi.h> | 8 | #include <asm/acpi.h> |
9 | 9 | ||
10 | #define ACPI_PROCESSOR_BUSY_METRIC 10 | 10 | #define ACPI_PROCESSOR_BUSY_METRIC 10 |
@@ -218,7 +218,7 @@ struct acpi_processor { | |||
218 | struct acpi_processor_performance *performance; | 218 | struct acpi_processor_performance *performance; |
219 | struct acpi_processor_throttling throttling; | 219 | struct acpi_processor_throttling throttling; |
220 | struct acpi_processor_limit limit; | 220 | struct acpi_processor_limit limit; |
221 | 221 | struct thermal_cooling_device *cdev; | |
222 | /* the _PDC objects for this processor, if any */ | 222 | /* the _PDC objects for this processor, if any */ |
223 | struct acpi_object_list *pdc; | 223 | struct acpi_object_list *pdc; |
224 | }; | 224 | }; |
@@ -330,7 +330,7 @@ extern struct cpuidle_driver acpi_idle_driver; | |||
330 | /* in processor_thermal.c */ | 330 | /* in processor_thermal.c */ |
331 | int acpi_processor_get_limit_info(struct acpi_processor *pr); | 331 | int acpi_processor_get_limit_info(struct acpi_processor *pr); |
332 | extern struct file_operations acpi_processor_limit_fops; | 332 | extern struct file_operations acpi_processor_limit_fops; |
333 | 333 | extern struct thermal_cooling_device_ops processor_cooling_ops; | |
334 | #ifdef CONFIG_CPU_FREQ | 334 | #ifdef CONFIG_CPU_FREQ |
335 | void acpi_thermal_cpufreq_init(void); | 335 | void acpi_thermal_cpufreq_init(void); |
336 | void acpi_thermal_cpufreq_exit(void); | 336 | void acpi_thermal_cpufreq_exit(void); |