forked from mirrors/linux
		
	[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: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
		
							parent
							
								
									c60c390620
								
							
						
					
					
						commit
						42d4dc3f4e
					
				
					 2 changed files with 93 additions and 6 deletions
				
			
		| 
						 | 
					@ -223,7 +223,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
 | 
						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)) {
 | 
						    (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
 | 
				
			||||||
		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
 | 
							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);
 | 
							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);
 | 
					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
 | 
					 *	cpufreq_resume -  restore proper CPU frequency handling after resume
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *	1.) resume CPUfreq hardware support (cpufreq_driver->resume())
 | 
					 *	1.) resume CPUfreq hardware support (cpufreq_driver->resume())
 | 
				
			||||||
 *	2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
 | 
					 *	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)
 | 
					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);
 | 
								cur_freq = cpufreq_driver->get(cpu_policy->cpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!cur_freq || !cpu_policy->cur) {
 | 
							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;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -923,13 +1004,15 @@ static int cpufreq_resume(struct sys_device * sysdev)
 | 
				
			||||||
			struct cpufreq_freqs freqs;
 | 
								struct cpufreq_freqs freqs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			printk(KERN_WARNING "Warning: CPU frequency is %u, "
 | 
								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.cpu = cpu;
 | 
				
			||||||
			freqs.old = cpu_policy->cur;
 | 
								freqs.old = cpu_policy->cur;
 | 
				
			||||||
			freqs.new = cur_freq;
 | 
								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);
 | 
								adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cpu_policy->cur = cur_freq;
 | 
								cpu_policy->cur = cur_freq;
 | 
				
			||||||
| 
						 | 
					@ -945,6 +1028,7 @@ static int cpufreq_resume(struct sys_device * sysdev)
 | 
				
			||||||
static struct sysdev_driver cpufreq_sysdev_driver = {
 | 
					static struct sysdev_driver cpufreq_sysdev_driver = {
 | 
				
			||||||
	.add		= cpufreq_add_dev,
 | 
						.add		= cpufreq_add_dev,
 | 
				
			||||||
	.remove		= cpufreq_remove_dev,
 | 
						.remove		= cpufreq_remove_dev,
 | 
				
			||||||
 | 
						.suspend	= cpufreq_suspend,
 | 
				
			||||||
	.resume		= cpufreq_resume,
 | 
						.resume		= cpufreq_resume,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +103,7 @@ struct cpufreq_policy {
 | 
				
			||||||
#define CPUFREQ_PRECHANGE	(0)
 | 
					#define CPUFREQ_PRECHANGE	(0)
 | 
				
			||||||
#define CPUFREQ_POSTCHANGE	(1)
 | 
					#define CPUFREQ_POSTCHANGE	(1)
 | 
				
			||||||
#define CPUFREQ_RESUMECHANGE	(8)
 | 
					#define CPUFREQ_RESUMECHANGE	(8)
 | 
				
			||||||
 | 
					#define CPUFREQ_SUSPENDCHANGE	(9)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cpufreq_freqs {
 | 
					struct cpufreq_freqs {
 | 
				
			||||||
	unsigned int cpu;	/* cpu nr */
 | 
						unsigned int cpu;	/* cpu nr */
 | 
				
			||||||
| 
						 | 
					@ -200,6 +201,7 @@ struct cpufreq_driver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* optional */
 | 
						/* optional */
 | 
				
			||||||
	int	(*exit)		(struct cpufreq_policy *policy);
 | 
						int	(*exit)		(struct cpufreq_policy *policy);
 | 
				
			||||||
 | 
						int	(*suspend)	(struct cpufreq_policy *policy, u32 state);
 | 
				
			||||||
	int	(*resume)	(struct cpufreq_policy *policy);
 | 
						int	(*resume)	(struct cpufreq_policy *policy);
 | 
				
			||||||
	struct freq_attr	**attr;
 | 
						struct freq_attr	**attr;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -211,7 +213,8 @@ struct cpufreq_driver {
 | 
				
			||||||
#define CPUFREQ_CONST_LOOPS 	0x02	/* loops_per_jiffy or other kernel
 | 
					#define CPUFREQ_CONST_LOOPS 	0x02	/* loops_per_jiffy or other kernel
 | 
				
			||||||
					 * "constants" aren't affected by
 | 
										 * "constants" aren't affected by
 | 
				
			||||||
					 * frequency transitions */
 | 
										 * 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_register_driver(struct cpufreq_driver *driver_data);
 | 
				
			||||||
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
 | 
					int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue