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

Commit 00e8b261 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-timers'

* 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
parents 5698bd75 feb70af0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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))
@@ -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);
+91 −39
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -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;
@@ -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);
@@ -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)
{
@@ -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)
{
@@ -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.
@@ -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;

@@ -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;

@@ -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);
@@ -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);
}

/**
@@ -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);
}

/**
@@ -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) {
@@ -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);
}

/**
@@ -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
@@ -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.
+30 −5
Original line number Diff line number Diff line
@@ -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;
}

/**
@@ -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);
@@ -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;
}
@@ -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);
@@ -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;
}
@@ -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);

@@ -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);
@@ -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) {
@@ -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);
@@ -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);
@@ -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) {
@@ -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);
+29 −7
Original line number Diff line number Diff line
#include <linux/pm_qos.h>

static inline void device_pm_init_common(struct device *dev)
{
	if (!dev->power.early_init) {
		spin_lock_init(&dev->power.lock);
		dev->power.power_state = PMSG_INVALID;
		dev->power.early_init = true;
	}
}

#ifdef CONFIG_PM_RUNTIME

static inline void pm_runtime_early_init(struct device *dev)
{
	dev->power.disable_depth = 1;
	device_pm_init_common(dev);
}

extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_remove(struct device *dev);

#else /* !CONFIG_PM_RUNTIME */

static inline void pm_runtime_early_init(struct device *dev)
{
	device_pm_init_common(dev);
}

static inline void pm_runtime_init(struct device *dev) {}
static inline void pm_runtime_remove(struct device *dev) {}

@@ -25,7 +45,7 @@ static inline struct device *to_device(struct list_head *entry)
	return container_of(entry, struct device, power.entry);
}

extern void device_pm_init(struct device *dev);
extern void device_pm_sleep_init(struct device *dev);
extern void device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
extern void device_pm_move_before(struct device *, struct device *);
@@ -34,12 +54,7 @@ extern void device_pm_move_last(struct device *);

#else /* !CONFIG_PM_SLEEP */

static inline void device_pm_init(struct device *dev)
{
	spin_lock_init(&dev->power.lock);
	dev->power.power_state = PMSG_INVALID;
	pm_runtime_init(dev);
}
static inline void device_pm_sleep_init(struct device *dev) {}

static inline void device_pm_add(struct device *dev)
{
@@ -60,6 +75,13 @@ static inline void device_pm_move_last(struct device *dev) {}

#endif /* !CONFIG_PM_SLEEP */

static inline void device_pm_init(struct device *dev)
{
	device_pm_init_common(dev);
	device_pm_sleep_init(dev);
	pm_runtime_init(dev);
}

#ifdef CONFIG_PM

/*
+63 −8
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>

struct sh_cmt_priv {
	void __iomem *mapbase;
@@ -52,6 +53,7 @@ struct sh_cmt_priv {
	struct clock_event_device ced;
	struct clocksource cs;
	unsigned long total_cycles;
	bool cs_enabled;
};

static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
@@ -155,6 +157,9 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
{
	int k, ret;

	pm_runtime_get_sync(&p->pdev->dev);
	dev_pm_syscore_device(&p->pdev->dev, true);

	/* enable clock */
	ret = clk_enable(p->clk);
	if (ret) {
@@ -221,6 +226,9 @@ static void sh_cmt_disable(struct sh_cmt_priv *p)

	/* stop clock */
	clk_disable(p->clk);

	dev_pm_syscore_device(&p->pdev->dev, false);
	pm_runtime_put(&p->pdev->dev);
}

/* private flags */
@@ -451,22 +459,42 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs)
	int ret;
	struct sh_cmt_priv *p = cs_to_sh_cmt(cs);

	WARN_ON(p->cs_enabled);

	p->total_cycles = 0;

	ret = sh_cmt_start(p, FLAG_CLOCKSOURCE);
	if (!ret)
	if (!ret) {
		__clocksource_updatefreq_hz(cs, p->rate);
		p->cs_enabled = true;
	}
	return ret;
}

