#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
++#include <linux/pm_clock.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/sched.h>
genpd->cpuidle_data->idle_state->exit_latency = usecs64;
}
++static int genpd_power_on(struct generic_pm_domain *genpd)
++{
++ ktime_t time_start;
++ s64 elapsed_ns;
++ int ret;
++
++ if (!genpd->power_on)
++ return 0;
++
++ time_start = ktime_get();
++ ret = genpd->power_on(genpd);
++ if (ret)
++ return ret;
++
++ elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
++ if (elapsed_ns <= genpd->power_on_latency_ns)
++ return ret;
++
++ genpd->power_on_latency_ns = elapsed_ns;
++ genpd->max_off_time_changed = true;
++ genpd_recalc_cpu_exit_latency(genpd);
++ pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n",
++ genpd->name, "on", elapsed_ns);
++
++ return ret;
++}
++
++static int genpd_power_off(struct generic_pm_domain *genpd)
++{
++ ktime_t time_start;
++ s64 elapsed_ns;
++ int ret;
++
++ if (!genpd->power_off)
++ return 0;
++
++ time_start = ktime_get();
++ ret = genpd->power_off(genpd);
++ if (ret == -EBUSY)
++ return ret;
++
++ elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
++ if (elapsed_ns <= genpd->power_off_latency_ns)
++ return ret;
++
++ genpd->power_off_latency_ns = elapsed_ns;
++ genpd->max_off_time_changed = true;
++ pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n",
++ genpd->name, "off", elapsed_ns);
++
++ return ret;
++}
++
/**
* __pm_genpd_poweron - Restore power to a given PM domain and its masters.
* @genpd: PM domain to power up.
}
}
-- if (genpd->power_on) {
-- ktime_t time_start = ktime_get();
-- s64 elapsed_ns;
--
-- ret = genpd->power_on(genpd);
-- if (ret)
-- goto err;
--
-- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
-- if (elapsed_ns > genpd->power_on_latency_ns) {
-- genpd->power_on_latency_ns = elapsed_ns;
-- genpd->max_off_time_changed = true;
-- genpd_recalc_cpu_exit_latency(genpd);
-- if (genpd->name)
-- pr_warning("%s: Power-on latency exceeded, "
-- "new value %lld ns\n", genpd->name,
-- elapsed_ns);
-- }
-- }
++ ret = genpd_power_on(genpd);
++ if (ret)
++ goto err;
out:
genpd_set_active(genpd);
return genpd ? pm_genpd_poweron(genpd) : -EINVAL;
}
---#ifdef CONFIG_PM_RUNTIME
---
static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd,
struct device *dev)
{
struct device *dev = pdd->dev;
int ret = 0;
- if (gpd_data->need_restore)
+ if (gpd_data->need_restore > 0)
return 0;
+ /*
+ * If the value of the need_restore flag is still unknown at this point,
+ * we trust that pm_genpd_poweroff() has verified that the device is
+ * already runtime PM suspended.
+ */
+ if (gpd_data->need_restore < 0) {
+ gpd_data->need_restore = 1;
+ return 0;
+ }
+
mutex_unlock(&genpd->lock);
genpd_start_dev(genpd, dev);
mutex_lock(&genpd->lock);
if (!ret)
- gpd_data->need_restore = true;
+ gpd_data->need_restore = 1;
return ret;
}
{
struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
struct device *dev = pdd->dev;
- bool need_restore = gpd_data->need_restore;
+ int need_restore = gpd_data->need_restore;
- gpd_data->need_restore = false;
+ gpd_data->need_restore = 0;
mutex_unlock(&genpd->lock);
genpd_start_dev(genpd, dev);
+
+ /*
+ * Call genpd_restore_dev() for recently added devices too (need_restore
+ * is negative then).
+ */
if (need_restore)
genpd_restore_dev(genpd, dev);
}
if (genpd->power_off) {
-- ktime_t time_start;
-- s64 elapsed_ns;
--
if (atomic_read(&genpd->sd_count) > 0) {
ret = -EBUSY;
goto out;
}
-- time_start = ktime_get();
--
/*
* If sd_count > 0 at this point, one of the subdomains hasn't
* managed to call pm_genpd_poweron() for the master yet after
* the pm_genpd_poweron() restore power for us (this shouldn't
* happen very often).
*/
-- ret = genpd->power_off(genpd);
++ ret = genpd_power_off(genpd);
if (ret == -EBUSY) {
genpd_set_active(genpd);
goto out;
}
--
-- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
-- if (elapsed_ns > genpd->power_off_latency_ns) {
-- genpd->power_off_latency_ns = elapsed_ns;
-- genpd->max_off_time_changed = true;
-- if (genpd->name)
-- pr_warning("%s: Power-off latency exceeded, "
-- "new value %lld ns\n", genpd->name,
-- elapsed_ns);
-- }
}
genpd->status = GPD_STATE_POWER_OFF;
static int pm_genpd_runtime_suspend(struct device *dev)
{
struct generic_pm_domain *genpd;
+ struct generic_pm_domain_data *gpd_data;
bool (*stop_ok)(struct device *__dev);
int ret;
return 0;
mutex_lock(&genpd->lock);
+
+ /*
+ * If we have an unknown state of the need_restore flag, it means none
+ * of the runtime PM callbacks has been invoked yet. Let's update the
+ * flag to reflect that the current state is active.
+ */
+ gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
+ if (gpd_data->need_restore < 0)
+ gpd_data->need_restore = 0;
+
genpd->in_progress++;
pm_genpd_poweroff(genpd);
genpd->in_progress--;
}
late_initcall(genpd_poweroff_unused);
---#else
---
---static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
--- unsigned long val, void *ptr)
---{
--- return NOTIFY_DONE;
---}
---
---static inline void
---genpd_queue_power_off_work(struct generic_pm_domain *genpd) {}
---
---static inline void genpd_power_off_work_fn(struct work_struct *work) {}
---
---#define pm_genpd_runtime_suspend NULL
---#define pm_genpd_runtime_resume NULL
---
---#endif /* CONFIG_PM_RUNTIME */
---
#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)
++static bool pm_genpd_present(const struct generic_pm_domain *genpd)
{
-- struct generic_pm_domain *gpd;
++ const struct generic_pm_domain *gpd;
if (IS_ERR_OR_NULL(genpd))
return false;
|| atomic_read(&genpd->sd_count) > 0)
return;
-- if (genpd->power_off)
-- genpd->power_off(genpd);
++ genpd_power_off(genpd);
genpd->status = GPD_STATE_POWER_OFF;
genpd_sd_counter_inc(link->master);
}
-- if (genpd->power_on)
-- genpd->power_on(genpd);
++ genpd_power_on(genpd);
genpd->status = GPD_STATE_ACTIVE;
}
* If the domain was off before the hibernation, make
* sure it will be off going forward.
*/
-- if (genpd->power_off)
-- genpd->power_off(genpd);
++ genpd_power_off(genpd);
return 0;
}
}
EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
---#else
+++#else /* !CONFIG_PM_SLEEP */
#define pm_genpd_prepare NULL
#define pm_genpd_suspend NULL
spin_unlock_irq(&dev->power.lock);
if (genpd->attach_dev)
- genpd->attach_dev(dev);
+ genpd->attach_dev(genpd, dev);
mutex_lock(&gpd_data->lock);
gpd_data->base.dev = dev;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
- gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
+ gpd_data->need_restore = -1;
gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = -1;
mutex_unlock(&gpd_data->lock);
genpd->max_off_time_changed = true;
if (genpd->detach_dev)
- genpd->detach_dev(dev);
+ genpd->detach_dev(genpd, dev);
spin_lock_irq(&dev->power.lock);
psd = dev_to_psd(dev);
if (psd && psd->domain_data)
- to_gpd_data(psd->domain_data)->need_restore = val;
+ to_gpd_data(psd->domain_data)->need_restore = val ? 1 : 0;
spin_unlock_irqrestore(&dev->power.lock, flags);
}
genpd->domain.ops.complete = pm_genpd_complete;
genpd->dev_ops.save_state = pm_genpd_default_save_state;
genpd->dev_ops.restore_state = pm_genpd_default_restore_state;
++
++ if (genpd->flags & GENPD_FLAG_PM_CLK) {
++ genpd->dev_ops.stop = pm_clk_suspend;
++ genpd->dev_ops.start = pm_clk_resume;
++ }
++
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);
* Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
* on failure.
*/
- --static struct generic_pm_domain *of_genpd_get_from_provider(
+ ++struct generic_pm_domain *of_genpd_get_from_provider(
struct of_phandle_args *genpdspec)
{
struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
return genpd;
}
+ ++EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
/**
* genpd_dev_pm_detach - Detach a device from its PM domain.
}
dev->pm_domain->detach = genpd_dev_pm_detach;
++ pm_genpd_poweron(pd);
return 0;
}
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
---#endif
+++#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
/*** debugfs support ***/
/*
* TODO: This function is a slightly modified version of rtpm_status_show
--- * from sysfs.c, but dependencies between PM_GENERIC_DOMAINS and PM_RUNTIME
--- * are too loose to generalize it.
+++ * from sysfs.c, so generalize it.
*/
---#ifdef CONFIG_PM_RUNTIME
static void rtpm_status_str(struct seq_file *s, struct device *dev)
{
static const char * const status_lookup[] = {
seq_puts(s, p);
}
---#else
---static void rtpm_status_str(struct seq_file *s, struct device *dev)
---{
--- seq_puts(s, "active");
---}
---#endif
static int pm_genpd_summary_one(struct seq_file *s,
struct generic_pm_domain *gpd)
#include <linux/notifier.h>
#include <linux/cpuidle.h>
++/* Defines used for the flags field in the struct generic_pm_domain */
++#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */
++
enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */
GPD_STATE_WAIT_MASTER, /* PM domain's master is being waited for */
bool max_off_time_changed;
bool cached_power_down_ok;
struct gpd_cpuidle_data *cpuidle_data;
- void (*attach_dev)(struct device *dev);
- void (*detach_dev)(struct device *dev);
+ int (*attach_dev)(struct generic_pm_domain *domain,
+ struct device *dev);
+ void (*detach_dev)(struct generic_pm_domain *domain,
+ struct device *dev);
++ unsigned int flags; /* Bit field of configs for genpd */
};
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
bool cached_stop_ok;
};
++struct pm_domain_data {
++ struct list_head list_node;
++ struct device *dev;
++};
++
struct generic_pm_domain_data {
struct pm_domain_data base;
struct gpd_timing_data td;
struct notifier_block nb;
struct mutex lock;
unsigned int refcount;
- bool need_restore;
+ int need_restore;
};
#ifdef CONFIG_PM_GENERIC_DOMAINS
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
extern int pm_genpd_name_poweron(const char *domain_name);
+++extern void pm_genpd_poweroff_unused(void);
extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
{
return -ENOSYS;
}
+++static inline void pm_genpd_poweroff_unused(void) {}
#define simple_qos_governor NULL
#define pm_domain_always_on_gov NULL
#endif
return __pm_genpd_name_add_device(domain_name, dev, NULL);
}
---#ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME
---extern void pm_genpd_poweroff_unused(void);
---#else
---static inline void pm_genpd_poweroff_unused(void) {}
---#endif
---
#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
extern void pm_genpd_syscore_poweroff(struct device *dev);
extern void pm_genpd_syscore_poweron(struct device *dev);
int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
void *data);
void of_genpd_del_provider(struct device_node *np);
+ ++struct generic_pm_domain *of_genpd_get_from_provider(
+ ++ struct of_phandle_args *genpdspec);
struct generic_pm_domain *__of_genpd_xlate_simple(
struct of_phandle_args *genpdspec,
}
static inline void of_genpd_del_provider(struct device_node *np) {}
+ ++static inline struct generic_pm_domain *of_genpd_get_from_provider(
+ ++ struct of_phandle_args *genpdspec)
+ ++{
+ ++ return NULL;
+ ++}
+ ++
#define __of_genpd_xlate_simple NULL
#define __of_genpd_xlate_onecell NULL