Skip to content

Commit

Permalink
Merge branch 'pm-timers'
Browse files Browse the repository at this point in the history
* pm-timers:
  PM: Do not use the syscore flag for runtime PM
  sh: MTU2: Basic runtime PM support
  sh: CMT: Basic runtime PM support
  sh: TMU: Basic runtime PM support
  PM / Domains: Do not measure start time for "irq safe" devices
  PM / Domains: Move syscore flag from subsys data to struct device
  PM / Domains: Rename the always_on device flag to syscore
  PM / Runtime: Allow helpers to be called by early platform drivers
  PM: Reorganize device PM initialization
  sh: MTU2: Introduce clock events suspend/resume routines
  sh: CMT: Introduce clocksource/clock events suspend/resume routines
  sh: TMU: Introduce clocksource/clock events suspend/resume routines
  timekeeping: Add suspend and resume of clock event devices
  PM / Domains: Add power off/on function for system core suspend stage
  PM / Domains: Introduce simplified power on routine for system resume
  • Loading branch information
rjwysocki committed Sep 17, 2012
2 parents 5698bd7 + feb70af commit 00e8b26
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 74 deletions.
2 changes: 2 additions & 0 deletions drivers/base/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/pm_runtime.h>

#include "base.h"
#include "power/power.h"

#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
driver))
Expand Down Expand Up @@ -948,6 +949,7 @@ void __init early_platform_add_devices(struct platform_device **devs, int num)
dev = &devs[i]->dev;

if (!dev->devres_head.next) {
pm_runtime_early_init(dev);
INIT_LIST_HEAD(&dev->devres_head);
list_add_tail(&dev->devres_head,
&early_platform_device_list);
Expand Down
130 changes: 91 additions & 39 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
start_latency_ns, "start");
}

static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, int, start, dev);
}

static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
{
bool ret = false;
Expand Down Expand Up @@ -436,7 +442,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
not_suspended = 0;
list_for_each_entry(pdd, &genpd->dev_list, list_node)
if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
|| pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on))
|| pdd->dev->power.irq_safe))
not_suspended++;

if (not_suspended > genpd->in_progress)
Expand Down Expand Up @@ -578,9 +584,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)

might_sleep_if(!genpd->dev_irq_safe);

if (dev_gpd_data(dev)->always_on)
return -EBUSY;

stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
if (stop_ok && !stop_ok(dev))
return -EBUSY;
Expand Down Expand Up @@ -629,7 +632,7 @@ static int pm_genpd_runtime_resume(struct device *dev)

/* If power.irq_safe, the PM domain is never powered off. */
if (dev->power.irq_safe)
return genpd_start_dev(genpd, dev);
return genpd_start_dev_no_timing(genpd, dev);

mutex_lock(&genpd->lock);
ret = __pm_genpd_poweron(genpd);
Expand Down Expand Up @@ -697,6 +700,24 @@ static inline void genpd_power_off_work_fn(struct work_struct *work) {}

#ifdef CONFIG_PM_SLEEP

/**
* pm_genpd_present - Check if the given PM domain has been initialized.
* @genpd: PM domain to check.
*/
static bool pm_genpd_present(struct generic_pm_domain *genpd)
{
struct generic_pm_domain *gpd;

if (IS_ERR_OR_NULL(genpd))
return false;

list_for_each_entry(gpd, &gpd_list, gpd_list_node)
if (gpd == genpd)
return true;

return false;
}

static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
struct device *dev)
{
Expand Down Expand Up @@ -750,9 +771,10 @@ static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev)
* Check if the given PM domain can be powered off (during system suspend or
* hibernation) and do that if so. Also, in that case propagate to its masters.
*
* This function is only called in "noirq" stages of system power transitions,
* so it need not acquire locks (all of the "noirq" callbacks are executed
* sequentially, so it is guaranteed that it will never run twice in parallel).
* This function is only called in "noirq" and "syscore" stages of system power
* transitions, so it need not acquire locks (all of the "noirq" callbacks are
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
{
Expand All @@ -776,6 +798,33 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
}
}

/**
* pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
* @genpd: PM domain to power on.
*
* This function is only called in "noirq" and "syscore" stages of system power
* transitions, so it need not acquire locks (all of the "noirq" callbacks are
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
{
struct gpd_link *link;

if (genpd->status != GPD_STATE_POWER_OFF)
return;

list_for_each_entry(link, &genpd->slave_links, slave_node) {
pm_genpd_sync_poweron(link->master);
genpd_sd_counter_inc(link->master);
}

if (genpd->power_on)
genpd->power_on(genpd);

genpd->status = GPD_STATE_ACTIVE;
}

/**
* resume_needed - Check whether to resume a device before system suspend.
* @dev: Device to check.
Expand Down Expand Up @@ -937,7 +986,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
if (genpd->suspend_power_off
|| (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
return 0;

Expand Down Expand Up @@ -970,7 +1019,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
if (genpd->suspend_power_off
|| (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
return 0;

Expand All @@ -979,7 +1028,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
* guaranteed that this function will never run twice in parallel for
* the same PM domain, so it is not necessary to use locking here.
*/
pm_genpd_poweron(genpd);
pm_genpd_sync_poweron(genpd);
genpd->suspended_count--;

return genpd_start_dev(genpd, dev);
Expand Down Expand Up @@ -1090,8 +1139,7 @@ static int pm_genpd_freeze_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
0 : genpd_stop_dev(genpd, dev);
return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
}

