Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42d4dc3f authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds
Browse files

[PATCH] Add suspend method to cpufreq core



In order to properly fix some issues with cpufreq vs. sleep on
PowerBooks, I had to add a suspend callback to the pmac_cpufreq driver.
I must force a switch to full speed before sleep and I switch back to
previous speed on resume.

I also added a driver flag to disable the warnings in suspend/resume
since it is expected in this case to have different speed (and I want it
to fixup the jiffies properly).

Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c60c3906
Loading
Loading
Loading
Loading
+89 −5
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
	}
	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
	    (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
	    (val == CPUFREQ_RESUMECHANGE)) {
	    (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
		dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
	}
@@ -865,12 +865,91 @@ unsigned int cpufreq_get(unsigned int cpu)
EXPORT_SYMBOL(cpufreq_get);


/**
 *	cpufreq_suspend - let the low level driver prepare for suspend
 */

static int cpufreq_suspend(struct sys_device * sysdev, u32 state)
{
	int cpu = sysdev->id;
	unsigned int ret = 0;
	unsigned int cur_freq = 0;
	struct cpufreq_policy *cpu_policy;

	dprintk("resuming cpu %u\n", cpu);

	if (!cpu_online(cpu))
		return 0;

	/* we may be lax here as interrupts are off. Nonetheless
	 * we need to grab the correct cpu policy, as to check
	 * whether we really run on this CPU.
	 */

	cpu_policy = cpufreq_cpu_get(cpu);
	if (!cpu_policy)
		return -EINVAL;

	/* only handle each CPU group once */
	if (unlikely(cpu_policy->cpu != cpu)) {
		cpufreq_cpu_put(cpu_policy);
		return 0;
	}

	if (cpufreq_driver->suspend) {
		ret = cpufreq_driver->suspend(cpu_policy, state);
		if (ret) {
			printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
					"step on CPU %u\n", cpu_policy->cpu);
			cpufreq_cpu_put(cpu_policy);
			return ret;
		}
	}


	if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)
		goto out;

	if (cpufreq_driver->get)
		cur_freq = cpufreq_driver->get(cpu_policy->cpu);

	if (!cur_freq || !cpu_policy->cur) {
		printk(KERN_ERR "cpufreq: suspend 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_PM_NO_WARN))
			printk(KERN_DEBUG "Warning: CPU frequency is %u, "
			       "cpufreq assumed %u kHz.\n",
			       cur_freq, cpu_policy->cur);

		freqs.cpu = cpu;
		freqs.old = cpu_policy->cur;
		freqs.new = cur_freq;

		notifier_call_chain(&cpufreq_transition_notifier_list,
				    CPUFREQ_SUSPENDCHANGE, &freqs);
		adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs);

		cpu_policy->cur = cur_freq;
	}

 out:
	cpufreq_cpu_put(cpu_policy);
	return 0;
}

/**
 *	cpufreq_resume -  restore proper CPU frequency handling after resume
 *
 *	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.
 *	3.) schedule call cpufreq_update_policy() ASAP as interrupts are
 *	    restored.
 */
static int cpufreq_resume(struct sys_device * sysdev)
{
@@ -915,7 +994,9 @@ static int cpufreq_resume(struct sys_device * sysdev)
			cur_freq = cpufreq_driver->get(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");
			printk(KERN_ERR "cpufreq: resume failed to assert "
					"current frequency is what timing core "
					"thinks it is.\n");
			goto out;
		}

@@ -923,13 +1004,15 @@ static int cpufreq_resume(struct sys_device * sysdev)
			struct cpufreq_freqs freqs;

			printk(KERN_WARNING "Warning: CPU frequency is %u, "
			       "cpufreq assumed %u kHz.\n", cur_freq, cpu_policy->cur);
					"cpufreq assumed %u kHz.\n",
					cur_freq, cpu_policy->cur);

			freqs.cpu = cpu;
			freqs.old = cpu_policy->cur;
			freqs.new = cur_freq;

			notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs);
			notifier_call_chain(&cpufreq_transition_notifier_list,
					CPUFREQ_RESUMECHANGE, &freqs);
			adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);

			cpu_policy->cur = cur_freq;
@@ -945,6 +1028,7 @@ static int cpufreq_resume(struct sys_device * sysdev)
static struct sysdev_driver cpufreq_sysdev_driver = {
	.add		= cpufreq_add_dev,
	.remove		= cpufreq_remove_dev,
	.suspend	= cpufreq_suspend,
	.resume		= cpufreq_resume,
};

+4 −1
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ struct cpufreq_policy {
#define CPUFREQ_PRECHANGE	(0)
#define CPUFREQ_POSTCHANGE	(1)
#define CPUFREQ_RESUMECHANGE	(8)
#define CPUFREQ_SUSPENDCHANGE	(9)

struct cpufreq_freqs {
	unsigned int cpu;	/* cpu nr */
@@ -200,6 +201,7 @@ struct cpufreq_driver {

	/* optional */
	int	(*exit)		(struct cpufreq_policy *policy);
	int	(*suspend)	(struct cpufreq_policy *policy, u32 state);
	int	(*resume)	(struct cpufreq_policy *policy);
	struct freq_attr	**attr;
};
@@ -211,7 +213,8 @@ struct cpufreq_driver {
#define CPUFREQ_CONST_LOOPS 	0x02	/* loops_per_jiffy or other kernel
					 * "constants" aren't affected by
					 * frequency transitions */

#define CPUFREQ_PM_NO_WARN	0x04	/* don't warn on suspend/resume speed
					 * mismatches */

int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);