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);
/**
.release = cpufreq_sysfs_release,
};
-static void handle_update(void *data);
/**
* cpufreq_add_dev - add a CPU device
/**
- * 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)
{
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;
}
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
*/
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) {