// SPDX-License-Identifier: GPL-2.0/* * drivers/base/power/domain.c - Common code related to device power domains. * * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. */#define pr_fmt(fmt) "PM: " fmt #include <linux/delay.h>#include <linux/idr.h>#include <linux/kernel.h>#include <linux/io.h>#include <linux/platform_device.h>#include <linux/pm_opp.h>#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>#include <linux/suspend.h>#include <linux/export.h>#include <linux/cpu.h>#include <linux/debugfs.h> /* Provides a unique ID for each genpd device */static DEFINE_IDA(genpd_ida); /* The bus for genpd_providers. */static const struct bus_type genpd_provider_bus_type = { .name = "genpd_provider",}; #define GENPD_RETRY_MAX_MS 250 /* Approximate */ #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \({ \ type (*__routine)(struct device *__d); \ type __ret = (type)0; \ \ __routine = genpd->dev_ops.callback; \ if (__routine) { \ __ret = __routine(dev); \ } \ __ret; \}) static LIST_HEAD(gpd_list);static DEFINE_MUTEX(gpd_list_lock); struct genpd_lock_ops { void (*lock)(struct generic_pm_domain *genpd); void (*lock_nested)(struct generic_pm_domain *genpd, int depth); int (*lock_interruptible)(struct generic_pm_domain *genpd); void (*unlock)(struct generic_pm_domain *genpd);}; static void genpd_lock_mtx(struct generic_pm_domain *genpd){ mutex_lock(&genpd->mlock);} static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, int depth){ mutex_lock_nested(&genpd->mlock, depth);} static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd){ return mutex_lock_interruptible(&genpd->mlock);} static void genpd_unlock_mtx(struct generic_pm_domain *genpd){ return mutex_unlock(&genpd->mlock);} static const struct genpd_lock_ops genpd_mtx_ops = { .lock = genpd_lock_mtx, .lock_nested = genpd_lock_nested_mtx, .lock_interruptible = genpd_lock_interruptible_mtx, .unlock = genpd_unlock_mtx,}; static void genpd_lock_spin(struct generic_pm_domain *genpd) __acquires(&genpd->slock){ unsigned long flags; spin_lock_irqsave(&genpd->slock, flags); genpd->lock_flags = flags;} static void genpd_lock_nested_spin(struct generic_pm_domain *genpd, int depth) __acquires(&genpd->slock){ unsigned long flags; spin_lock_irqsave_nested(&genpd->slock, flags, depth); genpd->lock_flags = flags;} static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) __acquires(&genpd->slock){ unsigned long flags; spin_lock_irqsave(&genpd->slock, flags); genpd->lock_flags = flags; return 0;} static void genpd_unlock_spin(struct generic_pm_domain *genpd) __releases(&genpd->slock){ spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags);} static const struct genpd_lock_ops genpd_spin_ops = { .lock = genpd_lock_spin, .lock_nested = genpd_lock_nested_spin, .lock_interruptible = genpd_lock_interruptible_spin, .unlock = genpd_unlock_spin,}; static void genpd_lock_raw_spin(struct generic_pm_domain *genpd) __acquires(&genpd->raw_slock){ unsigned long flags; raw_spin_lock_irqsave(&genpd->raw_slock, flags); genpd->raw_lock_flags = flags;} static void genpd_lock_nested_raw_spin(struct generic_pm_domain *genpd, int depth) __acquires(&genpd->raw_slock){ unsigned long flags; raw_spin_lock_irqsave_nested(&genpd->raw_slock, flags, depth); genpd->raw_lock_flags = flags;} static int genpd_lock_interruptible_raw_spin(struct generic_pm_domain *genpd) __acquires(&genpd->raw_slock){ unsigned long flags; raw_spin_lock_irqsave(&genpd->raw_slock, flags); genpd->raw_lock_flags = flags; return 0;} static void genpd_unlock_raw_spin(struct generic_pm_domain *genpd) __releases(&genpd->raw_slock){ raw_spin_unlock_irqrestore(&genpd->raw_slock, genpd->raw_lock_flags);} static const struct genpd_lock_ops genpd_raw_spin_ops = { .lock = genpd_lock_raw_spin, .lock_nested = genpd_lock_nested_raw_spin, .lock_interruptible = genpd_lock_interruptible_raw_spin, .unlock = genpd_unlock_raw_spin,}; #define genpd_lock(p) p->lock_ops->lock(p)#define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d)#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p)#define genpd_unlock(p) p->lock_ops->unlock(p) #define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON)#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN)#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW)#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)#define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)#define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON) static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd){ bool ret; ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); /* * Warn once if an IRQ safe device is attached to a domain, which * callbacks are allowed to sleep. This indicates a suboptimal * configuration for PM, but it doesn't matter for an always on domain. */ if (genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) return ret; if (ret) dev_warn_once(dev, "PM domain %s will not be powered off\n", dev_name(&genpd->dev)); return ret;} static int genpd_runtime_suspend(struct device *dev); /* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer, * and checks that the PM domain pointer is a real generic PM domain. * Any failure results in NULL being returned. */static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev){ if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) return NULL; /* A genpd's always have its ->runtime_suspend() callback assigned. */ if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend) return pd_to_genpd(dev->pm_domain); return NULL;} /* * This should only be used where we are certain that the pm_domain * attached to the device is a genpd domain. */static struct generic_pm_domain *dev_to_genpd(struct device *dev){ if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); return pd_to_genpd(dev->pm_domain);} struct device *dev_to_genpd_dev(struct device *dev){ struct generic_pm_domain *genpd = dev_to_genpd(dev); if (IS_ERR(genpd)) return ERR_CAST(genpd); return &genpd->dev;} static int genpd_stop_dev(const struct generic_pm_domain *genpd, struct device *dev){ return GENPD_DEV_CALLBACK(genpd, int, stop, dev);} static int genpd_start_dev(const 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; if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) ret = !!atomic_dec_and_test(&genpd->sd_count); return ret;} static void genpd_sd_counter_inc(struct generic_pm_domain *genpd){ atomic_inc(&genpd->sd_count); smp_mb__after_atomic();} #ifdef CONFIG_DEBUG_FSstatic struct dentry *genpd_debugfs_dir; static void genpd_debug_add(struct generic_pm_domain *genpd); static void genpd_debug_remove(struct generic_pm_domain *genpd){ if (!genpd_debugfs_dir) return; debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir);} static void genpd_update_accounting(struct generic_pm_domain *genpd){ u64 delta, now; now = ktime_get_mono_fast_ns(); if (now <= genpd->accounting_time) return; delta = now - genpd->accounting_time; /* * If genpd->status is active, it means we are just * out of off and so update the idle time and vice * versa. */ if (genpd->status == GENPD_STATE_ON) genpd->states[genpd->state_idx].idle_time += delta; else genpd->on_time += delta; genpd->accounting_time = now;} static void genpd_reflect_residency(struct generic_pm_domain *genpd){ struct genpd_governor_data *gd = genpd->gd; struct genpd_power_state *state, *next_state; unsigned int state_idx; s64 sleep_ns, target_ns; if (!gd || !gd->reflect_residency) return; sleep_ns = ktime_to_ns(ktime_sub(ktime_get(), gd->last_enter)); state_idx = genpd->state_idx; state = &genpd->states[state_idx]; target_ns = state->power_off_latency_ns + state->residency_ns; if (sleep_ns < target_ns) { state->above++; } else if (state_idx < (genpd->state_count -1)) { next_state = &genpd->states[state_idx + 1]; target_ns = next_state->power_off_latency_ns + next_state->residency_ns; if (sleep_ns >= target_ns) state->below++; } gd->reflect_residency = false;}#elsestatic inline void genpd_debug_add(struct generic_pm_domain *genpd) {}static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}static inline void genpd_reflect_residency(struct generic_pm_domain *genpd) {}#endif static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, unsigned int state){ struct generic_pm_domain_data *pd_data; struct pm_domain_data *pdd; struct gpd_link *link; /* New requested state is same as Max requested state */ if (state == genpd->performance_state) return state; /* New requested state is higher than Max requested state */ if (state > genpd->performance_state) return state; /* Traverse all devices within the domain */ list_for_each_entry(pdd, &genpd->dev_list, list_node) { pd_data = to_gpd_data(pdd); if (pd_data->performance_state > state) state = pd_data->performance_state; } /* * Traverse all sub-domains within the domain. This can be * done without any additional locking as the link->performance_state * field is protected by the parent genpd->lock, which is already taken. * * Also note that link->performance_state (subdomain's performance state * requirement to parent domain) is different from * link->child->performance_state (current performance state requirement * of the devices/sub-domains of the subdomain) and so can have a * different value. * * Note that we also take vote from powered-off sub-domains into account * as the same is done for devices right now. */ list_for_each_entry(link, &genpd->parent_links, parent_node) { if (link->performance_state > state) state = link->performance_state; } return state;} static int genpd_xlate_performance_state(struct generic_pm_domain *genpd, struct generic_pm_domain *parent, unsigned int pstate){ if (!parent->set_performance_state) return pstate; return dev_pm_opp_xlate_performance_state(genpd->opp_table, parent->opp_table, pstate);} static int _genpd_set_performance_state(struct generic_pm_domain *genpd, unsigned int state, int depth); static void _genpd_rollback_parent_state(struct gpd_link *link, int depth){ struct generic_pm_domain *parent = link->parent; int parent_state; genpd_lock_nested(parent, depth + 1); parent_state = link->prev_performance_state; link->performance_state = parent_state; parent_state = _genpd_reeval_performance_state(parent, parent_state); if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { pr_err("%s: Failed to roll back to %d performance state\n", parent->name, parent_state); } genpd_unlock(parent);} static int _genpd_set_parent_state(struct generic_pm_domain *genpd, struct gpd_link *link, unsigned int state, int depth){ struct generic_pm_domain *parent = link->parent; int parent_state, ret; /* Find parent's performance state */ ret = genpd_xlate_performance_state(genpd, parent, state); if (unlikely(ret < 0)) return ret; parent_state = ret; genpd_lock_nested(parent, depth + 1); link->prev_performance_state = link->performance_state; link->performance_state = parent_state; parent_state = _genpd_reeval_performance_state(parent, parent_state); ret = _genpd_set_performance_state(parent, parent_state, depth + 1); if (ret) link->performance_state = link->prev_performance_state; genpd_unlock(parent); return ret;} static int _genpd_set_performance_state(struct generic_pm_domain *genpd, unsigned int state, int depth){ struct gpd_link *link = NULL; int ret; if (state == genpd->performance_state) return 0; /* When scaling up, propagate to parents first in normal order */ if (state > genpd->performance_state) { list_for_each_entry(link, &genpd->child_links, child_node) { ret = _genpd_set_parent_state(genpd, link, state, depth); if (ret) goto rollback_parents_up; } } if (genpd->set_performance_state) { ret = genpd->set_performance_state(genpd, state); if (ret) { if (link) goto rollback_parents_up; return ret; } } /* When scaling down, propagate to parents last in reverse order */ if (state < genpd->performance_state) { list_for_each_entry_reverse(link, &genpd->child_links, child_node) { ret = _genpd_set_parent_state(genpd, link, state, depth); if (ret) goto rollback_parents_down; } } genpd->performance_state = state; return 0; rollback_parents_up: list_for_each_entry_continue_reverse(link, &genpd->child_links, child_node) _genpd_rollback_parent_state(link, depth); return ret;Lines 1–500 of 4160. Load more
// SPDX-License-Identifier: GPL-2.0 /* * drivers/base/power/domain.c - Common code related to device power domains. * * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. */ #define pr_fmt(fmt) "PM: " fmt #include <linux/delay.h> #include <linux/idr.h> #include <linux/kernel.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #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> #include <linux/suspend.h> #include <linux/export.h> #include <linux/cpu.h> #include <linux/debugfs.h> /* Provides a unique ID for each genpd device */ static DEFINE_IDA(genpd_ida); /* The bus for genpd_providers. */ static const struct bus_type genpd_provider_bus_type = { .name = "genpd_provider", }; #define GENPD_RETRY_MAX_MS 250 /* Approximate */ #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ ({ \ type (*__routine)(struct device *__d); \ type __ret = (type)0; \ \ __routine = genpd->dev_ops.callback; \ if (__routine) { \ __ret = __routine(dev); \ } \ __ret; \ }) static LIST_HEAD(gpd_list); static DEFINE_MUTEX(gpd_list_lock); struct genpd_lock_ops { void (*lock)(struct generic_pm_domain *genpd); void (*lock_nested)(struct generic_pm_domain *genpd, int depth); int (*lock_interruptible)(struct generic_pm_domain *genpd); void (*unlock)(struct generic_pm_domain *genpd); }; static void genpd_lock_mtx(struct generic_pm_domain *genpd) { mutex_lock(&genpd->mlock); } static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, int depth) { mutex_lock_nested(&genpd->mlock, depth); } static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd) { return mutex_lock_interruptible(&genpd->mlock); } static void genpd_unlock_mtx(struct generic_pm_domain *genpd) { return mutex_unlock(&genpd->mlock); } static const struct genpd_lock_ops genpd_mtx_ops = { .lock = genpd_lock_mtx, .lock_nested = genpd_lock_nested_mtx, .lock_interruptible = genpd_lock_interruptible_mtx, .unlock = genpd_unlock_mtx, }; static void genpd_lock_spin(struct generic_pm_domain *genpd) __acquires(&genpd->slock) { unsigned long flags; spin_lock_irqsave(&genpd->slock, flags); genpd->lock_flags = flags; } static void genpd_lock_nested_spin(struct generic_pm_domain *genpd, int depth) __acquires(&genpd->slock) { unsigned long flags; spin_lock_irqsave_nested(&genpd->slock, flags, depth); genpd->lock_flags = flags; } static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) __acquires(&genpd->slock) { unsigned long flags; spin_lock_irqsave(&genpd->slock, flags); genpd->lock_flags = flags; return 0; } static void genpd_unlock_spin(struct generic_pm_domain *genpd) __releases(&genpd->slock) { spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags); } static const struct genpd_lock_ops genpd_spin_ops = { .lock = genpd_lock_spin, .lock_nested = genpd_lock_nested_spin, .lock_interruptible = genpd_lock_interruptible_spin, .unlock = genpd_unlock_spin, }; static void genpd_lock_raw_spin(struct generic_pm_domain *genpd) __acquires(&genpd->raw_slock) { unsigned long flags; raw_spin_lock_irqsave(&genpd->raw_slock, flags); genpd->raw_lock_flags = flags; } static void genpd_lock_nested_raw_spin(struct generic_pm_domain *genpd, int depth) __acquires(&genpd->raw_slock) { unsigned long flags; raw_spin_lock_irqsave_nested(&genpd->raw_slock, flags, depth); genpd->raw_lock_flags = flags; } static int genpd_lock_interruptible_raw_spin(struct generic_pm_domain *genpd) __acquires(&genpd->raw_slock) { unsigned long flags; raw_spin_lock_irqsave(&genpd->raw_slock, flags); genpd->raw_lock_flags = flags; return 0; } static void genpd_unlock_raw_spin(struct generic_pm_domain *genpd) __releases(&genpd->raw_slock) { raw_spin_unlock_irqrestore(&genpd->raw_slock, genpd->raw_lock_flags); } static const struct genpd_lock_ops genpd_raw_spin_ops = { .lock = genpd_lock_raw_spin, .lock_nested = genpd_lock_nested_raw_spin, .lock_interruptible = genpd_lock_interruptible_raw_spin, .unlock = genpd_unlock_raw_spin, }; #define genpd_lock(p) p->lock_ops->lock(p) #define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) #define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) #define genpd_unlock(p) p->lock_ops->unlock(p) #define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON) #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) #define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) #define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) #define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW) #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW) #define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE) #define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON) static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) { bool ret; ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); /* * Warn once if an IRQ safe device is attached to a domain, which * callbacks are allowed to sleep. This indicates a suboptimal * configuration for PM, but it doesn't matter for an always on domain. */ if (genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) return ret; if (ret) dev_warn_once(dev, "PM domain %s will not be powered off\n", dev_name(&genpd->dev)); return ret; } static int genpd_runtime_suspend(struct device *dev); /* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer, * and checks that the PM domain pointer is a real generic PM domain. * Any failure results in NULL being returned. */ static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev) { if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) return NULL; /* A genpd's always have its ->runtime_suspend() callback assigned. */ if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend) return pd_to_genpd(dev->pm_domain); return NULL; } /* * This should only be used where we are certain that the pm_domain * attached to the device is a genpd domain. */ static struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); return pd_to_genpd(dev->pm_domain); } struct device *dev_to_genpd_dev(struct device *dev) { struct generic_pm_domain *genpd = dev_to_genpd(dev); if (IS_ERR(genpd)) return ERR_CAST(genpd); return &genpd->dev; } static int genpd_stop_dev(const struct generic_pm_domain *genpd, struct device *dev) { return GENPD_DEV_CALLBACK(genpd, int, stop, dev); } static int genpd_start_dev(const 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; if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) ret = !!atomic_dec_and_test(&genpd->sd_count); return ret; } static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) { atomic_inc(&genpd->sd_count); smp_mb__after_atomic(); } #ifdef CONFIG_DEBUG_FS static struct dentry *genpd_debugfs_dir; static void genpd_debug_add(struct generic_pm_domain *genpd); static void genpd_debug_remove(struct generic_pm_domain *genpd) { if (!genpd_debugfs_dir) return; debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir); } static void genpd_update_accounting(struct generic_pm_domain *genpd) { u64 delta, now; now = ktime_get_mono_fast_ns(); if (now <= genpd->accounting_time) return; delta = now - genpd->accounting_time; /* * If genpd->status is active, it means we are just * out of off and so update the idle time and vice * versa. */ if (genpd->status == GENPD_STATE_ON) genpd->states[genpd->state_idx].idle_time += delta; else genpd->on_time += delta; genpd->accounting_time = now; } static void genpd_reflect_residency(struct generic_pm_domain *genpd) { struct genpd_governor_data *gd = genpd->gd; struct genpd_power_state *state, *next_state; unsigned int state_idx; s64 sleep_ns, target_ns; if (!gd || !gd->reflect_residency) return; sleep_ns = ktime_to_ns(ktime_sub(ktime_get(), gd->last_enter)); state_idx = genpd->state_idx; state = &genpd->states[state_idx]; target_ns = state->power_off_latency_ns + state->residency_ns; if (sleep_ns < target_ns) { state->above++; } else if (state_idx < (genpd->state_count -1)) { next_state = &genpd->states[state_idx + 1]; target_ns = next_state->power_off_latency_ns + next_state->residency_ns; if (sleep_ns >= target_ns) state->below++; } gd->reflect_residency = false; } #else static inline void genpd_debug_add(struct generic_pm_domain *genpd) {} static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {} static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} static inline void genpd_reflect_residency(struct generic_pm_domain *genpd) {} #endif static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, unsigned int state) { struct generic_pm_domain_data *pd_data; struct pm_domain_data *pdd; struct gpd_link *link; /* New requested state is same as Max requested state */ if (state == genpd->performance_state) return state; /* New requested state is higher than Max requested state */ if (state > genpd->performance_state) return state; /* Traverse all devices within the domain */ list_for_each_entry(pdd, &genpd->dev_list, list_node) { pd_data = to_gpd_data(pdd); if (pd_data->performance_state > state) state = pd_data->performance_state; } /* * Traverse all sub-domains within the domain. This can be * done without any additional locking as the link->performance_state * field is protected by the parent genpd->lock, which is already taken. * * Also note that link->performance_state (subdomain's performance state * requirement to parent domain) is different from * link->child->performance_state (current performance state requirement * of the devices/sub-domains of the subdomain) and so can have a * different value. * * Note that we also take vote from powered-off sub-domains into account * as the same is done for devices right now. */ list_for_each_entry(link, &genpd->parent_links, parent_node) { if (link->performance_state > state) state = link->performance_state; } return state; } static int genpd_xlate_performance_state(struct generic_pm_domain *genpd, struct generic_pm_domain *parent, unsigned int pstate) { if (!parent->set_performance_state) return pstate; return dev_pm_opp_xlate_performance_state(genpd->opp_table, parent->opp_table, pstate); } static int _genpd_set_performance_state(struct generic_pm_domain *genpd, unsigned int state, int depth); static void _genpd_rollback_parent_state(struct gpd_link *link, int depth) { struct generic_pm_domain *parent = link->parent; int parent_state; genpd_lock_nested(parent, depth + 1); parent_state = link->prev_performance_state; link->performance_state = parent_state; parent_state = _genpd_reeval_performance_state(parent, parent_state); if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { pr_err("%s: Failed to roll back to %d performance state\n", parent->name, parent_state); } genpd_unlock(parent); } static int _genpd_set_parent_state(struct generic_pm_domain *genpd, struct gpd_link *link, unsigned int state, int depth) { struct generic_pm_domain *parent = link->parent; int parent_state, ret; /* Find parent's performance state */ ret = genpd_xlate_performance_state(genpd, parent, state); if (unlikely(ret < 0)) return ret; parent_state = ret; genpd_lock_nested(parent, depth + 1); link->prev_performance_state = link->performance_state; link->performance_state = parent_state; parent_state = _genpd_reeval_performance_state(parent, parent_state); ret = _genpd_set_performance_state(parent, parent_state, depth + 1); if (ret) link->performance_state = link->prev_performance_state; genpd_unlock(parent); return ret; } static int _genpd_set_performance_state(struct generic_pm_domain *genpd, unsigned int state, int depth) { struct gpd_link *link = NULL; int ret; if (state == genpd->performance_state) return 0; /* When scaling up, propagate to parents first in normal order */ if (state > genpd->performance_state) { list_for_each_entry(link, &genpd->child_links, child_node) { ret = _genpd_set_parent_state(genpd, link, state, depth); if (ret) goto rollback_parents_up; } } if (genpd->set_performance_state) { ret = genpd->set_performance_state(genpd, state); if (ret) { if (link) goto rollback_parents_up; return ret; } } /* When scaling down, propagate to parents last in reverse order */ if (state < genpd->performance_state) { list_for_each_entry_reverse(link, &genpd->child_links, child_node) { ret = _genpd_set_parent_state(genpd, link, state, depth); if (ret) goto rollback_parents_down; } } genpd->performance_state = state; return 0; rollback_parents_up: list_for_each_entry_continue_reverse(link, &genpd->child_links, child_node) _genpd_rollback_parent_state(link, depth); return ret;