patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / i386 / kernel / cpu / cpufreq / speedstep-ich.c
index 5ba993a..93b70f0 100644 (file)
@@ -67,28 +67,19 @@ static struct cpufreq_frequency_table speedstep_freqs[] = {
 /**
  * speedstep_set_state - set the SpeedStep state
  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
- * @notify: whether to call cpufreq_notify_transition for CPU speed changes
  *
  *   Tries to change the SpeedStep state. 
  */
-static void speedstep_set_state (unsigned int state, unsigned int notify)
+static void speedstep_set_state (unsigned int state)
 {
        u32                     pmbase;
        u8                      pm2_blk;
        u8                      value;
        unsigned long           flags;
-       struct cpufreq_freqs    freqs;
 
        if (!speedstep_chipset_dev || (state > 0x1))
                return;
 
-       freqs.old = speedstep_get_processor_frequency(speedstep_processor);
-       freqs.new = speedstep_freqs[state].frequency;
-       freqs.cpu = 0; /* speedstep.c is UP only driver */
-       
-       if (notify)
-               cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
        /* get PMBASE */
        pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
        if (!(pmbase & 0x01))
@@ -143,9 +134,6 @@ static void speedstep_set_state (unsigned int state, unsigned int notify)
                printk (KERN_ERR "cpufreq: change failed - I/O error\n");
        }
 
-       if (notify)
-               cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
-
        return;
 }
 
@@ -179,7 +167,7 @@ static int speedstep_activate (void)
 /**
  * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
  *
- *   Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to 
+ *   Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to 
  * the LPC bridge / PM module which contains all power-management 
  * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
  * chipset, or zero on failure.
@@ -252,11 +240,47 @@ static int speedstep_target (struct cpufreq_policy *policy,
                             unsigned int relation)
 {
        unsigned int    newstate = 0;
+       struct cpufreq_freqs freqs;
+       cpumask_t cpus_allowed, affected_cpu_map;
+       int i;
 
        if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
                return -EINVAL;
 
-       speedstep_set_state(newstate, 1);
+       /* no transition necessary */
+       if (freqs.old == freqs.new)
+               return 0;
+
+       freqs.old = speedstep_get_processor_frequency(speedstep_processor);
+       freqs.new = speedstep_freqs[newstate].frequency;
+       freqs.cpu = policy->cpu;
+
+       cpus_allowed = current->cpus_allowed;
+
+       /* only run on CPU to be set, or on its sibling */
+#ifdef CONFIG_SMP
+       affected_cpu_map = cpu_sibling_map[policy->cpu];
+#else
+       affected_cpu_map = cpumask_of_cpu(policy->cpu);
+#endif
+
+       for_each_cpu_mask(i, affected_cpu_map) {
+               freqs.cpu = i;
+               cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+       }
+
+       /* switch to physical CPU where state is to be changed */
+       set_cpus_allowed(current, affected_cpu_map);
+
+       speedstep_set_state(newstate);
+
+       /* allow to be run on all CPUs */
+       set_cpus_allowed(current, cpus_allowed);
+
+       for_each_cpu_mask(i, affected_cpu_map) {
+               freqs.cpu = i;
+               cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+       }
 
        return 0;
 }
@@ -279,21 +303,35 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
 {
        int             result = 0;
        unsigned int    speed;
+       cpumask_t       cpus_allowed,affected_cpu_map;
+
 
        /* capability check */
-       if (policy->cpu != 0)
+       if (policy->cpu != 0) /* FIXME: better support for SMT in cpufreq core. Up until then, it's better to register only one CPU */
                return -ENODEV;
 
+       /* only run on CPU to be set, or on its sibling */
+       cpus_allowed = current->cpus_allowed;
+#ifdef CONFIG_SMP
+       affected_cpu_map = cpu_sibling_map[policy->cpu];
+#else
+       affected_cpu_map = cpumask_of_cpu(policy->cpu);
+#endif
+       set_cpus_allowed(current, affected_cpu_map);
+
        /* detect low and high frequency */
        result = speedstep_get_freqs(speedstep_processor,
                                     &speedstep_freqs[SPEEDSTEP_LOW].frequency,
                                     &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
                                     &speedstep_set_state);
-       if (result)
+       if (result) {
+               set_cpus_allowed(current, cpus_allowed);
                return result;
+       }
 
        /* get current speed setting */
        speed = speedstep_get_processor_frequency(speedstep_processor);
+       set_cpus_allowed(current, cpus_allowed);
        if (!speed)
                return -EIO;
 
@@ -322,6 +360,10 @@ static int speedstep_cpu_exit(struct cpufreq_policy *policy)
        return 0;
 }
 
+static unsigned int speedstep_get(unsigned int cpu)
+{
+       return speedstep_get_processor_frequency(speedstep_processor);
+}
 
 static struct freq_attr* speedstep_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
@@ -335,6 +377,7 @@ static struct cpufreq_driver speedstep_driver = {
        .target         = speedstep_target,
        .init           = speedstep_cpu_init,
        .exit           = speedstep_cpu_exit,
+       .get            = speedstep_get,
        .owner          = THIS_MODULE,
        .attr           = speedstep_attr,
 };