/**
Expand All @@ -1111,8 +1159,7 @@ static int pm_genpd_thaw_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
0 : genpd_start_dev(genpd, dev);
return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
}

/**
Expand Down Expand Up @@ -1186,8 +1233,8 @@ static int pm_genpd_restore_noirq(struct device *dev)
if (genpd->suspended_count++ == 0) {
/*
* The boot kernel might put the domain into arbitrary state,
* so make it appear as powered off to pm_genpd_poweron(), so
* that it tries to power it on in case it was really off.
* so make it appear as powered off to pm_genpd_sync_poweron(),
* so that it tries to power it on in case it was really off.
*/
genpd->status = GPD_STATE_POWER_OFF;
if (genpd->suspend_power_off) {
Expand All @@ -1205,9 +1252,9 @@ static int pm_genpd_restore_noirq(struct device *dev)
if (genpd->suspend_power_off)
return 0;

pm_genpd_poweron(genpd);
pm_genpd_sync_poweron(genpd);

return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev);
return genpd_start_dev(genpd, dev);
}

/**
Expand Down Expand Up @@ -1246,6 +1293,31 @@ static void pm_genpd_complete(struct device *dev)
}
}

/**
* pm_genpd_syscore_switch - Switch power during system core suspend or resume.
* @dev: Device that normally is marked as "always on" to switch power for.
*
* This routine may only be called during the system core (syscore) suspend or
* resume phase for devices whose "always on" flags are set.
*/
void pm_genpd_syscore_switch(struct device *dev, bool suspend)
{
struct generic_pm_domain *genpd;

genpd = dev_to_genpd(dev);
if (!pm_genpd_present(genpd))
return;

if (suspend) {
genpd->suspended_count++;
pm_genpd_sync_poweroff(genpd);
} else {
pm_genpd_sync_poweron(genpd);
genpd->suspended_count--;
}
}
EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch);

#else

#define pm_genpd_prepare NULL
Expand Down Expand Up @@ -1454,26 +1526,6 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
return ret;
}

/**
* pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device.
* @dev: Device to set/unset the flag for.
* @val: The new value of the device's "always on" flag.
*/
void pm_genpd_dev_always_on(struct device *dev, bool val)
{
struct pm_subsys_data *psd;
unsigned long flags;

spin_lock_irqsave(&dev->power.lock, flags);

psd = dev_to_psd(dev);
if (psd && psd->domain_data)
to_gpd_data(psd->domain_data)->always_on = val;

spin_unlock_irqrestore(&dev->power.lock, flags);
}
EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);

/**
* pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
* @dev: Device to set/unset the flag for.
Expand Down
35 changes: 30 additions & 5 deletions drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,17 @@ static pm_message_t pm_transition;
static int async_error;

/**
* device_pm_init - Initialize the PM-related part of a device object.
* device_pm_sleep_init - Initialize system suspend-related device fields.
* @dev: Device object being initialized.
*/
void device_pm_init(struct device *dev)
void device_pm_sleep_init(struct device *dev)
{
dev->power.is_prepared = false;
dev->power.is_suspended = false;
init_completion(&dev->power.completion);
complete_all(&dev->power.completion);
dev->power.wakeup = NULL;
spin_lock_init(&dev->power.lock);
pm_runtime_init(dev);
INIT_LIST_HEAD(&dev->power.entry);
dev->power.power_state = PMSG_INVALID;
}

/**
Expand Down Expand Up @@ -408,6 +405,9 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
TRACE_DEVICE(dev);
TRACE_RESUME(0);

if (dev->power.syscore)
goto Out;

if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
Expand All @@ -429,6 +429,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)

error = dpm_run_callback(callback, dev, state, info);

Out:
TRACE_RESUME(error);
return error;
}
Expand Down Expand Up @@ -486,6 +487,9 @@ static int device_resume_early(struct device *dev, pm_message_t state)
TRACE_DEVICE(dev);
TRACE_RESUME(0);

if (dev->power.syscore)
goto Out;

if (dev->pm_domain) {
info = "early power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
Expand All @@ -507,6 +511,7 @@ static int device_resume_early(struct device *dev, pm_message_t state)

error = dpm_run_callback(callback, dev, state, info);

Out:
TRACE_RESUME(error);
return error;
}
Expand Down Expand Up @@ -570,6 +575,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
TRACE_DEVICE(dev);
TRACE_RESUME(0);

if (dev->power.syscore)
goto Complete;

dpm_wait(dev->parent, async);
device_lock(dev);

Expand Down Expand Up @@ -632,6 +640,8 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)

Unlock:
device_unlock(dev);

Complete:
complete_all(&dev->power.completion);

TRACE_RESUME(error);
Expand Down Expand Up @@ -722,6 +732,9 @@ static void device_complete(struct device *dev, pm_message_t state)
void (*callback)(struct device *) = NULL;
char *info = NULL;

if (dev->power.syscore)
return;

device_lock(dev);

if (dev->pm_domain) {
Expand Down Expand Up @@ -834,6 +847,9 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
pm_callback_t callback = NULL;
char *info = NULL;

if (dev->power.syscore)
return 0;

if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
Expand Down Expand Up @@ -917,6 +933,9 @@ static int device_suspend_late(struct device *dev, pm_message_t state)
pm_callback_t callback = NULL;
char *info = NULL;

if (dev->power.syscore)
return 0;

if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
Expand Down Expand Up @@ -1053,6 +1072,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Complete;
}

if (dev->power.syscore)
goto Complete;

device_lock(dev);

if (dev->pm_domain) {
Expand Down Expand Up @@ -1209,6 +1231,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
char *info = NULL;
int error = 0;

if (dev->power.syscore)
return 0;

device_lock(dev);

dev->power.wakeup_path = device_may_wakeup(dev);
Expand Down
Loading

0 comments on commit 00e8b26

Please sign in to comment.