]> err.no Git - linux-2.6/blobdiff - drivers/acpi/processor_throttling.c
Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
[linux-2.6] / drivers / acpi / processor_throttling.c
index 20d82f55ce5fa078c2aa5f9bbbaa9c57a1c94772..6742d7bc4777d8343d10aa34aa5d40a920f5f41a 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/sched.h>
 #include <linux/cpufreq.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -131,6 +132,7 @@ static int acpi_processor_get_throttling_control(struct acpi_processor *pr)
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *ptc = NULL;
        union acpi_object obj = { 0 };
+       struct acpi_processor_throttling *throttling;
 
        status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer);
        if (ACPI_FAILURE(status)) {
@@ -182,6 +184,22 @@ static int acpi_processor_get_throttling_control(struct acpi_processor *pr)
        memcpy(&pr->throttling.status_register, obj.buffer.pointer,
               sizeof(struct acpi_ptc_register));
 
+       throttling = &pr->throttling;
+
+       if ((throttling->control_register.bit_width +
+               throttling->control_register.bit_offset) > 32) {
+               printk(KERN_ERR PREFIX "Invalid _PTC control register\n");
+               result = -EFAULT;
+               goto end;
+       }
+
+       if ((throttling->status_register.bit_width +
+               throttling->status_register.bit_offset) > 32) {
+               printk(KERN_ERR PREFIX "Invalid _PTC status register\n");
+               result = -EFAULT;
+               goto end;
+       }
+
       end:
        kfree(buffer.pointer);
 
@@ -376,44 +394,132 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr)
        return 0;
 }
 
-static int acpi_read_throttling_status(struct acpi_processor_throttling
-                                      *throttling)
+#ifdef CONFIG_X86
+static int acpi_throttling_rdmsr(struct acpi_processor *pr,
+                                       acpi_integer * value)
+{
+       struct cpuinfo_x86 *c;
+       u64 msr_high, msr_low;
+       unsigned int cpu;
+       u64 msr = 0;
+       int ret = -1;
+
+       cpu = pr->id;
+       c = &cpu_data(cpu);
+
+       if ((c->x86_vendor != X86_VENDOR_INTEL) ||
+               !cpu_has(c, X86_FEATURE_ACPI)) {
+               printk(KERN_ERR PREFIX
+                       "HARDWARE addr space,NOT supported yet\n");
+       } else {
+               msr_low = 0;
+               msr_high = 0;
+               rdmsr_safe(MSR_IA32_THERM_CONTROL,
+                       (u32 *)&msr_low , (u32 *) &msr_high);
+               msr = (msr_high << 32) | msr_low;
+               *value = (acpi_integer) msr;
+               ret = 0;
+       }
+       return ret;
+}
+
+static int acpi_throttling_wrmsr(struct acpi_processor *pr, acpi_integer value)
+{
+       struct cpuinfo_x86 *c;
+       unsigned int cpu;
+       int ret = -1;
+       u64 msr;
+
+       cpu = pr->id;
+       c = &cpu_data(cpu);
+
+       if ((c->x86_vendor != X86_VENDOR_INTEL) ||
+               !cpu_has(c, X86_FEATURE_ACPI)) {
+               printk(KERN_ERR PREFIX
+                       "HARDWARE addr space,NOT supported yet\n");
+       } else {
+               msr = value;
+               wrmsr_safe(MSR_IA32_THERM_CONTROL,
+                       msr & 0xffffffff, msr >> 32);
+               ret = 0;
+       }
+       return ret;
+}
+#else
+static int acpi_throttling_rdmsr(struct acpi_processor *pr,
+                               acpi_integer * value)
+{
+       printk(KERN_ERR PREFIX
+               "HARDWARE addr space,NOT supported yet\n");
+       return -1;
+}
+
+static int acpi_throttling_wrmsr(struct acpi_processor *pr, acpi_integer value)
+{
+       printk(KERN_ERR PREFIX
+               "HARDWARE addr space,NOT supported yet\n");
+       return -1;
+}
+#endif
+
+static int acpi_read_throttling_status(struct acpi_processor *pr,
+                                       acpi_integer *value)
 {
-       int value = -1;
+       u32 bit_width, bit_offset;
+       u64 ptc_value;
+       u64 ptc_mask;
+       struct acpi_processor_throttling *throttling;
+       int ret = -1;
+
+       throttling = &pr->throttling;
        switch (throttling->status_register.space_id) {
        case ACPI_ADR_SPACE_SYSTEM_IO:
+               ptc_value = 0;
+               bit_width = throttling->status_register.bit_width;
+               bit_offset = throttling->status_register.bit_offset;
+
                acpi_os_read_port((acpi_io_address) throttling->status_register.
-                                 address, &value,
-                                 (u32) throttling->status_register.bit_width *
-                                 8);
+                                 address, (u32 *) &ptc_value,
+                                 (u32) (bit_width + bit_offset));
+               ptc_mask = (1 << bit_width) - 1;
+               *value = (acpi_integer) ((ptc_value >> bit_offset) & ptc_mask);
+               ret = 0;
                break;
        case ACPI_ADR_SPACE_FIXED_HARDWARE:
-               printk(KERN_ERR PREFIX
-                      "HARDWARE addr space,NOT supported yet\n");
+               ret = acpi_throttling_rdmsr(pr, value);
                break;
        default:
                printk(KERN_ERR PREFIX "Unknown addr space %d\n",
                       (u32) (throttling->status_register.space_id));
        }
