cpufreq: Use syscore_ops for boot CPU suspend/resume (v2)

The cpufreq subsystem uses sysdev suspend and resume for
executing cpufreq_suspend() and cpufreq_resume(), respectively,
during system suspend, after interrupts have been switched off on the
boot CPU, and during system resume, while interrupts are still off on
the boot CPU.  In both cases the other CPUs are off-line at the
relevant point (either they have been switched off via CPU hotplug
during suspend, or they haven't been switched on yet during resume).
For this reason, although it may seem that cpufreq_suspend() and
cpufreq_resume() are executed for all CPUs in the system, they are
only called for the boot CPU in fact, which is quite confusing.

To remove the confusion and to prepare for elimiating sysdev
suspend and resume operations from the kernel enirely, convernt
cpufreq to using a struct syscore_ops object for the boot CPU
suspend and resume and rename the callbacks so that their names
reflect their purpose.  In addition, put some explanatory remarks
into their kerneldoc comments.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
This commit is contained in:
Rafael J. Wysocki 2011-03-23 22:16:32 +01:00
parent fb3600cc50
commit e00e56dfd3

View file

@ -28,6 +28,7 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/syscore_ops.h>
#include <trace/events/power.h> #include <trace/events/power.h>
@ -1340,35 +1341,31 @@ unsigned int cpufreq_get(unsigned int cpu)
} }
EXPORT_SYMBOL(cpufreq_get); EXPORT_SYMBOL(cpufreq_get);
static struct sysdev_driver cpufreq_sysdev_driver = {
.add = cpufreq_add_dev,
.remove = cpufreq_remove_dev,
};
/** /**
* cpufreq_suspend - let the low level driver prepare for suspend * cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
*
* This function is only executed for the boot processor. The other CPUs
* have been put offline by means of CPU hotplug.
*/ */
static int cpufreq_bp_suspend(void)
static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg)
{ {
int ret = 0; int ret = 0;
int cpu = sysdev->id; int cpu = smp_processor_id();
struct cpufreq_policy *cpu_policy; struct cpufreq_policy *cpu_policy;
dprintk("suspending cpu %u\n", cpu); dprintk("suspending cpu %u\n", cpu);
if (!cpu_online(cpu)) /* If there's no policy for the boot CPU, we have nothing to do. */
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); cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) if (!cpu_policy)
return -EINVAL; return 0;
/* only handle each CPU group once */
if (unlikely(cpu_policy->cpu != cpu))
goto out;
if (cpufreq_driver->suspend) { if (cpufreq_driver->suspend) {
ret = cpufreq_driver->suspend(cpu_policy); ret = cpufreq_driver->suspend(cpu_policy);
@ -1377,13 +1374,12 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg)
"step on CPU %u\n", cpu_policy->cpu); "step on CPU %u\n", cpu_policy->cpu);
} }
out:
cpufreq_cpu_put(cpu_policy); cpufreq_cpu_put(cpu_policy);
return ret; return ret;
} }
/** /**
* cpufreq_resume - restore proper CPU frequency handling after resume * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU.
* *
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) * 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
* 2.) schedule call cpufreq_update_policy() ASAP as interrupts are * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are
@ -1391,31 +1387,23 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg)
* what we believe it to be. This is a bit later than when it * what we believe it to be. This is a bit later than when it
* should be, but nonethteless it's better than calling * should be, but nonethteless it's better than calling
* cpufreq_driver->get() here which might re-enable interrupts... * cpufreq_driver->get() here which might re-enable interrupts...
*
* This function is only executed for the boot CPU. The other CPUs have not
* been turned on yet.
*/ */
static int cpufreq_resume(struct sys_device *sysdev) static void cpufreq_bp_resume(void)
{ {
int ret = 0; int ret = 0;
int cpu = sysdev->id; int cpu = smp_processor_id();
struct cpufreq_policy *cpu_policy; struct cpufreq_policy *cpu_policy;
dprintk("resuming cpu %u\n", cpu); dprintk("resuming cpu %u\n", cpu);
if (!cpu_online(cpu)) /* If there's no policy for the boot CPU, we have nothing to do. */
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); cpu_policy = cpufreq_cpu_get(cpu);
if (!cpu_policy) if (!cpu_policy)
return -EINVAL; return;
/* only handle each CPU group once */
if (unlikely(cpu_policy->cpu != cpu))
goto fail;
if (cpufreq_driver->resume) { if (cpufreq_driver->resume) {
ret = cpufreq_driver->resume(cpu_policy); ret = cpufreq_driver->resume(cpu_policy);
@ -1430,14 +1418,11 @@ static int cpufreq_resume(struct sys_device *sysdev)
fail: fail:
cpufreq_cpu_put(cpu_policy); cpufreq_cpu_put(cpu_policy);
return ret;
} }
static struct sysdev_driver cpufreq_sysdev_driver = { static struct syscore_ops cpufreq_syscore_ops = {
.add = cpufreq_add_dev, .suspend = cpufreq_bp_suspend,
.remove = cpufreq_remove_dev, .resume = cpufreq_bp_resume,
.suspend = cpufreq_suspend,
.resume = cpufreq_resume,
}; };
@ -2002,6 +1987,7 @@ static int __init cpufreq_core_init(void)
cpufreq_global_kobject = kobject_create_and_add("cpufreq", cpufreq_global_kobject = kobject_create_and_add("cpufreq",
&cpu_sysdev_class.kset.kobj); &cpu_sysdev_class.kset.kobj);
BUG_ON(!cpufreq_global_kobject); BUG_ON(!cpufreq_global_kobject);
register_syscore_ops(&cpufreq_syscore_ops);
return 0; return 0;
} }