]> git.hungrycats.org Git - linux/commitdiff
[CPUFREQ] (Hopefully) fix cpufreq resume support.
authorDave Jones <davej@redhat.com>
Fri, 7 May 2004 16:13:59 +0000 (17:13 +0100)
committerDave Jones <davej@delerium.codemonkey.org.uk>
Fri, 7 May 2004 16:13:59 +0000 (17:13 +0100)
Upon resuming, first CPUfreq hardware support needs to be re-enabled in certain cases
(call to cpufreq_driver->resume()).

Then, two different paths may need to be taken:
a) frequency during suspend equals frequency during resume ==> everything is fine,

b) frequency differ ==> either we can't handle it, then panic (see flag
   CPUFREQ_PANIC_RESUME_OUTOFSYNC). Or we can handle it, then notify all

Documentation/cpu-freq/core.txt
drivers/cpufreq/cpufreq.c
include/linux/cpufreq.h

index 1ad90c39753c4f53aae445a8a01e367447b9cf14..29b3f9ffc66cda69ee9481bb9e0e1271295376fb 100644 (file)
@@ -92,3 +92,7 @@ values:
 cpu    - number of the affected CPU
 old    - old frequency
 new    - new frequency
+
+If the cpufreq core detects the frequency has changed while the system
+was suspended, these notifiers are called with CPUFREQ_RESUMECHANGE as
+second argument.
index 005c5267a2a15fc9ee3cc34913294faf533ffce6..bf9efc61c6bc997617dbbc5267cd8886b1d9a59d 100644 (file)
@@ -33,8 +33,10 @@ static struct cpufreq_driver         *cpufreq_driver;
 static struct cpufreq_policy   *cpufreq_cpu_data[NR_CPUS];
 static spinlock_t              cpufreq_driver_lock = SPIN_LOCK_UNLOCKED;
 
-/* internal prototype */
+/* internal prototypes */
 static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
+static void handle_update(void *data);
+static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci);
 
 
 /**
@@ -362,7 +364,6 @@ static struct kobj_type ktype_cpufreq = {
        .release        = cpufreq_sysfs_release,
 };
 
-static void handle_update(void *data);
 
 /**
  * cpufreq_add_dev - add a CPU device
@@ -576,10 +577,11 @@ EXPORT_SYMBOL(cpufreq_get);
 
 
 /**
- *     cpufreq_resume - restore the CPU clock frequency after resume
+ *     cpufreq_resume -  restore proper CPU frequency handling after resume
  *
- *     Restore the CPU clock frequency so that our idea of the current
- *     frequency reflects the actual hardware.
+ *     1.) resume CPUfreq hardware support (cpufreq_driver->resume())
+ *     2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
+ *     3.) schedule call cpufreq_update_policy() ASAP as interrupts are restored.
  */
 static int cpufreq_resume(struct sys_device * sysdev)
 {
@@ -599,25 +601,37 @@ static int cpufreq_resume(struct sys_device * sysdev)
        if (!cpu_policy)
                return -EINVAL;
 
-       if (cpufreq_driver->resume)
-               ret = cpufreq_driver->resume(cpu_policy);
-       if (ret) {
-               printk(KERN_ERR "cpufreq: resume failed in ->resume step on CPU %u\n", cpu_policy->cpu);
-               goto out;
-       }
+       if (!cpufreq_driver->flags & CPUFREQ_CONST_LOOPS) {
+               unsigned int cur_freq = 0;
 
-       if (cpufreq_driver->setpolicy)
-               ret = cpufreq_driver->setpolicy(cpu_policy);
-       else
-               /* CPUFREQ_RELATION_H or CPUFREQ_RELATION_L have the same effect here, as cpu_policy->cur is known
-                * to be a valid and exact target frequency
-                */
-               ret = cpufreq_driver->target(cpu_policy, cpu_policy->cur, CPUFREQ_RELATION_H);
+               if (cpufreq_driver->get)
+                       cur_freq = cpufreq_driver->get(cpu_policy->cpu);
 
-       if (ret)
-               printk(KERN_ERR "cpufreq: resume failed in ->setpolicy/target step on CPU %u\n", cpu_policy->cpu);
+               if (!cur_freq || !cpu_policy->cur) {
+                       printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n");
+                       goto out;
+               }
+
+               if (unlikely(cur_freq != cpu_policy->cur)) {
+                       struct cpufreq_freqs freqs;
+
+                       if (cpufreq_driver->flags & CPUFREQ_PANIC_RESUME_OUTOFSYNC)
+                               panic("CPU Frequency is out of sync.");
+
+                       printk(KERN_WARNING "Warning: CPU frequency out of sync: cpufreq and timing"
+                              "core thinks of %u, is %u kHz.\n", cpu_policy->cur, cur_freq);
+
+                       freqs.cpu = cpu;
+                       freqs.old = cpu_policy->cur;
+                       freqs.new = cur_freq;
+
+                       notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs);
+                       adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
+               }
+       }
 
 out:
+       schedule_work(&cpu_policy->update);
        cpufreq_cpu_put(cpu_policy);
        return ret;
 }
@@ -1013,11 +1027,12 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
                l_p_j_ref_freq = ci->old;
        }
        if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
-           (val == CPUFREQ_POSTCHANGE && ci->old > ci->new))
+           (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
+           (val == CPUFREQ_RESUMECHANGE))
                loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
 }
 #else
-#define adjust_jiffies(x...) do {} while (0)
+static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; }
 #endif
 
 
@@ -1029,9 +1044,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
  */
 void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
 {
-       if (irqs_disabled())
-               return;   /* Only valid if we're in the resume process where
-                          * everyone knows what CPU frequency we are at */
+       BUG_ON(irqs_disabled());
 
        down_read(&cpufreq_notifier_rwsem);
        switch (state) {
index ff36c699ff79ae6034e45ff2ab648d5faede4a00..36f58874788f3074f3b9cd74135645a268747dfc 100644 (file)
@@ -100,6 +100,7 @@ struct cpufreq_policy {
 
 #define CPUFREQ_PRECHANGE      (0)
 #define CPUFREQ_POSTCHANGE     (1)
+#define CPUFREQ_RESUMECHANGE   (8)
 
 struct cpufreq_freqs {
        unsigned int cpu;       /* cpu nr */
@@ -210,6 +211,10 @@ struct cpufreq_driver {
 #define CPUFREQ_PANIC_OUTOFSYNC        0x04    /* panic if cpufreq's opinion of
                                         * current frequency differs from
                                         * actual frequency */
+#define CPUFREQ_PANIC_RESUME_OUTOFSYNC 0x08 /* panic if cpufreq's opinion of
+                                        * current frequency differs from
+                                        * actual frequency on resume
+                                        * from sleep. */
 
 
 int cpufreq_register_driver(struct cpufreq_driver *driver_data);