From: Zhang Rui <[EMAIL PROTECTED]>

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 <[EMAIL PROTECTED]>
Signed-off-by: Zhao Yakui <[EMAIL PROTECTED]>
Signed-off-by: Thomas Sujith <[EMAIL PROTECTED]>
---
 drivers/acpi/processor_core.c    |   23 ++++++
 drivers/acpi/processor_thermal.c |  134 +++++++++++++++++++++++++++++++++++++--
 include/acpi/processor.h         |    6 -
 3 files changed, 155 insertions(+), 8 deletions(-)

Index: linux-2.6/drivers/acpi/processor_thermal.c
===================================================================
--- linux-2.6.orig/drivers/acpi/processor_thermal.c
+++ linux-2.6/drivers/acpi/processor_thermal.c
@@ -32,6 +32,7 @@
 #include <linux/cpufreq.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/sysdev.h>
 
 #include <asm/uaccess.h>
 
@@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(st
  * _any_ cpufreq driver and not only the acpi-cpufreq driver.
  */
 
+#define CPUFREQ_THERMAL_MIN_STEP 0
+#define CPUFREQ_THERMAL_MAX_STEP 3
+
 static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
 static unsigned int acpi_thermal_cpufreq_is_init = 0;
 
@@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase
        if (!cpu_has_cpufreq(cpu))
                return -ENODEV;
 
-       if (cpufreq_thermal_reduction_pctg[cpu] < 60) {
-               cpufreq_thermal_reduction_pctg[cpu] += 20;
+       if (cpufreq_thermal_reduction_pctg[cpu] <
+               CPUFREQ_THERMAL_MAX_STEP) {
+               cpufreq_thermal_reduction_pctg[cpu]++;
                cpufreq_update_policy(cpu);
                return 0;
        }
@@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease
        if (!cpu_has_cpufreq(cpu))
                return -ENODEV;
 
-       if (cpufreq_thermal_reduction_pctg[cpu] > 20)
-               cpufreq_thermal_reduction_pctg[cpu] -= 20;
+       if (cpufreq_thermal_reduction_pctg[cpu] >
+               (CPUFREQ_THERMAL_MIN_STEP + 1))
+               cpufreq_thermal_reduction_pctg[cpu]--;
        else
                cpufreq_thermal_reduction_pctg[cpu] = 0;
        cpufreq_update_policy(cpu);
@@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier
 
        max_freq =
            (policy->cpuinfo.max_freq *
-            (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;
+            (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100;
 
        cpufreq_verify_within_limits(policy, 0, max_freq);
 
@@ -155,6 +161,32 @@ static struct notifier_block acpi_therma
        .notifier_call = acpi_thermal_cpufreq_notifier,
 };
 
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       return CPUFREQ_THERMAL_MAX_STEP;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       return cpufreq_thermal_reduction_pctg[cpu];
+}
+
+static int cpufreq_set_cur_state(unsigned int cpu, int state)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return 0;
+
+       cpufreq_thermal_reduction_pctg[cpu] = state;
+       cpufreq_update_policy(cpu);
+       return 0;
+}
+
 void acpi_thermal_cpufreq_init(void)
 {
        int i;
@@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void)
 }
 
 #else                          /* ! CONFIG_CPU_FREQ */
+static int cpufreq_get_max_state(unsigned int cpu)
+{
+       return 0;
+}
+
+static int cpufreq_get_cur_state(unsigned int cpu)
+{
+       return 0;
+}
+
+static int cpufreq_set_cur_state(unsigned int cpu, int state)
+{
+       return 0;
+}
 
 static int acpi_thermal_cpufreq_increase(unsigned int cpu)
 {
@@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct
        return 0;
 }
 
+/* thermal coolign device callbacks */
+static int acpi_processor_max_state(struct acpi_processor *pr)
+{
+       int max_state = 0;
+
+       /*
+        * There exists four states according to
+        * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3
+        */
+       max_state += cpufreq_get_max_state(pr->id);
+       if (pr->flags.throttling)
+               max_state += (pr->throttling.state_count -1);
+
+       return max_state;
+}
+static int
+processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
+}
+
+static int
+processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+       int cur_state;
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       cur_state = cpufreq_get_cur_state(pr->id);
+       if (pr->flags.throttling)
+               cur_state += pr->throttling.state;
+
+       return sprintf(buf, "%d\n", cur_state);
+}
+
+static int
+processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int 
state)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_processor *pr = acpi_driver_data(device);
+       int result = 0;
+       int max_pstate;
+
+       if (!device || !pr)
+               return -EINVAL;
+
+       max_pstate = cpufreq_get_max_state(pr->id);
+
+       if (state > acpi_processor_max_state(pr))
+               return -EINVAL;
+
+       if (state <= max_pstate) {
+               if (pr->flags.throttling && pr->throttling.state)
+                       result = acpi_processor_set_throttling(pr, 0);
+               cpufreq_set_cur_state(pr->id, state);
+       } else {
+               cpufreq_set_cur_state(pr->id, max_pstate);
+               result = acpi_processor_set_throttling(pr,
+                               state - max_pstate);
+       }
+       return result;
+}
+
+struct thermal_cooling_device_ops processor_cooling_ops = {
+       .get_max_state = processor_get_max_state,
+       .get_cur_state = processor_get_cur_state,
+       .set_cur_state = processor_set_cur_state,
+};
+
 /* /proc interface */
 
 static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