static void sh_cmt_clocksource_disable(struct clocksource *cs)
{
	sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
	struct sh_cmt_priv *p = cs_to_sh_cmt(cs);

	WARN_ON(!p->cs_enabled);

	sh_cmt_stop(p, FLAG_CLOCKSOURCE);
	p->cs_enabled = false;
}

static void sh_cmt_clocksource_suspend(struct clocksource *cs)
{
	struct sh_cmt_priv *p = cs_to_sh_cmt(cs);

	sh_cmt_stop(p, FLAG_CLOCKSOURCE);
	pm_genpd_syscore_poweroff(&p->pdev->dev);
}

static void sh_cmt_clocksource_resume(struct clocksource *cs)
{
	sh_cmt_start(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
	struct sh_cmt_priv *p = cs_to_sh_cmt(cs);

	pm_genpd_syscore_poweron(&p->pdev->dev);
	sh_cmt_start(p, FLAG_CLOCKSOURCE);
}

static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
@@ -479,7 +507,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
	cs->read = sh_cmt_clocksource_read;
	cs->enable = sh_cmt_clocksource_enable;
	cs->disable = sh_cmt_clocksource_disable;
	cs->suspend = sh_cmt_clocksource_disable;
	cs->suspend = sh_cmt_clocksource_suspend;
	cs->resume = sh_cmt_clocksource_resume;
	cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
	cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
@@ -562,6 +590,16 @@ static int sh_cmt_clock_event_next(unsigned long delta,
	return 0;
}

static void sh_cmt_clock_event_suspend(struct clock_event_device *ced)
{
	pm_genpd_syscore_poweroff(&ced_to_sh_cmt(ced)->pdev->dev);
}

static void sh_cmt_clock_event_resume(struct clock_event_device *ced)
{
	pm_genpd_syscore_poweron(&ced_to_sh_cmt(ced)->pdev->dev);
}

static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
				       char *name, unsigned long rating)
{
@@ -576,6 +614,8 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
	ced->cpumask = cpumask_of(0);
	ced->set_next_event = sh_cmt_clock_event_next;
	ced->set_mode = sh_cmt_clock_event_mode;
	ced->suspend = sh_cmt_clock_event_suspend;
	ced->resume = sh_cmt_clock_event_resume;

	dev_info(&p->pdev->dev, "used for clock events\n");
	clockevents_register_device(ced);
@@ -670,6 +710,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
		dev_err(&p->pdev->dev, "registration failed\n");
		goto err1;
	}
	p->cs_enabled = false;

	ret = setup_irq(irq, &p->irqaction);
	if (ret) {
@@ -688,14 +729,17 @@ err0:
static int __devinit sh_cmt_probe(struct platform_device *pdev)
{
	struct sh_cmt_priv *p = platform_get_drvdata(pdev);
	struct sh_timer_config *cfg = pdev->dev.platform_data;
	int ret;

	if (!is_early_platform_device(pdev))
		pm_genpd_dev_always_on(&pdev->dev, true);
	if (!is_early_platform_device(pdev)) {
		pm_runtime_set_active(&pdev->dev);
		pm_runtime_enable(&pdev->dev);
	}

	if (p) {
		dev_info(&pdev->dev, "kept as earlytimer\n");
		return 0;
		goto out;
	}

	p = kmalloc(sizeof(*p), GFP_KERNEL);
@@ -708,9 +752,20 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev)
	if (ret) {
		kfree(p);
		platform_set_drvdata(pdev, NULL);
	}
		pm_runtime_idle(&pdev->dev);
		return ret;
	}
	if (is_early_platform_device(pdev))
		return 0;

 out:
	if (cfg->clockevent_rating || cfg->clocksource_rating)
		pm_runtime_irq_safe(&pdev->dev);
	else
		pm_runtime_idle(&pdev->dev);

	return 0;
}

static int __devexit sh_cmt_remove(struct platform_device *pdev)
{
Loading