[CPUFREQ] Measure transition latency at driver initialization
authorMattia Dongili <malattia@linux.it>
Fri, 2 Dec 2005 20:59:41 +0000 (21:59 +0100)
committerDave Jones <davej@redhat.com>
Wed, 7 Dec 2005 03:27:15 +0000 (19:27 -0800)
The attached patch introduces runtime latency measurement for ICH[234]
based chipsets instead of using CPUFREQ_ETERNAL. It includes
some sanity checks in case the measured value is out of range and
assigns a safe value of 500uSec that should still be enough on
problematics chipsets (current testing report values ~200uSec). The
measurement is currently done in speedstep_get_freqs in order to avoid
further unnecessary transitions and in the hope it'll come handy for SMI
also.

Signed-off-by: Mattia Dongili <malattia@linux.it>
Acked-by: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Dave Jones <davej@redhat.com>
 speedstep-ich.c |    4 ++--
 speedstep-lib.c |   32 +++++++++++++++++++++++++++++++-
 speedstep-lib.h |    1 +
 speedstep-smi.c |    1 +
 4 files changed, 35 insertions(+), 3 deletions(-)

arch/i386/kernel/cpu/cpufreq/speedstep-ich.c
arch/i386/kernel/cpu/cpufreq/speedstep-lib.c
arch/i386/kernel/cpu/cpufreq/speedstep-lib.h
arch/i386/kernel/cpu/cpufreq/speedstep-smi.c

index 5b7d18a06afaeee09b090c20d3bb6b7c7016f0f4..862e0b5656b9ed24cc5d46a26ba23b1458c4a35a 100644 (file)
@@ -315,10 +315,11 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
        cpus_allowed = current->cpus_allowed;
        set_cpus_allowed(current, policy->cpus);
 
-       /* detect low and high frequency */
+       /* detect low and high frequency and transition latency */
        result = speedstep_get_freqs(speedstep_processor,
                                     &speedstep_freqs[SPEEDSTEP_LOW].frequency,
                                     &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+                                    &policy->cpuinfo.transition_latency,
                                     &speedstep_set_state);
        set_cpus_allowed(current, cpus_allowed);
        if (result)
@@ -335,7 +336,6 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
 
        /* cpuinfo and default policy values */
        policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
        policy->cur = speed;
 
        result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
index d368b3f5fce85397e9e139eb4f5b0314ac15c178..7c47005a1805c41dec67157c18c103dd6720f3c3 100644 (file)
@@ -320,11 +320,13 @@ EXPORT_SYMBOL_GPL(speedstep_detect_processor);
 unsigned int speedstep_get_freqs(unsigned int processor,
                                  unsigned int *low_speed,
                                  unsigned int *high_speed,
+                                 unsigned int *transition_latency,
                                  void (*set_state) (unsigned int state))
 {
        unsigned int prev_speed;
        unsigned int ret = 0;
        unsigned long flags;
+       struct timeval tv1, tv2;
 
        if ((!processor) || (!low_speed) || (!high_speed) || (!set_state))
                return -EINVAL;
@@ -337,7 +339,7 @@ unsigned int speedstep_get_freqs(unsigned int processor,
                return -EIO;
 
        dprintk("previous speed is %u\n", prev_speed);
-       
+
        local_irq_save(flags);
 
        /* switch to low state */
@@ -350,8 +352,17 @@ unsigned int speedstep_get_freqs(unsigned int processor,
 
        dprintk("low speed is %u\n", *low_speed);
 
+       /* start latency measurement */
+       if (transition_latency)
+               do_gettimeofday(&tv1);
+
        /* switch to high state */
        set_state(SPEEDSTEP_HIGH);
+
+       /* end latency measurement */
+       if (transition_latency)
+               do_gettimeofday(&tv2);
+
        *high_speed = speedstep_get_processor_frequency(processor);
        if (!*high_speed) {
                ret = -EIO;
@@ -369,6 +380,25 @@ unsigned int speedstep_get_freqs(unsigned int processor,
        if (*high_speed != prev_speed)
                set_state(SPEEDSTEP_LOW);
 
+       if (transition_latency) {
+               *transition_latency = (tv2.tv_sec - tv1.tv_sec) * USEC_PER_SEC +
+                       tv2.tv_usec - tv1.tv_usec;
+               dprintk("transition latency is %u uSec\n", *transition_latency);
+
+               /* convert uSec to nSec and add 20% for safety reasons */
+               *transition_latency *= 1200;
+
+               /* check if the latency measurement is too high or too low
+                * and set it to a safe value (500uSec) in that case
+                */
+               if (*transition_latency > 10000000 || *transition_latency < 50000) {
+                       printk (KERN_WARNING "speedstep: frequency transition measured seems out of "
+                                       "range (%u nSec), falling back to a safe one of %u nSec.\n",
+                                       *transition_latency, 500000);
+                       *transition_latency = 500000;
+               }
+       }
+
  out:
        local_irq_restore(flags);
        return (ret);
index 261a2c9b7f6b875bbded0143ba87011660a4c0c1..6a727fd3a77e099e863bc982309cd1028fd2e55e 100644 (file)
@@ -44,4 +44,5 @@ extern unsigned int speedstep_get_processor_frequency(unsigned int processor);
 extern unsigned int speedstep_get_freqs(unsigned int processor,
          unsigned int *low_speed,
          unsigned int *high_speed,
+         unsigned int *transition_latency,
          void (*set_state) (unsigned int state));
index 2718fb6f6aba89467467e48f1dd24fba465fae08..28cc5d524afcd18a2dce1ee4f5ccb3d2c737faf1 100644 (file)
@@ -269,6 +269,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
                result = speedstep_get_freqs(speedstep_processor,
                                &speedstep_freqs[SPEEDSTEP_LOW].frequency,
                                &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+                               NULL,
                                &speedstep_set_state);
 
                if (result) {