Index: linux-2.6/include/acpi/processor.h
===================================================================
--- linux-2.6.orig/include/acpi/processor.h
+++ linux-2.6/include/acpi/processor.h
@@ -4,7 +4,7 @@
 #include <linux/kernel.h>
 #include <linux/cpu.h>
 #include <linux/cpuidle.h>
-
+#include <linux/thermal.h>
 #include <asm/acpi.h>
 
 #define ACPI_PROCESSOR_BUSY_METRIC     10
@@ -218,7 +218,7 @@ struct acpi_processor {
        struct acpi_processor_performance *performance;
        struct acpi_processor_throttling throttling;
        struct acpi_processor_limit limit;
-
+       struct thermal_cooling_device *cdev;
        /* the _PDC objects for this processor, if any */
        struct acpi_object_list *pdc;
 };
@@ -330,7 +330,7 @@ extern struct cpuidle_driver acpi_idle_d
 /* in processor_thermal.c */
 int acpi_processor_get_limit_info(struct acpi_processor *pr);
 extern struct file_operations acpi_processor_limit_fops;
-
+extern struct thermal_cooling_device_ops processor_cooling_ops;
 #ifdef CONFIG_CPU_FREQ
 void acpi_thermal_cpufreq_init(void);
 void acpi_thermal_cpufreq_exit(void);
Index: linux-2.6/drivers/acpi/processor_core.c
===================================================================
--- linux-2.6.orig/drivers/acpi/processor_core.c
+++ linux-2.6/drivers/acpi/processor_core.c
@@ -668,6 +668,24 @@ static int __cpuinit acpi_processor_star
 
        acpi_processor_power_init(pr, device);
 
+       pr->cdev = thermal_cooling_device_register("Processor", device,
+                                               &processor_cooling_ops);
+       if (pr->cdev)
+               printk(KERN_INFO PREFIX
+                       "%s is registered as cooling_device%d\n",
+                       device->dev.bus_id, pr->cdev->id);
+       else
+               goto end;
+
+       result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj,
+                                       "thermal_cooling");
+       if (result)
+               return result;
+       result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj,
+                                       "device");
+       if (result)
+               return result;
+
        if (pr->flags.throttling) {
                printk(KERN_INFO PREFIX "%s [%s] (supports",
                       acpi_device_name(device), acpi_device_bid(device));
@@ -791,6 +809,11 @@ static int acpi_processor_remove(struct 
 
        acpi_processor_remove_fs(device);
 
+       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+       sysfs_remove_link(&pr->cdev->device.kobj, "device");
+       thermal_cooling_device_unregister(pr->cdev);
+       pr->cdev = NULL;
+
        processors[pr->id] = NULL;
 
        kfree(pr);


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to