-       return value;
+       return ret;
 }
 
-static int acpi_write_throttling_state(struct acpi_processor_throttling
-                                      *throttling, int value)
+static int acpi_write_throttling_state(struct acpi_processor *pr,
+                               acpi_integer value)
 {
+       u32 bit_width, bit_offset;
+       u64 ptc_value;
+       u64 ptc_mask;
+       struct acpi_processor_throttling *throttling;
        int ret = -1;
 
+       throttling = &pr->throttling;
        switch (throttling->control_register.space_id) {
        case ACPI_ADR_SPACE_SYSTEM_IO:
+               bit_width = throttling->control_register.bit_width;
+               bit_offset = throttling->control_register.bit_offset;
+               ptc_mask = (1 << bit_width) - 1;
+               ptc_value = value & ptc_mask;
+
                acpi_os_write_port((acpi_io_address) throttling->
-                                  control_register.address, value,
-                                  (u32) throttling->control_register.
-                                  bit_width * 8);
+                                       control_register.address,
+                                       (u32) (ptc_value << bit_offset),
+                                       (u32) (bit_width + bit_offset));
                ret = 0;
                break;
        case ACPI_ADR_SPACE_FIXED_HARDWARE:
-               printk(KERN_ERR PREFIX
-                      "HARDWARE addr space,NOT supported yet\n");
+               ret = acpi_throttling_wrmsr(pr, value);
                break;
        default:
                printk(KERN_ERR PREFIX "Unknown addr space %d\n",
@@ -422,7 +528,8 @@ static int acpi_write_throttling_state(struct acpi_processor_throttling
        return ret;
 }
 
-static int acpi_get_throttling_state(struct acpi_processor *pr, int value)
+static int acpi_get_throttling_state(struct acpi_processor *pr,
+                               acpi_integer value)
 {
        int i;
 
@@ -438,22 +545,26 @@ static int acpi_get_throttling_state(struct acpi_processor *pr, int value)
        return i;
 }
 
-static int acpi_get_throttling_value(struct acpi_processor *pr, int state)
+static int acpi_get_throttling_value(struct acpi_processor *pr,
+                       int state, acpi_integer *value)
 {
-       int value = -1;
+       int ret = -1;
+
        if (state >= 0 && state <= pr->throttling.state_count) {
                struct acpi_processor_tx_tss *tx =
                    (struct acpi_processor_tx_tss *)&(pr->throttling.
                                                      states_tss[state]);
-               value = tx->control;
+               *value = tx->control;
+               ret = 0;
        }
-       return value;
+       return ret;
 }
 
 static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
 {
        int state = 0;
-       u32 value = 0;
+       int ret;
+       acpi_integer value;
 
        if (!pr)
                return -EINVAL;
@@ -462,20 +573,32 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
                return -ENODEV;
 
        pr->throttling.state = 0;
-       local_irq_disable();
-       value = acpi_read_throttling_status(&pr->throttling);
-       if (value >= 0) {
+
+       value = 0;
+       ret = acpi_read_throttling_status(pr, &value);
+       if (ret >= 0) {
                state = acpi_get_throttling_state(pr, value);
                pr->throttling.state = state;
        }
-       local_irq_enable();
 
        return 0;
 }
 
 static int acpi_processor_get_throttling(struct acpi_processor *pr)
 {
-       return pr->throttling.acpi_processor_get_throttling(pr);
+       cpumask_t saved_mask;
+       int ret;
+
+       /*
+        * Migrate task to the cpu pointed by pr.
+        */
+       saved_mask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(pr->id));
+       ret = pr->throttling.acpi_processor_get_throttling(pr);
+       /* restore the previous state */
+       set_cpus_allowed(current, saved_mask);
+
+       return ret;
 }
 
 static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
@@ -588,7 +711,8 @@ static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
 static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
                                             int state)
 {
-       u32 value = 0;
+       int ret;
+       acpi_integer value;
 
        if (!pr)
                return -EINVAL;
@@ -605,21 +729,29 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
        if (state < pr->throttling_platform_limit)
                return -EPERM;
 
-       local_irq_disable();
-
-       value = acpi_get_throttling_value(pr, state);
-       if (value >= 0) {
-               acpi_write_throttling_state(&pr->throttling, value);
+       value = 0;
+       ret = acpi_get_throttling_value(pr, state, &value);
+       if (ret >= 0) {
+               acpi_write_throttling_state(pr, value);
                pr->throttling.state = state;
        }
-       local_irq_enable();
 
        return 0;
 }
 
 int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
 {
-       return pr->throttling.acpi_processor_set_throttling(pr, state);
+       cpumask_t saved_mask;
+       int ret;
+       /*
+        * Migrate task to the cpu pointed by pr.
+        */
+       saved_mask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(pr->id));
+       ret = pr->throttling.acpi_processor_set_throttling(pr, state);
+       /* restore the previous state */
+       set_cpus_allowed(current, saved_mask);
+       return ret;
 }
 
 int acpi_processor_get_throttling_info(struct acpi_processor *pr)