15de363b6SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2f721889fSRafael J. Wysocki /* 3f721889fSRafael J. Wysocki * drivers/base/power/domain.c - Common code related to device power domains. 4f721889fSRafael J. Wysocki * 5f721889fSRafael J. Wysocki * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 6f721889fSRafael J. Wysocki */ 77a5bd127SJoe Perches #define pr_fmt(fmt) "PM: " fmt 87a5bd127SJoe Perches 993af5e93SGeert Uytterhoeven #include <linux/delay.h> 10f721889fSRafael J. Wysocki #include <linux/kernel.h> 11f721889fSRafael J. Wysocki #include <linux/io.h> 12aa42240aSTomasz Figa #include <linux/platform_device.h> 136a0ae73dSViresh Kumar #include <linux/pm_opp.h> 14f721889fSRafael J. Wysocki #include <linux/pm_runtime.h> 15f721889fSRafael J. Wysocki #include <linux/pm_domain.h> 166ff7bb0dSRafael J. Wysocki #include <linux/pm_qos.h> 17c11f6f5bSUlf Hansson #include <linux/pm_clock.h> 18f721889fSRafael J. Wysocki #include <linux/slab.h> 19f721889fSRafael J. Wysocki #include <linux/err.h> 2017b75ecaSRafael J. Wysocki #include <linux/sched.h> 2117b75ecaSRafael J. Wysocki #include <linux/suspend.h> 22d5e4cbfeSRafael J. Wysocki #include <linux/export.h> 23eb594b73SUlf Hansson #include <linux/cpu.h> 24718072ceSThierry Strudel #include <linux/debugfs.h> 25d5e4cbfeSRafael J. Wysocki 26aa8e54b5STomeu Vizoso #include "power.h" 27aa8e54b5STomeu Vizoso 2893af5e93SGeert Uytterhoeven #define GENPD_RETRY_MAX_MS 250 /* Approximate */ 2993af5e93SGeert Uytterhoeven 30d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 31d5e4cbfeSRafael J. Wysocki ({ \ 32d5e4cbfeSRafael J. Wysocki type (*__routine)(struct device *__d); \ 33d5e4cbfeSRafael J. Wysocki type __ret = (type)0; \ 34d5e4cbfeSRafael J. Wysocki \ 35d5e4cbfeSRafael J. Wysocki __routine = genpd->dev_ops.callback; \ 36d5e4cbfeSRafael J. Wysocki if (__routine) { \ 37d5e4cbfeSRafael J. Wysocki __ret = __routine(dev); \ 38d5e4cbfeSRafael J. Wysocki } \ 39d5e4cbfeSRafael J. Wysocki __ret; \ 40d5e4cbfeSRafael J. Wysocki }) 41f721889fSRafael J. Wysocki 425125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 435125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 445125bbf3SRafael J. Wysocki 4535241d12SLina Iyer struct genpd_lock_ops { 4635241d12SLina Iyer void (*lock)(struct generic_pm_domain *genpd); 4735241d12SLina Iyer void (*lock_nested)(struct generic_pm_domain *genpd, int depth); 4835241d12SLina Iyer int (*lock_interruptible)(struct generic_pm_domain *genpd); 4935241d12SLina Iyer void (*unlock)(struct generic_pm_domain *genpd); 5035241d12SLina Iyer }; 5135241d12SLina Iyer 5235241d12SLina Iyer static void genpd_lock_mtx(struct generic_pm_domain *genpd) 5335241d12SLina Iyer { 5435241d12SLina Iyer mutex_lock(&genpd->mlock); 5535241d12SLina Iyer } 5635241d12SLina Iyer 5735241d12SLina Iyer static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, 5835241d12SLina Iyer int depth) 5935241d12SLina Iyer { 6035241d12SLina Iyer mutex_lock_nested(&genpd->mlock, depth); 6135241d12SLina Iyer } 6235241d12SLina Iyer 6335241d12SLina Iyer static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd) 6435241d12SLina Iyer { 6535241d12SLina Iyer return mutex_lock_interruptible(&genpd->mlock); 6635241d12SLina Iyer } 6735241d12SLina Iyer 6835241d12SLina Iyer static void genpd_unlock_mtx(struct generic_pm_domain *genpd) 6935241d12SLina Iyer { 7035241d12SLina Iyer return mutex_unlock(&genpd->mlock); 7135241d12SLina Iyer } 7235241d12SLina Iyer 7335241d12SLina Iyer static const struct genpd_lock_ops genpd_mtx_ops = { 7435241d12SLina Iyer .lock = genpd_lock_mtx, 7535241d12SLina Iyer .lock_nested = genpd_lock_nested_mtx, 7635241d12SLina Iyer .lock_interruptible = genpd_lock_interruptible_mtx, 7735241d12SLina Iyer .unlock = genpd_unlock_mtx, 7835241d12SLina Iyer }; 7935241d12SLina Iyer 80d716f479SLina Iyer static void genpd_lock_spin(struct generic_pm_domain *genpd) 81d716f479SLina Iyer __acquires(&genpd->slock) 82d716f479SLina Iyer { 83d716f479SLina Iyer unsigned long flags; 84d716f479SLina Iyer 85d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 86d716f479SLina Iyer genpd->lock_flags = flags; 87d716f479SLina Iyer } 88d716f479SLina Iyer 89d716f479SLina Iyer static void genpd_lock_nested_spin(struct generic_pm_domain *genpd, 90d716f479SLina Iyer int depth) 91d716f479SLina Iyer __acquires(&genpd->slock) 92d716f479SLina Iyer { 93d716f479SLina Iyer unsigned long flags; 94d716f479SLina Iyer 95d716f479SLina Iyer spin_lock_irqsave_nested(&genpd->slock, flags, depth); 96d716f479SLina Iyer genpd->lock_flags = flags; 97d716f479SLina Iyer } 98d716f479SLina Iyer 99d716f479SLina Iyer static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) 100d716f479SLina Iyer __acquires(&genpd->slock) 101d716f479SLina Iyer { 102d716f479SLina Iyer unsigned long flags; 103d716f479SLina Iyer 104d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 105d716f479SLina Iyer genpd->lock_flags = flags; 106d716f479SLina Iyer return 0; 107d716f479SLina Iyer } 108d716f479SLina Iyer 109d716f479SLina Iyer static void genpd_unlock_spin(struct generic_pm_domain *genpd) 110d716f479SLina Iyer __releases(&genpd->slock) 111d716f479SLina Iyer { 112d716f479SLina Iyer spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags); 113d716f479SLina Iyer } 114d716f479SLina Iyer 115d716f479SLina Iyer static const struct genpd_lock_ops genpd_spin_ops = { 116d716f479SLina Iyer .lock = genpd_lock_spin, 117d716f479SLina Iyer .lock_nested = genpd_lock_nested_spin, 118d716f479SLina Iyer .lock_interruptible = genpd_lock_interruptible_spin, 119d716f479SLina Iyer .unlock = genpd_unlock_spin, 120d716f479SLina Iyer }; 121d716f479SLina Iyer 12235241d12SLina Iyer #define genpd_lock(p) p->lock_ops->lock(p) 12335241d12SLina Iyer #define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) 12435241d12SLina Iyer #define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) 12535241d12SLina Iyer #define genpd_unlock(p) p->lock_ops->unlock(p) 12635241d12SLina Iyer 12749f618e1SUlf Hansson #define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON) 128d716f479SLina Iyer #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 129ffaa42e8SUlf Hansson #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) 13095a20ef6SGeert Uytterhoeven #define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) 131eb594b73SUlf Hansson #define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) 132ed61e18aSLeonard Crestez #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) 133d716f479SLina Iyer 134d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 135d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd) 136d716f479SLina Iyer { 137d716f479SLina Iyer bool ret; 138d716f479SLina Iyer 139d716f479SLina Iyer ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 140d716f479SLina Iyer 141075c37d5SUlf Hansson /* 142075c37d5SUlf Hansson * Warn once if an IRQ safe device is attached to a no sleep domain, as 143075c37d5SUlf Hansson * to indicate a suboptimal configuration for PM. For an always on 144075c37d5SUlf Hansson * domain this isn't case, thus don't warn. 145075c37d5SUlf Hansson */ 146075c37d5SUlf Hansson if (ret && !genpd_is_always_on(genpd)) 147d716f479SLina Iyer dev_warn_once(dev, "PM domain %s will not be powered off\n", 148d716f479SLina Iyer genpd->name); 149d716f479SLina Iyer 150d716f479SLina Iyer return ret; 151d716f479SLina Iyer } 152d716f479SLina Iyer 153b3ad17c0SUlf Hansson static int genpd_runtime_suspend(struct device *dev); 154b3ad17c0SUlf Hansson 155446d999cSRussell King /* 156446d999cSRussell King * Get the generic PM domain for a particular struct device. 157446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 158446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 159446d999cSRussell King * Any failure results in NULL being returned. 160446d999cSRussell King */ 161b3ad17c0SUlf Hansson static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev) 162446d999cSRussell King { 163446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 164446d999cSRussell King return NULL; 165446d999cSRussell King 166b3ad17c0SUlf Hansson /* A genpd's always have its ->runtime_suspend() callback assigned. */ 167b3ad17c0SUlf Hansson if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend) 168b3ad17c0SUlf Hansson return pd_to_genpd(dev->pm_domain); 169446d999cSRussell King 170b3ad17c0SUlf Hansson return NULL; 171446d999cSRussell King } 172446d999cSRussell King 173446d999cSRussell King /* 174446d999cSRussell King * This should only be used where we are certain that the pm_domain 175446d999cSRussell King * attached to the device is a genpd domain. 176446d999cSRussell King */ 177446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 1785248051bSRafael J. Wysocki { 1795248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 1805248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 1815248051bSRafael J. Wysocki 182596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 1835248051bSRafael J. Wysocki } 184f721889fSRafael J. Wysocki 185d8600c8bSKrzysztof Kozlowski static int genpd_stop_dev(const struct generic_pm_domain *genpd, 186d8600c8bSKrzysztof Kozlowski struct device *dev) 187d5e4cbfeSRafael J. Wysocki { 18851cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 189d5e4cbfeSRafael J. Wysocki } 190d5e4cbfeSRafael J. Wysocki 191d8600c8bSKrzysztof Kozlowski static int genpd_start_dev(const struct generic_pm_domain *genpd, 192d8600c8bSKrzysztof Kozlowski struct device *dev) 193d5e4cbfeSRafael J. Wysocki { 194ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 195d5e4cbfeSRafael J. Wysocki } 196d5e4cbfeSRafael J. Wysocki 197c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 198f721889fSRafael J. Wysocki { 199c4bb3160SRafael J. Wysocki bool ret = false; 200c4bb3160SRafael J. Wysocki 201c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 202c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 203c4bb3160SRafael J. Wysocki 204c4bb3160SRafael J. Wysocki return ret; 205c4bb3160SRafael J. Wysocki } 206c4bb3160SRafael J. Wysocki 207c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 208c4bb3160SRafael J. Wysocki { 209c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 2104e857c58SPeter Zijlstra smp_mb__after_atomic(); 211f721889fSRafael J. Wysocki } 212f721889fSRafael J. Wysocki 213afece3abSThara Gopinath #ifdef CONFIG_DEBUG_FS 214718072ceSThierry Strudel static struct dentry *genpd_debugfs_dir; 215718072ceSThierry Strudel 216718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd); 217718072ceSThierry Strudel 218718072ceSThierry Strudel static void genpd_debug_remove(struct generic_pm_domain *genpd) 219718072ceSThierry Strudel { 220718072ceSThierry Strudel struct dentry *d; 221718072ceSThierry Strudel 222718072ceSThierry Strudel d = debugfs_lookup(genpd->name, genpd_debugfs_dir); 223718072ceSThierry Strudel debugfs_remove(d); 224718072ceSThierry Strudel } 225718072ceSThierry Strudel 226afece3abSThara Gopinath static void genpd_update_accounting(struct generic_pm_domain *genpd) 227afece3abSThara Gopinath { 228afece3abSThara Gopinath ktime_t delta, now; 229afece3abSThara Gopinath 230afece3abSThara Gopinath now = ktime_get(); 231afece3abSThara Gopinath delta = ktime_sub(now, genpd->accounting_time); 232afece3abSThara Gopinath 233afece3abSThara Gopinath /* 234afece3abSThara Gopinath * If genpd->status is active, it means we are just 235afece3abSThara Gopinath * out of off and so update the idle time and vice 236afece3abSThara Gopinath * versa. 237afece3abSThara Gopinath */ 23849f618e1SUlf Hansson if (genpd->status == GENPD_STATE_ON) { 239afece3abSThara Gopinath int state_idx = genpd->state_idx; 240afece3abSThara Gopinath 241afece3abSThara Gopinath genpd->states[state_idx].idle_time = 242afece3abSThara Gopinath ktime_add(genpd->states[state_idx].idle_time, delta); 243afece3abSThara Gopinath } else { 244afece3abSThara Gopinath genpd->on_time = ktime_add(genpd->on_time, delta); 245afece3abSThara Gopinath } 246afece3abSThara Gopinath 247afece3abSThara Gopinath genpd->accounting_time = now; 248afece3abSThara Gopinath } 249afece3abSThara Gopinath #else 250718072ceSThierry Strudel static inline void genpd_debug_add(struct generic_pm_domain *genpd) {} 251718072ceSThierry Strudel static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {} 252afece3abSThara Gopinath static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} 253afece3abSThara Gopinath #endif 254afece3abSThara Gopinath 255cd50c6d3SViresh Kumar static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, 256cd50c6d3SViresh Kumar unsigned int state) 257cd50c6d3SViresh Kumar { 258cd50c6d3SViresh Kumar struct generic_pm_domain_data *pd_data; 259cd50c6d3SViresh Kumar struct pm_domain_data *pdd; 26018edf49cSViresh Kumar struct gpd_link *link; 261cd50c6d3SViresh Kumar 262cd50c6d3SViresh Kumar /* New requested state is same as Max requested state */ 263cd50c6d3SViresh Kumar if (state == genpd->performance_state) 264cd50c6d3SViresh Kumar return state; 265cd50c6d3SViresh Kumar 266cd50c6d3SViresh Kumar /* New requested state is higher than Max requested state */ 267cd50c6d3SViresh Kumar if (state > genpd->performance_state) 268cd50c6d3SViresh Kumar return state; 269cd50c6d3SViresh Kumar 270cd50c6d3SViresh Kumar /* Traverse all devices within the domain */ 271cd50c6d3SViresh Kumar list_for_each_entry(pdd, &genpd->dev_list, list_node) { 272cd50c6d3SViresh Kumar pd_data = to_gpd_data(pdd); 273cd50c6d3SViresh Kumar 274cd50c6d3SViresh Kumar if (pd_data->performance_state > state) 275cd50c6d3SViresh Kumar state = pd_data->performance_state; 276cd50c6d3SViresh Kumar } 277cd50c6d3SViresh Kumar 278cd50c6d3SViresh Kumar /* 27918edf49cSViresh Kumar * Traverse all sub-domains within the domain. This can be 28018edf49cSViresh Kumar * done without any additional locking as the link->performance_state 2818d87ae48SKees Cook * field is protected by the parent genpd->lock, which is already taken. 28218edf49cSViresh Kumar * 28318edf49cSViresh Kumar * Also note that link->performance_state (subdomain's performance state 2848d87ae48SKees Cook * requirement to parent domain) is different from 2858d87ae48SKees Cook * link->child->performance_state (current performance state requirement 28618edf49cSViresh Kumar * of the devices/sub-domains of the subdomain) and so can have a 28718edf49cSViresh Kumar * different value. 28818edf49cSViresh Kumar * 28918edf49cSViresh Kumar * Note that we also take vote from powered-off sub-domains into account 29018edf49cSViresh Kumar * as the same is done for devices right now. 291cd50c6d3SViresh Kumar */ 2928d87ae48SKees Cook list_for_each_entry(link, &genpd->parent_links, parent_node) { 29318edf49cSViresh Kumar if (link->performance_state > state) 29418edf49cSViresh Kumar state = link->performance_state; 29518edf49cSViresh Kumar } 29618edf49cSViresh Kumar 297cd50c6d3SViresh Kumar return state; 298cd50c6d3SViresh Kumar } 299cd50c6d3SViresh Kumar 300079c42a0SDmitry Osipenko static int genpd_xlate_performance_state(struct generic_pm_domain *genpd, 301079c42a0SDmitry Osipenko struct generic_pm_domain *parent, 302079c42a0SDmitry Osipenko unsigned int pstate) 303079c42a0SDmitry Osipenko { 304079c42a0SDmitry Osipenko if (!parent->set_performance_state) 305079c42a0SDmitry Osipenko return pstate; 306079c42a0SDmitry Osipenko 307079c42a0SDmitry Osipenko return dev_pm_opp_xlate_performance_state(genpd->opp_table, 308079c42a0SDmitry Osipenko parent->opp_table, 309079c42a0SDmitry Osipenko pstate); 310079c42a0SDmitry Osipenko } 311079c42a0SDmitry Osipenko 312cd50c6d3SViresh Kumar static int _genpd_set_performance_state(struct generic_pm_domain *genpd, 31318edf49cSViresh Kumar unsigned int state, int depth) 314cd50c6d3SViresh Kumar { 3158d87ae48SKees Cook struct generic_pm_domain *parent; 31618edf49cSViresh Kumar struct gpd_link *link; 3178d87ae48SKees Cook int parent_state, ret; 318cd50c6d3SViresh Kumar 319cd50c6d3SViresh Kumar if (state == genpd->performance_state) 320cd50c6d3SViresh Kumar return 0; 321cd50c6d3SViresh Kumar 3228d87ae48SKees Cook /* Propagate to parents of genpd */ 3238d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 3248d87ae48SKees Cook parent = link->parent; 32518edf49cSViresh Kumar 3268d87ae48SKees Cook /* Find parent's performance state */ 327079c42a0SDmitry Osipenko ret = genpd_xlate_performance_state(genpd, parent, state); 32818edf49cSViresh Kumar if (unlikely(ret < 0)) 32918edf49cSViresh Kumar goto err; 33018edf49cSViresh Kumar 3318d87ae48SKees Cook parent_state = ret; 33218edf49cSViresh Kumar 3338d87ae48SKees Cook genpd_lock_nested(parent, depth + 1); 33418edf49cSViresh Kumar 33518edf49cSViresh Kumar link->prev_performance_state = link->performance_state; 3368d87ae48SKees Cook link->performance_state = parent_state; 3378d87ae48SKees Cook parent_state = _genpd_reeval_performance_state(parent, 3388d87ae48SKees Cook parent_state); 3398d87ae48SKees Cook ret = _genpd_set_performance_state(parent, parent_state, depth + 1); 34018edf49cSViresh Kumar if (ret) 34118edf49cSViresh Kumar link->performance_state = link->prev_performance_state; 34218edf49cSViresh Kumar 3438d87ae48SKees Cook genpd_unlock(parent); 34418edf49cSViresh Kumar 34518edf49cSViresh Kumar if (ret) 34618edf49cSViresh Kumar goto err; 34718edf49cSViresh Kumar } 34818edf49cSViresh Kumar 349079c42a0SDmitry Osipenko if (genpd->set_performance_state) { 350cd50c6d3SViresh Kumar ret = genpd->set_performance_state(genpd, state); 351cd50c6d3SViresh Kumar if (ret) 35218edf49cSViresh Kumar goto err; 353079c42a0SDmitry Osipenko } 354cd50c6d3SViresh Kumar 355cd50c6d3SViresh Kumar genpd->performance_state = state; 356cd50c6d3SViresh Kumar return 0; 35718edf49cSViresh Kumar 35818edf49cSViresh Kumar err: 35918edf49cSViresh Kumar /* Encountered an error, lets rollback */ 3608d87ae48SKees Cook list_for_each_entry_continue_reverse(link, &genpd->child_links, 3618d87ae48SKees Cook child_node) { 3628d87ae48SKees Cook parent = link->parent; 36318edf49cSViresh Kumar 3648d87ae48SKees Cook genpd_lock_nested(parent, depth + 1); 36518edf49cSViresh Kumar 3668d87ae48SKees Cook parent_state = link->prev_performance_state; 3678d87ae48SKees Cook link->performance_state = parent_state; 36818edf49cSViresh Kumar 3698d87ae48SKees Cook parent_state = _genpd_reeval_performance_state(parent, 3708d87ae48SKees Cook parent_state); 3718d87ae48SKees Cook if (_genpd_set_performance_state(parent, parent_state, depth + 1)) { 37218edf49cSViresh Kumar pr_err("%s: Failed to roll back to %d performance state\n", 3738d87ae48SKees Cook parent->name, parent_state); 37418edf49cSViresh Kumar } 37518edf49cSViresh Kumar 3768d87ae48SKees Cook genpd_unlock(parent); 37718edf49cSViresh Kumar } 37818edf49cSViresh Kumar 37918edf49cSViresh Kumar return ret; 380cd50c6d3SViresh Kumar } 381cd50c6d3SViresh Kumar 3820eef091dSUlf Hansson static int genpd_set_performance_state(struct device *dev, unsigned int state) 3830eef091dSUlf Hansson { 3840eef091dSUlf Hansson struct generic_pm_domain *genpd = dev_to_genpd(dev); 3850eef091dSUlf Hansson struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 3860eef091dSUlf Hansson unsigned int prev_state; 3870eef091dSUlf Hansson int ret; 3880eef091dSUlf Hansson 3890eef091dSUlf Hansson prev_state = gpd_data->performance_state; 390*d97fe100SUlf Hansson if (prev_state == state) 391*d97fe100SUlf Hansson return 0; 392*d97fe100SUlf Hansson 3930eef091dSUlf Hansson gpd_data->performance_state = state; 3940eef091dSUlf Hansson state = _genpd_reeval_performance_state(genpd, state); 3950eef091dSUlf Hansson 3960eef091dSUlf Hansson ret = _genpd_set_performance_state(genpd, state, 0); 3970eef091dSUlf Hansson if (ret) 3980eef091dSUlf Hansson gpd_data->performance_state = prev_state; 3990eef091dSUlf Hansson 4000eef091dSUlf Hansson return ret; 4010eef091dSUlf Hansson } 4020eef091dSUlf Hansson 40342f6284aSViresh Kumar /** 40442f6284aSViresh Kumar * dev_pm_genpd_set_performance_state- Set performance state of device's power 40542f6284aSViresh Kumar * domain. 40642f6284aSViresh Kumar * 40742f6284aSViresh Kumar * @dev: Device for which the performance-state needs to be set. 40842f6284aSViresh Kumar * @state: Target performance state of the device. This can be set as 0 when the 40942f6284aSViresh Kumar * device doesn't have any performance state constraints left (And so 41042f6284aSViresh Kumar * the device wouldn't participate anymore to find the target 41142f6284aSViresh Kumar * performance state of the genpd). 41242f6284aSViresh Kumar * 41342f6284aSViresh Kumar * It is assumed that the users guarantee that the genpd wouldn't be detached 41442f6284aSViresh Kumar * while this routine is getting called. 41542f6284aSViresh Kumar * 41642f6284aSViresh Kumar * Returns 0 on success and negative error values on failures. 41742f6284aSViresh Kumar */ 41842f6284aSViresh Kumar int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) 41942f6284aSViresh Kumar { 42042f6284aSViresh Kumar struct generic_pm_domain *genpd; 421cd50c6d3SViresh Kumar int ret; 42242f6284aSViresh Kumar 4233ea4ca92SUlf Hansson genpd = dev_to_genpd_safe(dev); 4243ea4ca92SUlf Hansson if (!genpd) 42542f6284aSViresh Kumar return -ENODEV; 42642f6284aSViresh Kumar 427e757e7faSYangtao Li if (WARN_ON(!dev->power.subsys_data || 428e757e7faSYangtao Li !dev->power.subsys_data->domain_data)) 42942f6284aSViresh Kumar return -EINVAL; 43042f6284aSViresh Kumar 43142f6284aSViresh Kumar genpd_lock(genpd); 4320eef091dSUlf Hansson ret = genpd_set_performance_state(dev, state); 43342f6284aSViresh Kumar genpd_unlock(genpd); 43442f6284aSViresh Kumar 43542f6284aSViresh Kumar return ret; 43642f6284aSViresh Kumar } 43742f6284aSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); 43842f6284aSViresh Kumar 43967e3242eSLina Iyer /** 44067e3242eSLina Iyer * dev_pm_genpd_set_next_wakeup - Notify PM framework of an impending wakeup. 44167e3242eSLina Iyer * 44267e3242eSLina Iyer * @dev: Device to handle 44367e3242eSLina Iyer * @next: impending interrupt/wakeup for the device 44467e3242eSLina Iyer * 44567e3242eSLina Iyer * 44667e3242eSLina Iyer * Allow devices to inform of the next wakeup. It's assumed that the users 44767e3242eSLina Iyer * guarantee that the genpd wouldn't be detached while this routine is getting 44867e3242eSLina Iyer * called. Additionally, it's also assumed that @dev isn't runtime suspended 44967e3242eSLina Iyer * (RPM_SUSPENDED)." 45067e3242eSLina Iyer * Although devices are expected to update the next_wakeup after the end of 45167e3242eSLina Iyer * their usecase as well, it is possible the devices themselves may not know 45267e3242eSLina Iyer * about that, so stale @next will be ignored when powering off the domain. 45367e3242eSLina Iyer */ 45467e3242eSLina Iyer void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next) 45567e3242eSLina Iyer { 45667e3242eSLina Iyer struct generic_pm_domain_data *gpd_data; 45767e3242eSLina Iyer struct generic_pm_domain *genpd; 45867e3242eSLina Iyer 45967e3242eSLina Iyer genpd = dev_to_genpd_safe(dev); 46067e3242eSLina Iyer if (!genpd) 46167e3242eSLina Iyer return; 46267e3242eSLina Iyer 46367e3242eSLina Iyer gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 46467e3242eSLina Iyer gpd_data->next_wakeup = next; 46567e3242eSLina Iyer } 46667e3242eSLina Iyer EXPORT_SYMBOL_GPL(dev_pm_genpd_set_next_wakeup); 46767e3242eSLina Iyer 46886e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 469c8f0ea45SGeert Uytterhoeven { 470fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 471c8f0ea45SGeert Uytterhoeven ktime_t time_start; 472c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 473330e3932SUlf Hansson int ret; 474d4f81383SUlf Hansson 475d4f81383SUlf Hansson /* Notify consumers that we are about to power on. */ 476330e3932SUlf Hansson ret = raw_notifier_call_chain_robust(&genpd->power_notifiers, 477330e3932SUlf Hansson GENPD_NOTIFY_PRE_ON, 478330e3932SUlf Hansson GENPD_NOTIFY_OFF, NULL); 479d4f81383SUlf Hansson ret = notifier_to_errno(ret); 480d4f81383SUlf Hansson if (ret) 481330e3932SUlf Hansson return ret; 482c8f0ea45SGeert Uytterhoeven 483c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 484d4f81383SUlf Hansson goto out; 485c8f0ea45SGeert Uytterhoeven 486d4f81383SUlf Hansson if (!timed) { 487d4f81383SUlf Hansson ret = genpd->power_on(genpd); 488d4f81383SUlf Hansson if (ret) 489d4f81383SUlf Hansson goto err; 490d4f81383SUlf Hansson 491d4f81383SUlf Hansson goto out; 492d4f81383SUlf Hansson } 493a4630c61SGeert Uytterhoeven 494c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 495c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 496c8f0ea45SGeert Uytterhoeven if (ret) 497d4f81383SUlf Hansson goto err; 498c8f0ea45SGeert Uytterhoeven 499c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 500fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 501d4f81383SUlf Hansson goto out; 502c8f0ea45SGeert Uytterhoeven 503fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 504c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 5056d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 506c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 507c8f0ea45SGeert Uytterhoeven 508d4f81383SUlf Hansson out: 509d4f81383SUlf Hansson raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); 510d4f81383SUlf Hansson return 0; 511d4f81383SUlf Hansson err: 512d4f81383SUlf Hansson raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, 513d4f81383SUlf Hansson NULL); 514c8f0ea45SGeert Uytterhoeven return ret; 515c8f0ea45SGeert Uytterhoeven } 516c8f0ea45SGeert Uytterhoeven 51786e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 518c8f0ea45SGeert Uytterhoeven { 519fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 520c8f0ea45SGeert Uytterhoeven ktime_t time_start; 521c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 522330e3932SUlf Hansson int ret; 523d4f81383SUlf Hansson 524d4f81383SUlf Hansson /* Notify consumers that we are about to power off. */ 525330e3932SUlf Hansson ret = raw_notifier_call_chain_robust(&genpd->power_notifiers, 526330e3932SUlf Hansson GENPD_NOTIFY_PRE_OFF, 527330e3932SUlf Hansson GENPD_NOTIFY_ON, NULL); 528d4f81383SUlf Hansson ret = notifier_to_errno(ret); 529d4f81383SUlf Hansson if (ret) 530330e3932SUlf Hansson return ret; 531c8f0ea45SGeert Uytterhoeven 532c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 533d4f81383SUlf Hansson goto out; 534c8f0ea45SGeert Uytterhoeven 535d4f81383SUlf Hansson if (!timed) { 536d4f81383SUlf Hansson ret = genpd->power_off(genpd); 537d4f81383SUlf Hansson if (ret) 538d4f81383SUlf Hansson goto busy; 539d4f81383SUlf Hansson 540d4f81383SUlf Hansson goto out; 541d4f81383SUlf Hansson } 542a4630c61SGeert Uytterhoeven 543c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 544c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 5450cec68a9SAisheng Dong if (ret) 546d4f81383SUlf Hansson goto busy; 547c8f0ea45SGeert Uytterhoeven 548c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 549fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 550d4f81383SUlf Hansson goto out; 551c8f0ea45SGeert Uytterhoeven 552fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 553c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 5546d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 555c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 556c8f0ea45SGeert Uytterhoeven 557d4f81383SUlf Hansson out: 558d4f81383SUlf Hansson raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, 559d4f81383SUlf Hansson NULL); 5600cec68a9SAisheng Dong return 0; 561d4f81383SUlf Hansson busy: 562330e3932SUlf Hansson raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); 563d4f81383SUlf Hansson return ret; 564c8f0ea45SGeert Uytterhoeven } 565c8f0ea45SGeert Uytterhoeven 566f721889fSRafael J. Wysocki /** 56786e12eacSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 568a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 56929e47e21SUlf Hansson * 57086e12eacSUlf Hansson * Queue up the execution of genpd_power_off() unless it's already been done 57129e47e21SUlf Hansson * before. 57229e47e21SUlf Hansson */ 57329e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 57429e47e21SUlf Hansson { 57529e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 57629e47e21SUlf Hansson } 57729e47e21SUlf Hansson 57829e47e21SUlf Hansson /** 5791f8728b7SUlf Hansson * genpd_power_off - Remove power from a given PM domain. 5801f8728b7SUlf Hansson * @genpd: PM domain to power down. 5813c64649dSUlf Hansson * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the 5823c64649dSUlf Hansson * RPM status of the releated device is in an intermediate state, not yet turned 5833c64649dSUlf Hansson * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not 5843c64649dSUlf Hansson * be RPM_SUSPENDED, while it tries to power off the PM domain. 585763663c9SYang Yingliang * @depth: nesting count for lockdep. 5861f8728b7SUlf Hansson * 5871f8728b7SUlf Hansson * If all of the @genpd's devices have been suspended and all of its subdomains 5881f8728b7SUlf Hansson * have been powered down, remove power from @genpd. 5891f8728b7SUlf Hansson */ 5902da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, 5912da83545SUlf Hansson unsigned int depth) 5921f8728b7SUlf Hansson { 5931f8728b7SUlf Hansson struct pm_domain_data *pdd; 5941f8728b7SUlf Hansson struct gpd_link *link; 5951f8728b7SUlf Hansson unsigned int not_suspended = 0; 596f63816e4SUlf Hansson int ret; 5971f8728b7SUlf Hansson 5981f8728b7SUlf Hansson /* 5991f8728b7SUlf Hansson * Do not try to power off the domain in the following situations: 6001f8728b7SUlf Hansson * (1) The domain is already in the "power off" state. 6011f8728b7SUlf Hansson * (2) System suspend is in progress. 6021f8728b7SUlf Hansson */ 60341e2c8e0SUlf Hansson if (!genpd_status_on(genpd) || genpd->prepared_count > 0) 6041f8728b7SUlf Hansson return 0; 6051f8728b7SUlf Hansson 606ffaa42e8SUlf Hansson /* 607ffaa42e8SUlf Hansson * Abort power off for the PM domain in the following situations: 608ffaa42e8SUlf Hansson * (1) The domain is configured as always on. 609ffaa42e8SUlf Hansson * (2) When the domain has a subdomain being powered on. 610ffaa42e8SUlf Hansson */ 611ed61e18aSLeonard Crestez if (genpd_is_always_on(genpd) || 612ed61e18aSLeonard Crestez genpd_is_rpm_always_on(genpd) || 613ed61e18aSLeonard Crestez atomic_read(&genpd->sd_count) > 0) 6141f8728b7SUlf Hansson return -EBUSY; 6151f8728b7SUlf Hansson 6161f8728b7SUlf Hansson list_for_each_entry(pdd, &genpd->dev_list, list_node) { 6171f8728b7SUlf Hansson enum pm_qos_flags_status stat; 6181f8728b7SUlf Hansson 61920f97cafSRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF); 6201f8728b7SUlf Hansson if (stat > PM_QOS_FLAGS_NONE) 6211f8728b7SUlf Hansson return -EBUSY; 6221f8728b7SUlf Hansson 6231f8728b7SUlf Hansson /* 6241f8728b7SUlf Hansson * Do not allow PM domain to be powered off, when an IRQ safe 6251f8728b7SUlf Hansson * device is part of a non-IRQ safe domain. 6261f8728b7SUlf Hansson */ 6271f8728b7SUlf Hansson if (!pm_runtime_suspended(pdd->dev) || 6281f8728b7SUlf Hansson irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) 6291f8728b7SUlf Hansson not_suspended++; 6301f8728b7SUlf Hansson } 6311f8728b7SUlf Hansson 6323c64649dSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) 6331f8728b7SUlf Hansson return -EBUSY; 6341f8728b7SUlf Hansson 6351f8728b7SUlf Hansson if (genpd->gov && genpd->gov->power_down_ok) { 6361f8728b7SUlf Hansson if (!genpd->gov->power_down_ok(&genpd->domain)) 6371f8728b7SUlf Hansson return -EAGAIN; 6381f8728b7SUlf Hansson } 6391f8728b7SUlf Hansson 6402c9b7f87SUlf Hansson /* Default to shallowest state. */ 6412c9b7f87SUlf Hansson if (!genpd->gov) 6422c9b7f87SUlf Hansson genpd->state_idx = 0; 6432c9b7f87SUlf Hansson 644f63816e4SUlf Hansson /* Don't power off, if a child domain is waiting to power on. */ 6451f8728b7SUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 6461f8728b7SUlf Hansson return -EBUSY; 6471f8728b7SUlf Hansson 6481f8728b7SUlf Hansson ret = _genpd_power_off(genpd, true); 649c6a113b5SLina Iyer if (ret) { 650c6a113b5SLina Iyer genpd->states[genpd->state_idx].rejected++; 6511f8728b7SUlf Hansson return ret; 652c6a113b5SLina Iyer } 6531f8728b7SUlf Hansson 65449f618e1SUlf Hansson genpd->status = GENPD_STATE_OFF; 655afece3abSThara Gopinath genpd_update_accounting(genpd); 656c6a113b5SLina Iyer genpd->states[genpd->state_idx].usage++; 6571f8728b7SUlf Hansson 6588d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 6598d87ae48SKees Cook genpd_sd_counter_dec(link->parent); 6608d87ae48SKees Cook genpd_lock_nested(link->parent, depth + 1); 6618d87ae48SKees Cook genpd_power_off(link->parent, false, depth + 1); 6628d87ae48SKees Cook genpd_unlock(link->parent); 6631f8728b7SUlf Hansson } 6641f8728b7SUlf Hansson 6651f8728b7SUlf Hansson return 0; 6661f8728b7SUlf Hansson } 6671f8728b7SUlf Hansson 6681f8728b7SUlf Hansson /** 6698d87ae48SKees Cook * genpd_power_on - Restore power to a given PM domain and its parents. 6705248051bSRafael J. Wysocki * @genpd: PM domain to power up. 6710106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 6725248051bSRafael J. Wysocki * 6738d87ae48SKees Cook * Restore power to @genpd and all of its parents so that it is possible to 6745248051bSRafael J. Wysocki * resume a device belonging to it. 6755248051bSRafael J. Wysocki */ 67686e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 6775248051bSRafael J. Wysocki { 6785063ce15SRafael J. Wysocki struct gpd_link *link; 6795248051bSRafael J. Wysocki int ret = 0; 6805248051bSRafael J. Wysocki 68141e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 6823f241775SRafael J. Wysocki return 0; 6835248051bSRafael J. Wysocki 6845063ce15SRafael J. Wysocki /* 6855063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 6868d87ae48SKees Cook * executed, unless one of the parents' .power_on() callbacks fiddles 6875063ce15SRafael J. Wysocki * with it. 6885063ce15SRafael J. Wysocki */ 6898d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 6908d87ae48SKees Cook struct generic_pm_domain *parent = link->parent; 6915248051bSRafael J. Wysocki 6928d87ae48SKees Cook genpd_sd_counter_inc(parent); 6930106ef51SMarek Szyprowski 6948d87ae48SKees Cook genpd_lock_nested(parent, depth + 1); 6958d87ae48SKees Cook ret = genpd_power_on(parent, depth + 1); 6968d87ae48SKees Cook genpd_unlock(parent); 6970106ef51SMarek Szyprowski 6985063ce15SRafael J. Wysocki if (ret) { 6998d87ae48SKees Cook genpd_sd_counter_dec(parent); 7009e08cf42SRafael J. Wysocki goto err; 7015248051bSRafael J. Wysocki } 7025063ce15SRafael J. Wysocki } 7035248051bSRafael J. Wysocki 70486e12eacSUlf Hansson ret = _genpd_power_on(genpd, true); 7059e08cf42SRafael J. Wysocki if (ret) 7069e08cf42SRafael J. Wysocki goto err; 7070140d8bdSRafael J. Wysocki 70849f618e1SUlf Hansson genpd->status = GENPD_STATE_ON; 709afece3abSThara Gopinath genpd_update_accounting(genpd); 710afece3abSThara Gopinath 7113f241775SRafael J. Wysocki return 0; 7129e08cf42SRafael J. Wysocki 7139e08cf42SRafael J. Wysocki err: 71429e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 7158d87ae48SKees Cook &genpd->child_links, 7168d87ae48SKees Cook child_node) { 7178d87ae48SKees Cook genpd_sd_counter_dec(link->parent); 7188d87ae48SKees Cook genpd_lock_nested(link->parent, depth + 1); 7198d87ae48SKees Cook genpd_power_off(link->parent, false, depth + 1); 7208d87ae48SKees Cook genpd_unlock(link->parent); 72129e47e21SUlf Hansson } 7229e08cf42SRafael J. Wysocki 7233f241775SRafael J. Wysocki return ret; 7243f241775SRafael J. Wysocki } 7253f241775SRafael J. Wysocki 726ea71c596SUlf Hansson static int genpd_dev_pm_start(struct device *dev) 727ea71c596SUlf Hansson { 728ea71c596SUlf Hansson struct generic_pm_domain *genpd = dev_to_genpd(dev); 729ea71c596SUlf Hansson 730ea71c596SUlf Hansson return genpd_start_dev(genpd, dev); 731ea71c596SUlf Hansson } 732ea71c596SUlf Hansson 7336ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 7346ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 7356ff7bb0dSRafael J. Wysocki { 7366ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 7376ff7bb0dSRafael J. Wysocki struct device *dev; 7386ff7bb0dSRafael J. Wysocki 7396ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 7406ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 7416ff7bb0dSRafael J. Wysocki 7426ff7bb0dSRafael J. Wysocki for (;;) { 7436ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 7446ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 7456ff7bb0dSRafael J. Wysocki 7466ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 7476ff7bb0dSRafael J. Wysocki 7486ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 7496ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 750b4883ca4SViresh Kumar if (pdd) { 7516ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 7526ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 7536ff7bb0dSRafael J. Wysocki } else { 7546ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 7556ff7bb0dSRafael J. Wysocki } 7566ff7bb0dSRafael J. Wysocki 7576ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 7586ff7bb0dSRafael J. Wysocki 7596ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 76035241d12SLina Iyer genpd_lock(genpd); 7616ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 76235241d12SLina Iyer genpd_unlock(genpd); 7636ff7bb0dSRafael J. Wysocki } 7646ff7bb0dSRafael J. Wysocki 7656ff7bb0dSRafael J. Wysocki dev = dev->parent; 7666ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 7676ff7bb0dSRafael J. Wysocki break; 7686ff7bb0dSRafael J. Wysocki } 7696ff7bb0dSRafael J. Wysocki 7706ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 7716ff7bb0dSRafael J. Wysocki } 7726ff7bb0dSRafael J. Wysocki 7735248051bSRafael J. Wysocki /** 774f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 775f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 776f721889fSRafael J. Wysocki */ 777f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 778f721889fSRafael J. Wysocki { 779f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 780f721889fSRafael J. Wysocki 781f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 782f721889fSRafael J. Wysocki 78335241d12SLina Iyer genpd_lock(genpd); 7842da83545SUlf Hansson genpd_power_off(genpd, false, 0); 78535241d12SLina Iyer genpd_unlock(genpd); 786f721889fSRafael J. Wysocki } 787f721889fSRafael J. Wysocki 788f721889fSRafael J. Wysocki /** 78954eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 79054eeddbfSUlf Hansson * @dev: Device to handle. 79154eeddbfSUlf Hansson */ 79254eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 79354eeddbfSUlf Hansson { 79454eeddbfSUlf Hansson int (*cb)(struct device *__dev); 79554eeddbfSUlf Hansson 79654eeddbfSUlf Hansson if (dev->type && dev->type->pm) 79754eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 79854eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 79954eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 80054eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 80154eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 80254eeddbfSUlf Hansson else 80354eeddbfSUlf Hansson cb = NULL; 80454eeddbfSUlf Hansson 80554eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 80654eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 80754eeddbfSUlf Hansson 80854eeddbfSUlf Hansson return cb ? cb(dev) : 0; 80954eeddbfSUlf Hansson } 81054eeddbfSUlf Hansson 81154eeddbfSUlf Hansson /** 81254eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 81354eeddbfSUlf Hansson * @dev: Device to handle. 81454eeddbfSUlf Hansson */ 81554eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 81654eeddbfSUlf Hansson { 81754eeddbfSUlf Hansson int (*cb)(struct device *__dev); 81854eeddbfSUlf Hansson 81954eeddbfSUlf Hansson if (dev->type && dev->type->pm) 82054eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 82154eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 82254eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 82354eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 82454eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 82554eeddbfSUlf Hansson else 82654eeddbfSUlf Hansson cb = NULL; 82754eeddbfSUlf Hansson 82854eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 82954eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 83054eeddbfSUlf Hansson 83154eeddbfSUlf Hansson return cb ? cb(dev) : 0; 83254eeddbfSUlf Hansson } 83354eeddbfSUlf Hansson 83454eeddbfSUlf Hansson /** 835795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 836f721889fSRafael J. Wysocki * @dev: Device to suspend. 837f721889fSRafael J. Wysocki * 838f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 839f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 840f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 841f721889fSRafael J. Wysocki */ 842795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 843f721889fSRafael J. Wysocki { 844f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 8459df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 8462b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 847ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 8482b1d88cdSUlf Hansson ktime_t time_start; 8492b1d88cdSUlf Hansson s64 elapsed_ns; 850d5e4cbfeSRafael J. Wysocki int ret; 851f721889fSRafael J. Wysocki 852f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 853f721889fSRafael J. Wysocki 8545248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 8555248051bSRafael J. Wysocki if (IS_ERR(genpd)) 856f721889fSRafael J. Wysocki return -EINVAL; 857f721889fSRafael J. Wysocki 858ffe12855SUlf Hansson /* 859ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 860ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 861ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 862ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 863ffe12855SUlf Hansson */ 8649df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 8659df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 866b02c999aSRafael J. Wysocki return -EBUSY; 867b02c999aSRafael J. Wysocki 8682b1d88cdSUlf Hansson /* Measure suspend latency. */ 869d33d5a6cSLinus Torvalds time_start = 0; 870ffe12855SUlf Hansson if (runtime_pm) 8712b1d88cdSUlf Hansson time_start = ktime_get(); 8722b1d88cdSUlf Hansson 87354eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 874f721889fSRafael J. Wysocki if (ret) 87517b75ecaSRafael J. Wysocki return ret; 87617b75ecaSRafael J. Wysocki 8772b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 878ba2bbfbfSUlf Hansson if (ret) { 87954eeddbfSUlf Hansson __genpd_runtime_resume(dev); 880ba2bbfbfSUlf Hansson return ret; 881ba2bbfbfSUlf Hansson } 882ba2bbfbfSUlf Hansson 8832b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 884ffe12855SUlf Hansson if (runtime_pm) { 8852b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 8862b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 8872b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 8882b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 8892b1d88cdSUlf Hansson elapsed_ns); 8902b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 8912b1d88cdSUlf Hansson td->constraint_changed = true; 8922b1d88cdSUlf Hansson } 893ffe12855SUlf Hansson } 8942b1d88cdSUlf Hansson 8950aa2a221SRafael J. Wysocki /* 896d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 897d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 8980aa2a221SRafael J. Wysocki */ 899d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 9000aa2a221SRafael J. Wysocki return 0; 9010aa2a221SRafael J. Wysocki 90235241d12SLina Iyer genpd_lock(genpd); 9032da83545SUlf Hansson genpd_power_off(genpd, true, 0); 90435241d12SLina Iyer genpd_unlock(genpd); 905f721889fSRafael J. Wysocki 906f721889fSRafael J. Wysocki return 0; 907f721889fSRafael J. Wysocki } 908f721889fSRafael J. Wysocki 909f721889fSRafael J. Wysocki /** 910795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 911f721889fSRafael J. Wysocki * @dev: Device to resume. 912f721889fSRafael J. Wysocki * 913f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 914f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 915f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 916f721889fSRafael J. Wysocki */ 917795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 918f721889fSRafael J. Wysocki { 919f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 9202b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 921ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 9222b1d88cdSUlf Hansson ktime_t time_start; 9232b1d88cdSUlf Hansson s64 elapsed_ns; 924f721889fSRafael J. Wysocki int ret; 925ba2bbfbfSUlf Hansson bool timed = true; 926f721889fSRafael J. Wysocki 927f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 928f721889fSRafael J. Wysocki 9295248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 9305248051bSRafael J. Wysocki if (IS_ERR(genpd)) 931f721889fSRafael J. Wysocki return -EINVAL; 932f721889fSRafael J. Wysocki 933d716f479SLina Iyer /* 934d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 935d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 936d716f479SLina Iyer */ 937d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 938ba2bbfbfSUlf Hansson timed = false; 939ba2bbfbfSUlf Hansson goto out; 940ba2bbfbfSUlf Hansson } 9410aa2a221SRafael J. Wysocki 94235241d12SLina Iyer genpd_lock(genpd); 94386e12eacSUlf Hansson ret = genpd_power_on(genpd, 0); 94435241d12SLina Iyer genpd_unlock(genpd); 945ba2bbfbfSUlf Hansson 946ba2bbfbfSUlf Hansson if (ret) 9473f241775SRafael J. Wysocki return ret; 948c6d22b37SRafael J. Wysocki 949ba2bbfbfSUlf Hansson out: 9502b1d88cdSUlf Hansson /* Measure resume latency. */ 951ab51e6baSAugusto Mecking Caringi time_start = 0; 952ffe12855SUlf Hansson if (timed && runtime_pm) 9532b1d88cdSUlf Hansson time_start = ktime_get(); 9542b1d88cdSUlf Hansson 955076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 956076395caSLaurent Pinchart if (ret) 957076395caSLaurent Pinchart goto err_poweroff; 958076395caSLaurent Pinchart 95954eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 960076395caSLaurent Pinchart if (ret) 961076395caSLaurent Pinchart goto err_stop; 9622b1d88cdSUlf Hansson 9632b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 964ffe12855SUlf Hansson if (timed && runtime_pm) { 9652b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 9662b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 9672b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 9682b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 9692b1d88cdSUlf Hansson elapsed_ns); 9702b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 9712b1d88cdSUlf Hansson td->constraint_changed = true; 9722b1d88cdSUlf Hansson } 9732b1d88cdSUlf Hansson } 97417b75ecaSRafael J. Wysocki 975f721889fSRafael J. Wysocki return 0; 976076395caSLaurent Pinchart 977076395caSLaurent Pinchart err_stop: 978076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 979076395caSLaurent Pinchart err_poweroff: 9806dc466d3SAbaci Team if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { 98135241d12SLina Iyer genpd_lock(genpd); 9822da83545SUlf Hansson genpd_power_off(genpd, true, 0); 98335241d12SLina Iyer genpd_unlock(genpd); 984076395caSLaurent Pinchart } 985076395caSLaurent Pinchart 986076395caSLaurent Pinchart return ret; 987f721889fSRafael J. Wysocki } 988f721889fSRafael J. Wysocki 98939ac5ba5STushar Behera static bool pd_ignore_unused; 99039ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 99139ac5ba5STushar Behera { 99239ac5ba5STushar Behera pd_ignore_unused = true; 99339ac5ba5STushar Behera return 1; 99439ac5ba5STushar Behera } 99539ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 99639ac5ba5STushar Behera 99717f2ae7fSRafael J. Wysocki /** 99886e12eacSUlf Hansson * genpd_power_off_unused - Power off all PM domains with no devices in use. 99917f2ae7fSRafael J. Wysocki */ 100086e12eacSUlf Hansson static int __init genpd_power_off_unused(void) 100117f2ae7fSRafael J. Wysocki { 100217f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 100317f2ae7fSRafael J. Wysocki 100439ac5ba5STushar Behera if (pd_ignore_unused) { 100539ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 1006bb4b72fcSUlf Hansson return 0; 100739ac5ba5STushar Behera } 100839ac5ba5STushar Behera 100917f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 101017f2ae7fSRafael J. Wysocki 101117f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 101217f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 101317f2ae7fSRafael J. Wysocki 101417f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 101517f2ae7fSRafael J. Wysocki 10162fe71dcdSUlf Hansson return 0; 10172fe71dcdSUlf Hansson } 101886e12eacSUlf Hansson late_initcall(genpd_power_off_unused); 10192fe71dcdSUlf Hansson 10200159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 10210159ec67SJon Hunter 1022596ba34bSRafael J. Wysocki /** 10238d87ae48SKees Cook * genpd_sync_power_off - Synchronously power off a PM domain and its parents. 1024596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 10250883ac03SUlf Hansson * @use_lock: use the lock. 10260883ac03SUlf Hansson * @depth: nesting count for lockdep. 1027596ba34bSRafael J. Wysocki * 1028596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 10298d87ae48SKees Cook * hibernation) and do that if so. Also, in that case propagate to its parents. 1030596ba34bSRafael J. Wysocki * 103177f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 10320883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 10330883ac03SUlf Hansson * these cases the lock must be held. 1034596ba34bSRafael J. Wysocki */ 10350883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, 10360883ac03SUlf Hansson unsigned int depth) 1037596ba34bSRafael J. Wysocki { 10385063ce15SRafael J. Wysocki struct gpd_link *link; 1039596ba34bSRafael J. Wysocki 1040ffaa42e8SUlf Hansson if (!genpd_status_on(genpd) || genpd_is_always_on(genpd)) 1041596ba34bSRafael J. Wysocki return; 1042596ba34bSRafael J. Wysocki 1043c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 1044c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 1045596ba34bSRafael J. Wysocki return; 1046596ba34bSRafael J. Wysocki 1047fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 1048fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 10491c14967cSUlf Hansson if (_genpd_power_off(genpd, false)) 10501c14967cSUlf Hansson return; 1051596ba34bSRafael J. Wysocki 105249f618e1SUlf Hansson genpd->status = GENPD_STATE_OFF; 10535063ce15SRafael J. Wysocki 10548d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 10558d87ae48SKees Cook genpd_sd_counter_dec(link->parent); 10560883ac03SUlf Hansson 10570883ac03SUlf Hansson if (use_lock) 10588d87ae48SKees Cook genpd_lock_nested(link->parent, depth + 1); 10590883ac03SUlf Hansson 10608d87ae48SKees Cook genpd_sync_power_off(link->parent, use_lock, depth + 1); 10610883ac03SUlf Hansson 10620883ac03SUlf Hansson if (use_lock) 10638d87ae48SKees Cook genpd_unlock(link->parent); 1064596ba34bSRafael J. Wysocki } 1065596ba34bSRafael J. Wysocki } 1066596ba34bSRafael J. Wysocki 1067596ba34bSRafael J. Wysocki /** 10688d87ae48SKees Cook * genpd_sync_power_on - Synchronously power on a PM domain and its parents. 1069802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 10700883ac03SUlf Hansson * @use_lock: use the lock. 10710883ac03SUlf Hansson * @depth: nesting count for lockdep. 1072802d8b49SRafael J. Wysocki * 107377f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 10740883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 10750883ac03SUlf Hansson * these cases the lock must be held. 1076802d8b49SRafael J. Wysocki */ 10770883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, 10780883ac03SUlf Hansson unsigned int depth) 1079802d8b49SRafael J. Wysocki { 1080802d8b49SRafael J. Wysocki struct gpd_link *link; 1081802d8b49SRafael J. Wysocki 108241e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 1083802d8b49SRafael J. Wysocki return; 1084802d8b49SRafael J. Wysocki 10858d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 10868d87ae48SKees Cook genpd_sd_counter_inc(link->parent); 10870883ac03SUlf Hansson 10880883ac03SUlf Hansson if (use_lock) 10898d87ae48SKees Cook genpd_lock_nested(link->parent, depth + 1); 10900883ac03SUlf Hansson 10918d87ae48SKees Cook genpd_sync_power_on(link->parent, use_lock, depth + 1); 10920883ac03SUlf Hansson 10930883ac03SUlf Hansson if (use_lock) 10948d87ae48SKees Cook genpd_unlock(link->parent); 1095802d8b49SRafael J. Wysocki } 1096802d8b49SRafael J. Wysocki 109786e12eacSUlf Hansson _genpd_power_on(genpd, false); 109849f618e1SUlf Hansson genpd->status = GENPD_STATE_ON; 1099802d8b49SRafael J. Wysocki } 1100802d8b49SRafael J. Wysocki 1101802d8b49SRafael J. Wysocki /** 11029e9704eaSUlf Hansson * genpd_prepare - Start power transition of a device in a PM domain. 1103596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 1104596ba34bSRafael J. Wysocki * 1105596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 1106596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 1107596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 1108596ba34bSRafael J. Wysocki * consisting of I/O devices. 1109596ba34bSRafael J. Wysocki */ 11109e9704eaSUlf Hansson static int genpd_prepare(struct device *dev) 1111596ba34bSRafael J. Wysocki { 1112596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1113b6c10c84SRafael J. Wysocki int ret; 1114596ba34bSRafael J. Wysocki 1115596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1116596ba34bSRafael J. Wysocki 1117596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1118596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1119596ba34bSRafael J. Wysocki return -EINVAL; 1120596ba34bSRafael J. Wysocki 112135241d12SLina Iyer genpd_lock(genpd); 1122596ba34bSRafael J. Wysocki 112339dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 112465533bbfSRafael J. Wysocki genpd->suspended_count = 0; 112517b75ecaSRafael J. Wysocki 112635241d12SLina Iyer genpd_unlock(genpd); 1127596ba34bSRafael J. Wysocki 1128b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 11295241ab40SUlf Hansson if (ret < 0) { 113035241d12SLina Iyer genpd_lock(genpd); 1131b6c10c84SRafael J. Wysocki 113239dd0f23SUlf Hansson genpd->prepared_count--; 1133b6c10c84SRafael J. Wysocki 113435241d12SLina Iyer genpd_unlock(genpd); 1135b6c10c84SRafael J. Wysocki } 113617b75ecaSRafael J. Wysocki 11375241ab40SUlf Hansson /* Never return 1, as genpd don't cope with the direct_complete path. */ 11385241ab40SUlf Hansson return ret >= 0 ? 0 : ret; 1139596ba34bSRafael J. Wysocki } 1140596ba34bSRafael J. Wysocki 1141596ba34bSRafael J. Wysocki /** 114210da6542SMikko Perttunen * genpd_finish_suspend - Completion of suspend or hibernation of device in an 114310da6542SMikko Perttunen * I/O pm domain. 11440496c8aeSRafael J. Wysocki * @dev: Device to suspend. 114510da6542SMikko Perttunen * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback. 11460496c8aeSRafael J. Wysocki * 11470496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 11480496c8aeSRafael J. Wysocki * been stopped. 11490496c8aeSRafael J. Wysocki */ 115010da6542SMikko Perttunen static int genpd_finish_suspend(struct device *dev, bool poweroff) 11510496c8aeSRafael J. Wysocki { 11520496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1153a935424bSUlf Hansson int ret = 0; 1154596ba34bSRafael J. Wysocki 11550496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11560496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11570496c8aeSRafael J. Wysocki return -EINVAL; 11580496c8aeSRafael J. Wysocki 115910da6542SMikko Perttunen if (poweroff) 116010da6542SMikko Perttunen ret = pm_generic_poweroff_noirq(dev); 116110da6542SMikko Perttunen else 116210da6542SMikko Perttunen ret = pm_generic_suspend_noirq(dev); 116310da6542SMikko Perttunen if (ret) 116410da6542SMikko Perttunen return ret; 116510da6542SMikko Perttunen 11664e1d9a73SPatrice Chotard if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) 1167a935424bSUlf Hansson return 0; 1168a935424bSUlf Hansson 116917218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 117017218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 117117218e00SRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 1172a935424bSUlf Hansson if (ret) { 1173a935424bSUlf Hansson if (poweroff) 1174a935424bSUlf Hansson pm_generic_restore_noirq(dev); 1175a935424bSUlf Hansson else 1176a935424bSUlf Hansson pm_generic_resume_noirq(dev); 1177122a2237SUlf Hansson return ret; 1178122a2237SUlf Hansson } 1179a935424bSUlf Hansson } 1180122a2237SUlf Hansson 11810883ac03SUlf Hansson genpd_lock(genpd); 1182596ba34bSRafael J. Wysocki genpd->suspended_count++; 11830883ac03SUlf Hansson genpd_sync_power_off(genpd, true, 0); 11840883ac03SUlf Hansson genpd_unlock(genpd); 1185596ba34bSRafael J. Wysocki 1186596ba34bSRafael J. Wysocki return 0; 1187596ba34bSRafael J. Wysocki } 1188596ba34bSRafael J. Wysocki 1189596ba34bSRafael J. Wysocki /** 11909e9704eaSUlf Hansson * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 119110da6542SMikko Perttunen * @dev: Device to suspend. 119210da6542SMikko Perttunen * 119310da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 119410da6542SMikko Perttunen * been stopped. 119510da6542SMikko Perttunen */ 11969e9704eaSUlf Hansson static int genpd_suspend_noirq(struct device *dev) 119710da6542SMikko Perttunen { 119810da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 119910da6542SMikko Perttunen 120010da6542SMikko Perttunen return genpd_finish_suspend(dev, false); 120110da6542SMikko Perttunen } 120210da6542SMikko Perttunen 120310da6542SMikko Perttunen /** 12049e9704eaSUlf Hansson * genpd_resume_noirq - Start of resume of device in an I/O PM domain. 1205596ba34bSRafael J. Wysocki * @dev: Device to resume. 1206596ba34bSRafael J. Wysocki * 12070496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 1208596ba34bSRafael J. Wysocki */ 12099e9704eaSUlf Hansson static int genpd_resume_noirq(struct device *dev) 1210596ba34bSRafael J. Wysocki { 1211596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1212a935424bSUlf Hansson int ret; 1213596ba34bSRafael J. Wysocki 1214596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1215596ba34bSRafael J. Wysocki 1216596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1217596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1218596ba34bSRafael J. Wysocki return -EINVAL; 1219596ba34bSRafael J. Wysocki 12204e1d9a73SPatrice Chotard if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd)) 1221a935424bSUlf Hansson return pm_generic_resume_noirq(dev); 1222596ba34bSRafael J. Wysocki 12230883ac03SUlf Hansson genpd_lock(genpd); 12240883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 1225596ba34bSRafael J. Wysocki genpd->suspended_count--; 12260883ac03SUlf Hansson genpd_unlock(genpd); 1227596ba34bSRafael J. Wysocki 122817218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 122917218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 123017218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 123110da6542SMikko Perttunen if (ret) 123210da6542SMikko Perttunen return ret; 1233a935424bSUlf Hansson } 123410da6542SMikko Perttunen 1235a935424bSUlf Hansson return pm_generic_resume_noirq(dev); 1236596ba34bSRafael J. Wysocki } 1237596ba34bSRafael J. Wysocki 1238596ba34bSRafael J. Wysocki /** 12399e9704eaSUlf Hansson * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1240596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1241596ba34bSRafael J. Wysocki * 1242596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1243596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1244596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1245596ba34bSRafael J. Wysocki * devices. 1246596ba34bSRafael J. Wysocki */ 12479e9704eaSUlf Hansson static int genpd_freeze_noirq(struct device *dev) 1248596ba34bSRafael J. Wysocki { 1249d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1250122a2237SUlf Hansson int ret = 0; 1251596ba34bSRafael J. Wysocki 1252596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1253596ba34bSRafael J. Wysocki 1254596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1255596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1256596ba34bSRafael J. Wysocki return -EINVAL; 1257596ba34bSRafael J. Wysocki 125810da6542SMikko Perttunen ret = pm_generic_freeze_noirq(dev); 125910da6542SMikko Perttunen if (ret) 126010da6542SMikko Perttunen return ret; 126110da6542SMikko Perttunen 126217218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 126317218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) 126417218e00SRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 1265122a2237SUlf Hansson 1266122a2237SUlf Hansson return ret; 1267596ba34bSRafael J. Wysocki } 1268596ba34bSRafael J. Wysocki 1269596ba34bSRafael J. Wysocki /** 12709e9704eaSUlf Hansson * genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1271596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1272596ba34bSRafael J. Wysocki * 12730496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 12740496c8aeSRafael J. Wysocki * before the system transition. 1275596ba34bSRafael J. Wysocki */ 12769e9704eaSUlf Hansson static int genpd_thaw_noirq(struct device *dev) 1277596ba34bSRafael J. Wysocki { 1278d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1279122a2237SUlf Hansson int ret = 0; 1280596ba34bSRafael J. Wysocki 1281596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1282596ba34bSRafael J. Wysocki 1283596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1284596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1285596ba34bSRafael J. Wysocki return -EINVAL; 1286596ba34bSRafael J. Wysocki 128717218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 128817218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 128917218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 129010da6542SMikko Perttunen if (ret) 1291122a2237SUlf Hansson return ret; 12920496c8aeSRafael J. Wysocki } 1293596ba34bSRafael J. Wysocki 129410da6542SMikko Perttunen return pm_generic_thaw_noirq(dev); 129510da6542SMikko Perttunen } 129610da6542SMikko Perttunen 129710da6542SMikko Perttunen /** 12989e9704eaSUlf Hansson * genpd_poweroff_noirq - Completion of hibernation of device in an 129910da6542SMikko Perttunen * I/O PM domain. 130010da6542SMikko Perttunen * @dev: Device to poweroff. 130110da6542SMikko Perttunen * 130210da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 130310da6542SMikko Perttunen * been stopped. 130410da6542SMikko Perttunen */ 13059e9704eaSUlf Hansson static int genpd_poweroff_noirq(struct device *dev) 130610da6542SMikko Perttunen { 130710da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 130810da6542SMikko Perttunen 130910da6542SMikko Perttunen return genpd_finish_suspend(dev, true); 131010da6542SMikko Perttunen } 131110da6542SMikko Perttunen 13120496c8aeSRafael J. Wysocki /** 13139e9704eaSUlf Hansson * genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1314596ba34bSRafael J. Wysocki * @dev: Device to resume. 1315596ba34bSRafael J. Wysocki * 13160496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 13170496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1318596ba34bSRafael J. Wysocki */ 13199e9704eaSUlf Hansson static int genpd_restore_noirq(struct device *dev) 1320596ba34bSRafael J. Wysocki { 1321596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1322122a2237SUlf Hansson int ret = 0; 1323596ba34bSRafael J. Wysocki 1324596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1325596ba34bSRafael J. Wysocki 1326596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1327596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1328596ba34bSRafael J. Wysocki return -EINVAL; 1329596ba34bSRafael J. Wysocki 1330596ba34bSRafael J. Wysocki /* 133165533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 133265533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 133365533bbfSRafael J. Wysocki */ 13340883ac03SUlf Hansson genpd_lock(genpd); 1335505a70b7SGeert Uytterhoeven if (genpd->suspended_count++ == 0) { 133665533bbfSRafael J. Wysocki /* 133765533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 133886e12eacSUlf Hansson * so make it appear as powered off to genpd_sync_power_on(), 1339802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1340596ba34bSRafael J. Wysocki */ 134149f618e1SUlf Hansson genpd->status = GENPD_STATE_OFF; 1342505a70b7SGeert Uytterhoeven } 134318dd2eceSRafael J. Wysocki 13440883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 13450883ac03SUlf Hansson genpd_unlock(genpd); 1346596ba34bSRafael J. Wysocki 134717218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 134817218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 134917218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 135010da6542SMikko Perttunen if (ret) 1351122a2237SUlf Hansson return ret; 1352596ba34bSRafael J. Wysocki } 1353596ba34bSRafael J. Wysocki 135410da6542SMikko Perttunen return pm_generic_restore_noirq(dev); 135510da6542SMikko Perttunen } 135610da6542SMikko Perttunen 1357596ba34bSRafael J. Wysocki /** 13589e9704eaSUlf Hansson * genpd_complete - Complete power transition of a device in a power domain. 1359596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1360596ba34bSRafael J. Wysocki * 1361596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1362596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1363596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1364596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1365596ba34bSRafael J. Wysocki */ 13669e9704eaSUlf Hansson static void genpd_complete(struct device *dev) 1367596ba34bSRafael J. Wysocki { 1368596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1369596ba34bSRafael J. Wysocki 1370596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1371596ba34bSRafael J. Wysocki 1372596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1373596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1374596ba34bSRafael J. Wysocki return; 1375596ba34bSRafael J. Wysocki 13764d23a5e8SUlf Hansson pm_generic_complete(dev); 13774d23a5e8SUlf Hansson 137835241d12SLina Iyer genpd_lock(genpd); 1379596ba34bSRafael J. Wysocki 138039dd0f23SUlf Hansson genpd->prepared_count--; 13814d23a5e8SUlf Hansson if (!genpd->prepared_count) 13824d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1383596ba34bSRafael J. Wysocki 138435241d12SLina Iyer genpd_unlock(genpd); 1385596ba34bSRafael J. Wysocki } 1386596ba34bSRafael J. Wysocki 1387fc519890SUlf Hansson static void genpd_switch_state(struct device *dev, bool suspend) 138877f827deSRafael J. Wysocki { 138977f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 1390b9795a3eSUlf Hansson bool use_lock; 139177f827deSRafael J. Wysocki 1392fe0c2baaSUlf Hansson genpd = dev_to_genpd_safe(dev); 1393fe0c2baaSUlf Hansson if (!genpd) 139477f827deSRafael J. Wysocki return; 139577f827deSRafael J. Wysocki 1396b9795a3eSUlf Hansson use_lock = genpd_is_irq_safe(genpd); 1397b9795a3eSUlf Hansson 1398b9795a3eSUlf Hansson if (use_lock) 1399b9795a3eSUlf Hansson genpd_lock(genpd); 1400b9795a3eSUlf Hansson 140177f827deSRafael J. Wysocki if (suspend) { 140277f827deSRafael J. Wysocki genpd->suspended_count++; 1403b9795a3eSUlf Hansson genpd_sync_power_off(genpd, use_lock, 0); 140477f827deSRafael J. Wysocki } else { 1405b9795a3eSUlf Hansson genpd_sync_power_on(genpd, use_lock, 0); 140677f827deSRafael J. Wysocki genpd->suspended_count--; 140777f827deSRafael J. Wysocki } 1408b9795a3eSUlf Hansson 1409b9795a3eSUlf Hansson if (use_lock) 1410b9795a3eSUlf Hansson genpd_unlock(genpd); 141177f827deSRafael J. Wysocki } 1412d47e6464SUlf Hansson 1413fc519890SUlf Hansson /** 1414fc519890SUlf Hansson * dev_pm_genpd_suspend - Synchronously try to suspend the genpd for @dev 1415fc519890SUlf Hansson * @dev: The device that is attached to the genpd, that can be suspended. 1416fc519890SUlf Hansson * 1417fc519890SUlf Hansson * This routine should typically be called for a device that needs to be 1418b9795a3eSUlf Hansson * suspended during the syscore suspend phase. It may also be called during 1419b9795a3eSUlf Hansson * suspend-to-idle to suspend a corresponding CPU device that is attached to a 1420b9795a3eSUlf Hansson * genpd. 1421fc519890SUlf Hansson */ 1422fc519890SUlf Hansson void dev_pm_genpd_suspend(struct device *dev) 1423d47e6464SUlf Hansson { 1424fc519890SUlf Hansson genpd_switch_state(dev, true); 1425d47e6464SUlf Hansson } 1426fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_suspend); 1427d47e6464SUlf Hansson 1428fc519890SUlf Hansson /** 1429fc519890SUlf Hansson * dev_pm_genpd_resume - Synchronously try to resume the genpd for @dev 1430fc519890SUlf Hansson * @dev: The device that is attached to the genpd, which needs to be resumed. 1431fc519890SUlf Hansson * 1432fc519890SUlf Hansson * This routine should typically be called for a device that needs to be resumed 1433b9795a3eSUlf Hansson * during the syscore resume phase. It may also be called during suspend-to-idle 1434b9795a3eSUlf Hansson * to resume a corresponding CPU device that is attached to a genpd. 1435fc519890SUlf Hansson */ 1436fc519890SUlf Hansson void dev_pm_genpd_resume(struct device *dev) 1437d47e6464SUlf Hansson { 1438fc519890SUlf Hansson genpd_switch_state(dev, false); 1439d47e6464SUlf Hansson } 1440fc519890SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_resume); 144177f827deSRafael J. Wysocki 1442d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1443596ba34bSRafael J. Wysocki 14449e9704eaSUlf Hansson #define genpd_prepare NULL 14459e9704eaSUlf Hansson #define genpd_suspend_noirq NULL 14469e9704eaSUlf Hansson #define genpd_resume_noirq NULL 14479e9704eaSUlf Hansson #define genpd_freeze_noirq NULL 14489e9704eaSUlf Hansson #define genpd_thaw_noirq NULL 14499e9704eaSUlf Hansson #define genpd_poweroff_noirq NULL 14509e9704eaSUlf Hansson #define genpd_restore_noirq NULL 14519e9704eaSUlf Hansson #define genpd_complete NULL 1452596ba34bSRafael J. Wysocki 1453596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1454596ba34bSRafael J. Wysocki 1455a174920dSUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev) 14561d5fcfecSRafael J. Wysocki { 14571d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 14583e235685SUlf Hansson int ret; 14593e235685SUlf Hansson 14603e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 14613e235685SUlf Hansson if (ret) 14623e235685SUlf Hansson return ERR_PTR(ret); 14631d5fcfecSRafael J. Wysocki 14641d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 14653e235685SUlf Hansson if (!gpd_data) { 14663e235685SUlf Hansson ret = -ENOMEM; 14673e235685SUlf Hansson goto err_put; 14683e235685SUlf Hansson } 14691d5fcfecSRafael J. Wysocki 1470f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1471f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 14720759e80bSRafael J. Wysocki gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; 1473f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 147467e3242eSLina Iyer gpd_data->next_wakeup = KTIME_MAX; 1475f104e1e5SUlf Hansson 1476f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1477f104e1e5SUlf Hansson 1478f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1479f104e1e5SUlf Hansson ret = -EINVAL; 1480f104e1e5SUlf Hansson goto err_free; 1481f104e1e5SUlf Hansson } 1482f104e1e5SUlf Hansson 1483f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1484f104e1e5SUlf Hansson 1485f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1486f104e1e5SUlf Hansson 14871d5fcfecSRafael J. Wysocki return gpd_data; 14883e235685SUlf Hansson 1489f104e1e5SUlf Hansson err_free: 1490f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1491f104e1e5SUlf Hansson kfree(gpd_data); 14923e235685SUlf Hansson err_put: 14933e235685SUlf Hansson dev_pm_put_subsys_data(dev); 14943e235685SUlf Hansson return ERR_PTR(ret); 14951d5fcfecSRafael J. Wysocki } 14961d5fcfecSRafael J. Wysocki 149749d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 14981d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 14991d5fcfecSRafael J. Wysocki { 1500f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1501f104e1e5SUlf Hansson 1502f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1503f104e1e5SUlf Hansson 1504f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1505f104e1e5SUlf Hansson 15061d5fcfecSRafael J. Wysocki kfree(gpd_data); 15073e235685SUlf Hansson dev_pm_put_subsys_data(dev); 15081d5fcfecSRafael J. Wysocki } 15091d5fcfecSRafael J. Wysocki 1510b24e1965SUlf Hansson static void genpd_update_cpumask(struct generic_pm_domain *genpd, 1511eb594b73SUlf Hansson int cpu, bool set, unsigned int depth) 1512eb594b73SUlf Hansson { 1513eb594b73SUlf Hansson struct gpd_link *link; 1514eb594b73SUlf Hansson 1515eb594b73SUlf Hansson if (!genpd_is_cpu_domain(genpd)) 1516eb594b73SUlf Hansson return; 1517eb594b73SUlf Hansson 15188d87ae48SKees Cook list_for_each_entry(link, &genpd->child_links, child_node) { 15198d87ae48SKees Cook struct generic_pm_domain *parent = link->parent; 1520eb594b73SUlf Hansson 15218d87ae48SKees Cook genpd_lock_nested(parent, depth + 1); 15228d87ae48SKees Cook genpd_update_cpumask(parent, cpu, set, depth + 1); 15238d87ae48SKees Cook genpd_unlock(parent); 1524eb594b73SUlf Hansson } 1525eb594b73SUlf Hansson 1526eb594b73SUlf Hansson if (set) 1527eb594b73SUlf Hansson cpumask_set_cpu(cpu, genpd->cpus); 1528eb594b73SUlf Hansson else 1529eb594b73SUlf Hansson cpumask_clear_cpu(cpu, genpd->cpus); 1530eb594b73SUlf Hansson } 1531eb594b73SUlf Hansson 1532b24e1965SUlf Hansson static void genpd_set_cpumask(struct generic_pm_domain *genpd, int cpu) 1533b24e1965SUlf Hansson { 1534b24e1965SUlf Hansson if (cpu >= 0) 1535b24e1965SUlf Hansson genpd_update_cpumask(genpd, cpu, true, 0); 1536b24e1965SUlf Hansson } 1537b24e1965SUlf Hansson 1538b24e1965SUlf Hansson static void genpd_clear_cpumask(struct generic_pm_domain *genpd, int cpu) 1539b24e1965SUlf Hansson { 1540b24e1965SUlf Hansson if (cpu >= 0) 1541b24e1965SUlf Hansson genpd_update_cpumask(genpd, cpu, false, 0); 1542b24e1965SUlf Hansson } 1543b24e1965SUlf Hansson 1544b24e1965SUlf Hansson static int genpd_get_cpu(struct generic_pm_domain *genpd, struct device *dev) 1545eb594b73SUlf Hansson { 1546eb594b73SUlf Hansson int cpu; 1547eb594b73SUlf Hansson 1548eb594b73SUlf Hansson if (!genpd_is_cpu_domain(genpd)) 1549b24e1965SUlf Hansson return -1; 1550eb594b73SUlf Hansson 1551eb594b73SUlf Hansson for_each_possible_cpu(cpu) { 1552b24e1965SUlf Hansson if (get_cpu_device(cpu) == dev) 1553b24e1965SUlf Hansson return cpu; 1554eb594b73SUlf Hansson } 1555eb594b73SUlf Hansson 1556b24e1965SUlf Hansson return -1; 1557eb594b73SUlf Hansson } 1558eb594b73SUlf Hansson 1559f9ccd7c3SUlf Hansson static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1560f9ccd7c3SUlf Hansson struct device *base_dev) 1561f721889fSRafael J. Wysocki { 1562c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1563f9ccd7c3SUlf Hansson int ret; 1564f721889fSRafael J. Wysocki 1565f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1566f721889fSRafael J. Wysocki 1567f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1568f721889fSRafael J. Wysocki return -EINVAL; 1569f721889fSRafael J. Wysocki 1570a174920dSUlf Hansson gpd_data = genpd_alloc_dev_data(dev); 15713e235685SUlf Hansson if (IS_ERR(gpd_data)) 15723e235685SUlf Hansson return PTR_ERR(gpd_data); 15736ff7bb0dSRafael J. Wysocki 1574f9ccd7c3SUlf Hansson gpd_data->cpu = genpd_get_cpu(genpd, base_dev); 1575b24e1965SUlf Hansson 1576b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1577b472c2faSUlf Hansson if (ret) 1578b472c2faSUlf Hansson goto out; 1579d79b6fe1SGeert Uytterhoeven 15802071ac98SJiada Wang genpd_lock(genpd); 15812071ac98SJiada Wang 1582f9ccd7c3SUlf Hansson genpd_set_cpumask(genpd, gpd_data->cpu); 1583975e83cfSSudeep Holla dev_pm_domain_set(dev, &genpd->domain); 1584975e83cfSSudeep Holla 158514b53064SUlf Hansson genpd->device_count++; 158614b53064SUlf Hansson genpd->max_off_time_changed = true; 158714b53064SUlf Hansson 15881d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 15896ff7bb0dSRafael J. Wysocki 159035241d12SLina Iyer genpd_unlock(genpd); 15912071ac98SJiada Wang out: 1592c0356db7SUlf Hansson if (ret) 1593c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1594c0356db7SUlf Hansson else 15950b07ee94SViresh Kumar dev_pm_qos_add_notifier(dev, &gpd_data->nb, 15960b07ee94SViresh Kumar DEV_PM_QOS_RESUME_LATENCY); 15971d5fcfecSRafael J. Wysocki 1598f721889fSRafael J. Wysocki return ret; 1599f721889fSRafael J. Wysocki } 160019efa5ffSJon Hunter 160119efa5ffSJon Hunter /** 16021a7a6707SUlf Hansson * pm_genpd_add_device - Add a device to an I/O PM domain. 160319efa5ffSJon Hunter * @genpd: PM domain to add the device to. 160419efa5ffSJon Hunter * @dev: Device to be added. 160519efa5ffSJon Hunter */ 16061a7a6707SUlf Hansson int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 160719efa5ffSJon Hunter { 160819efa5ffSJon Hunter int ret; 160919efa5ffSJon Hunter 161019efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 1611f9ccd7c3SUlf Hansson ret = genpd_add_device(genpd, dev, dev); 161219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 161319efa5ffSJon Hunter 161419efa5ffSJon Hunter return ret; 161519efa5ffSJon Hunter } 16161a7a6707SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_add_device); 1617f721889fSRafael J. Wysocki 161885168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1619f721889fSRafael J. Wysocki struct device *dev) 1620f721889fSRafael J. Wysocki { 16216ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 16224605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1623f9ccd7c3SUlf Hansson int ret = 0; 1624f721889fSRafael J. Wysocki 1625f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1626f721889fSRafael J. Wysocki 1627c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1628c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 16290b07ee94SViresh Kumar dev_pm_qos_remove_notifier(dev, &gpd_data->nb, 16300b07ee94SViresh Kumar DEV_PM_QOS_RESUME_LATENCY); 1631c0356db7SUlf Hansson 163235241d12SLina Iyer genpd_lock(genpd); 1633f721889fSRafael J. Wysocki 1634596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1635596ba34bSRafael J. Wysocki ret = -EAGAIN; 1636596ba34bSRafael J. Wysocki goto out; 1637596ba34bSRafael J. Wysocki } 1638596ba34bSRafael J. Wysocki 16396ff7bb0dSRafael J. Wysocki genpd->device_count--; 16406ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 16416ff7bb0dSRafael J. Wysocki 1642f9ccd7c3SUlf Hansson genpd_clear_cpumask(genpd, gpd_data->cpu); 1643975e83cfSSudeep Holla dev_pm_domain_set(dev, NULL); 1644975e83cfSSudeep Holla 1645efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 16466ff7bb0dSRafael J. Wysocki 164735241d12SLina Iyer genpd_unlock(genpd); 16486ff7bb0dSRafael J. Wysocki 16492071ac98SJiada Wang if (genpd->detach_dev) 16502071ac98SJiada Wang genpd->detach_dev(genpd, dev); 16512071ac98SJiada Wang 165249d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 16531d5fcfecSRafael J. Wysocki 16546ff7bb0dSRafael J. Wysocki return 0; 1655f721889fSRafael J. Wysocki 1656596ba34bSRafael J. Wysocki out: 165735241d12SLina Iyer genpd_unlock(genpd); 16580b07ee94SViresh Kumar dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY); 1659f721889fSRafael J. Wysocki 1660f721889fSRafael J. Wysocki return ret; 1661f721889fSRafael J. Wysocki } 166285168d56SUlf Hansson 166385168d56SUlf Hansson /** 166485168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 166585168d56SUlf Hansson * @dev: Device to be removed. 166685168d56SUlf Hansson */ 1667924f4486SUlf Hansson int pm_genpd_remove_device(struct device *dev) 166885168d56SUlf Hansson { 1669b3ad17c0SUlf Hansson struct generic_pm_domain *genpd = dev_to_genpd_safe(dev); 1670924f4486SUlf Hansson 1671924f4486SUlf Hansson if (!genpd) 167285168d56SUlf Hansson return -EINVAL; 167385168d56SUlf Hansson 167485168d56SUlf Hansson return genpd_remove_device(genpd, dev); 167585168d56SUlf Hansson } 167624c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1677f721889fSRafael J. Wysocki 1678d4f81383SUlf Hansson /** 1679d4f81383SUlf Hansson * dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev 1680d4f81383SUlf Hansson * 1681d4f81383SUlf Hansson * @dev: Device that should be associated with the notifier 1682d4f81383SUlf Hansson * @nb: The notifier block to register 1683d4f81383SUlf Hansson * 1684d4f81383SUlf Hansson * Users may call this function to add a genpd power on/off notifier for an 1685d4f81383SUlf Hansson * attached @dev. Only one notifier per device is allowed. The notifier is 1686d4f81383SUlf Hansson * sent when genpd is powering on/off the PM domain. 1687d4f81383SUlf Hansson * 1688d4f81383SUlf Hansson * It is assumed that the user guarantee that the genpd wouldn't be detached 1689d4f81383SUlf Hansson * while this routine is getting called. 1690d4f81383SUlf Hansson * 1691d4f81383SUlf Hansson * Returns 0 on success and negative error values on failures. 1692d4f81383SUlf Hansson */ 1693d4f81383SUlf Hansson int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb) 1694d4f81383SUlf Hansson { 1695d4f81383SUlf Hansson struct generic_pm_domain *genpd; 1696d4f81383SUlf Hansson struct generic_pm_domain_data *gpd_data; 1697d4f81383SUlf Hansson int ret; 1698d4f81383SUlf Hansson 1699d4f81383SUlf Hansson genpd = dev_to_genpd_safe(dev); 1700d4f81383SUlf Hansson if (!genpd) 1701d4f81383SUlf Hansson return -ENODEV; 1702d4f81383SUlf Hansson 1703d4f81383SUlf Hansson if (WARN_ON(!dev->power.subsys_data || 1704d4f81383SUlf Hansson !dev->power.subsys_data->domain_data)) 1705d4f81383SUlf Hansson return -EINVAL; 1706d4f81383SUlf Hansson 1707d4f81383SUlf Hansson gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 1708d4f81383SUlf Hansson if (gpd_data->power_nb) 1709d4f81383SUlf Hansson return -EEXIST; 1710d4f81383SUlf Hansson 1711d4f81383SUlf Hansson genpd_lock(genpd); 1712d4f81383SUlf Hansson ret = raw_notifier_chain_register(&genpd->power_notifiers, nb); 1713d4f81383SUlf Hansson genpd_unlock(genpd); 1714d4f81383SUlf Hansson 1715d4f81383SUlf Hansson if (ret) { 1716d4f81383SUlf Hansson dev_warn(dev, "failed to add notifier for PM domain %s\n", 1717d4f81383SUlf Hansson genpd->name); 1718d4f81383SUlf Hansson return ret; 1719d4f81383SUlf Hansson } 1720d4f81383SUlf Hansson 1721d4f81383SUlf Hansson gpd_data->power_nb = nb; 1722d4f81383SUlf Hansson return 0; 1723d4f81383SUlf Hansson } 1724d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier); 1725d4f81383SUlf Hansson 1726d4f81383SUlf Hansson /** 1727d4f81383SUlf Hansson * dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev 1728d4f81383SUlf Hansson * 1729d4f81383SUlf Hansson * @dev: Device that is associated with the notifier 1730d4f81383SUlf Hansson * 1731d4f81383SUlf Hansson * Users may call this function to remove a genpd power on/off notifier for an 1732d4f81383SUlf Hansson * attached @dev. 1733d4f81383SUlf Hansson * 1734d4f81383SUlf Hansson * It is assumed that the user guarantee that the genpd wouldn't be detached 1735d4f81383SUlf Hansson * while this routine is getting called. 1736d4f81383SUlf Hansson * 1737d4f81383SUlf Hansson * Returns 0 on success and negative error values on failures. 1738d4f81383SUlf Hansson */ 1739d4f81383SUlf Hansson int dev_pm_genpd_remove_notifier(struct device *dev) 1740d4f81383SUlf Hansson { 1741d4f81383SUlf Hansson struct generic_pm_domain *genpd; 1742d4f81383SUlf Hansson struct generic_pm_domain_data *gpd_data; 1743d4f81383SUlf Hansson int ret; 1744d4f81383SUlf Hansson 1745d4f81383SUlf Hansson genpd = dev_to_genpd_safe(dev); 1746d4f81383SUlf Hansson if (!genpd) 1747d4f81383SUlf Hansson return -ENODEV; 1748d4f81383SUlf Hansson 1749d4f81383SUlf Hansson if (WARN_ON(!dev->power.subsys_data || 1750d4f81383SUlf Hansson !dev->power.subsys_data->domain_data)) 1751d4f81383SUlf Hansson return -EINVAL; 1752d4f81383SUlf Hansson 1753d4f81383SUlf Hansson gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 1754d4f81383SUlf Hansson if (!gpd_data->power_nb) 1755d4f81383SUlf Hansson return -ENODEV; 1756d4f81383SUlf Hansson 1757d4f81383SUlf Hansson genpd_lock(genpd); 1758d4f81383SUlf Hansson ret = raw_notifier_chain_unregister(&genpd->power_notifiers, 1759d4f81383SUlf Hansson gpd_data->power_nb); 1760d4f81383SUlf Hansson genpd_unlock(genpd); 1761d4f81383SUlf Hansson 1762d4f81383SUlf Hansson if (ret) { 1763d4f81383SUlf Hansson dev_warn(dev, "failed to remove notifier for PM domain %s\n", 1764d4f81383SUlf Hansson genpd->name); 1765d4f81383SUlf Hansson return ret; 1766d4f81383SUlf Hansson } 1767d4f81383SUlf Hansson 1768d4f81383SUlf Hansson gpd_data->power_nb = NULL; 1769d4f81383SUlf Hansson return 0; 1770d4f81383SUlf Hansson } 1771d4f81383SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier); 1772d4f81383SUlf Hansson 177319efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1774bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1775f721889fSRafael J. Wysocki { 17762547923dSLina Iyer struct gpd_link *link, *itr; 1777f721889fSRafael J. Wysocki int ret = 0; 1778f721889fSRafael J. Wysocki 1779fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1780fb7268beSRafael J. Wysocki || genpd == subdomain) 1781f721889fSRafael J. Wysocki return -EINVAL; 1782f721889fSRafael J. Wysocki 1783d716f479SLina Iyer /* 1784d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1785d716f479SLina Iyer * context, ensure that the subdomain can also be 1786d716f479SLina Iyer * powered on/off in that context. 1787d716f479SLina Iyer */ 1788d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 178944cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1790d716f479SLina Iyer genpd->name, subdomain->name); 1791d716f479SLina Iyer return -EINVAL; 1792d716f479SLina Iyer } 1793d716f479SLina Iyer 17942547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 17952547923dSLina Iyer if (!link) 17962547923dSLina Iyer return -ENOMEM; 17972547923dSLina Iyer 179835241d12SLina Iyer genpd_lock(subdomain); 179935241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1800f721889fSRafael J. Wysocki 180141e2c8e0SUlf Hansson if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) { 1802f721889fSRafael J. Wysocki ret = -EINVAL; 1803f721889fSRafael J. Wysocki goto out; 1804f721889fSRafael J. Wysocki } 1805f721889fSRafael J. Wysocki 18068d87ae48SKees Cook list_for_each_entry(itr, &genpd->parent_links, parent_node) { 18078d87ae48SKees Cook if (itr->child == subdomain && itr->parent == genpd) { 1808f721889fSRafael J. Wysocki ret = -EINVAL; 1809f721889fSRafael J. Wysocki goto out; 1810f721889fSRafael J. Wysocki } 1811f721889fSRafael J. Wysocki } 1812f721889fSRafael J. Wysocki 18138d87ae48SKees Cook link->parent = genpd; 18148d87ae48SKees Cook list_add_tail(&link->parent_node, &genpd->parent_links); 18158d87ae48SKees Cook link->child = subdomain; 18168d87ae48SKees Cook list_add_tail(&link->child_node, &subdomain->child_links); 181741e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1818c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1819f721889fSRafael J. Wysocki 1820f721889fSRafael J. Wysocki out: 182135241d12SLina Iyer genpd_unlock(genpd); 182235241d12SLina Iyer genpd_unlock(subdomain); 18232547923dSLina Iyer if (ret) 18242547923dSLina Iyer kfree(link); 1825f721889fSRafael J. Wysocki return ret; 1826f721889fSRafael J. Wysocki } 182719efa5ffSJon Hunter 182819efa5ffSJon Hunter /** 182919efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 18308d87ae48SKees Cook * @genpd: Leader PM domain to add the subdomain to. 183119efa5ffSJon Hunter * @subdomain: Subdomain to be added. 183219efa5ffSJon Hunter */ 183319efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 183419efa5ffSJon Hunter struct generic_pm_domain *subdomain) 183519efa5ffSJon Hunter { 183619efa5ffSJon Hunter int ret; 183719efa5ffSJon Hunter 183819efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 183919efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 184019efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 184119efa5ffSJon Hunter 184219efa5ffSJon Hunter return ret; 184319efa5ffSJon Hunter } 1844d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1845f721889fSRafael J. Wysocki 1846f721889fSRafael J. Wysocki /** 1847f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 18488d87ae48SKees Cook * @genpd: Leader PM domain to remove the subdomain from. 18495063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1850f721889fSRafael J. Wysocki */ 1851f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 18525063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1853f721889fSRafael J. Wysocki { 1854c6e83cacSKrzysztof Kozlowski struct gpd_link *l, *link; 1855f721889fSRafael J. Wysocki int ret = -EINVAL; 1856f721889fSRafael J. Wysocki 18575063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1858f721889fSRafael J. Wysocki return -EINVAL; 1859f721889fSRafael J. Wysocki 186035241d12SLina Iyer genpd_lock(subdomain); 186135241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1862f721889fSRafael J. Wysocki 18638d87ae48SKees Cook if (!list_empty(&subdomain->parent_links) || subdomain->device_count) { 18647a5bd127SJoe Perches pr_warn("%s: unable to remove subdomain %s\n", 18657a5bd127SJoe Perches genpd->name, subdomain->name); 186630e7a65bSJon Hunter ret = -EBUSY; 186730e7a65bSJon Hunter goto out; 186830e7a65bSJon Hunter } 186930e7a65bSJon Hunter 18708d87ae48SKees Cook list_for_each_entry_safe(link, l, &genpd->parent_links, parent_node) { 18718d87ae48SKees Cook if (link->child != subdomain) 1872f721889fSRafael J. Wysocki continue; 1873f721889fSRafael J. Wysocki 18748d87ae48SKees Cook list_del(&link->parent_node); 18758d87ae48SKees Cook list_del(&link->child_node); 18765063ce15SRafael J. Wysocki kfree(link); 187741e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1878f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1879f721889fSRafael J. Wysocki 1880f721889fSRafael J. Wysocki ret = 0; 1881f721889fSRafael J. Wysocki break; 1882f721889fSRafael J. Wysocki } 1883f721889fSRafael J. Wysocki 188430e7a65bSJon Hunter out: 188535241d12SLina Iyer genpd_unlock(genpd); 188635241d12SLina Iyer genpd_unlock(subdomain); 1887f721889fSRafael J. Wysocki 1888f721889fSRafael J. Wysocki return ret; 1889f721889fSRafael J. Wysocki } 1890d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1891f721889fSRafael J. Wysocki 189249a27e27SUlf Hansson static void genpd_free_default_power_state(struct genpd_power_state *states, 189349a27e27SUlf Hansson unsigned int state_count) 189449a27e27SUlf Hansson { 189549a27e27SUlf Hansson kfree(states); 189649a27e27SUlf Hansson } 189749a27e27SUlf Hansson 189859d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 189959d65b73SLina Iyer { 190059d65b73SLina Iyer struct genpd_power_state *state; 190159d65b73SLina Iyer 190259d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 190359d65b73SLina Iyer if (!state) 190459d65b73SLina Iyer return -ENOMEM; 190559d65b73SLina Iyer 190659d65b73SLina Iyer genpd->states = state; 190759d65b73SLina Iyer genpd->state_count = 1; 190849a27e27SUlf Hansson genpd->free_states = genpd_free_default_power_state; 190959d65b73SLina Iyer 191059d65b73SLina Iyer return 0; 191159d65b73SLina Iyer } 191259d65b73SLina Iyer 1913d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1914d716f479SLina Iyer { 1915d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1916d716f479SLina Iyer spin_lock_init(&genpd->slock); 1917d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1918d716f479SLina Iyer } else { 1919d716f479SLina Iyer mutex_init(&genpd->mlock); 1920d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1921d716f479SLina Iyer } 1922d716f479SLina Iyer } 1923d716f479SLina Iyer 1924d23b9b00SRafael J. Wysocki /** 1925f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1926f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1927f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1928f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 19297eb231c3SUlf Hansson * 19307eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1931f721889fSRafael J. Wysocki */ 19327eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1933f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1934f721889fSRafael J. Wysocki { 193559d65b73SLina Iyer int ret; 193659d65b73SLina Iyer 1937f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 19387eb231c3SUlf Hansson return -EINVAL; 1939f721889fSRafael J. Wysocki 19408d87ae48SKees Cook INIT_LIST_HEAD(&genpd->parent_links); 19418d87ae48SKees Cook INIT_LIST_HEAD(&genpd->child_links); 1942f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1943d4f81383SUlf Hansson RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers); 1944d716f479SLina Iyer genpd_lock_init(genpd); 1945f721889fSRafael J. Wysocki genpd->gov = gov; 1946f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1947c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 194849f618e1SUlf Hansson genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; 1949596ba34bSRafael J. Wysocki genpd->device_count = 0; 1950221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 19516ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1952de0aa06dSJon Hunter genpd->provider = NULL; 1953de0aa06dSJon Hunter genpd->has_provider = false; 1954afece3abSThara Gopinath genpd->accounting_time = ktime_get(); 1955795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1956795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 19579e9704eaSUlf Hansson genpd->domain.ops.prepare = genpd_prepare; 19589e9704eaSUlf Hansson genpd->domain.ops.suspend_noirq = genpd_suspend_noirq; 19599e9704eaSUlf Hansson genpd->domain.ops.resume_noirq = genpd_resume_noirq; 19609e9704eaSUlf Hansson genpd->domain.ops.freeze_noirq = genpd_freeze_noirq; 19619e9704eaSUlf Hansson genpd->domain.ops.thaw_noirq = genpd_thaw_noirq; 19629e9704eaSUlf Hansson genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq; 19639e9704eaSUlf Hansson genpd->domain.ops.restore_noirq = genpd_restore_noirq; 19649e9704eaSUlf Hansson genpd->domain.ops.complete = genpd_complete; 1965ea71c596SUlf Hansson genpd->domain.start = genpd_dev_pm_start; 1966c11f6f5bSUlf Hansson 1967c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1968c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1969c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1970c11f6f5bSUlf Hansson } 1971c11f6f5bSUlf Hansson 1972ffaa42e8SUlf Hansson /* Always-on domains must be powered on at initialization. */ 1973ed61e18aSLeonard Crestez if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) && 1974ed61e18aSLeonard Crestez !genpd_status_on(genpd)) 1975ffaa42e8SUlf Hansson return -EINVAL; 1976ffaa42e8SUlf Hansson 1977eb594b73SUlf Hansson if (genpd_is_cpu_domain(genpd) && 1978eb594b73SUlf Hansson !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL)) 1979eb594b73SUlf Hansson return -ENOMEM; 1980eb594b73SUlf Hansson 1981fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 198259d65b73SLina Iyer if (genpd->state_count == 0) { 198359d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 1984eb594b73SUlf Hansson if (ret) { 1985eb594b73SUlf Hansson if (genpd_is_cpu_domain(genpd)) 1986eb594b73SUlf Hansson free_cpumask_var(genpd->cpus); 198759d65b73SLina Iyer return ret; 1988eb594b73SUlf Hansson } 198946b7fe94SAisheng Dong } else if (!gov && genpd->state_count > 1) { 19902c9b7f87SUlf Hansson pr_warn("%s: no governor for states\n", genpd->name); 199159d65b73SLina Iyer } 1992fc5cbf0cSAxel Haslam 1993401ea157SViresh Kumar device_initialize(&genpd->dev); 1994401ea157SViresh Kumar dev_set_name(&genpd->dev, "%s", genpd->name); 1995401ea157SViresh Kumar 19965125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 19975125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 1998718072ceSThierry Strudel genpd_debug_add(genpd); 19995125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 20007eb231c3SUlf Hansson 20017eb231c3SUlf Hansson return 0; 20025125bbf3SRafael J. Wysocki } 2003be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 2004aa42240aSTomasz Figa 20053fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 20063fe57710SJon Hunter { 20073fe57710SJon Hunter struct gpd_link *l, *link; 20083fe57710SJon Hunter 20093fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 20103fe57710SJon Hunter return -EINVAL; 20113fe57710SJon Hunter 201235241d12SLina Iyer genpd_lock(genpd); 20133fe57710SJon Hunter 20143fe57710SJon Hunter if (genpd->has_provider) { 201535241d12SLina Iyer genpd_unlock(genpd); 20163fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 20173fe57710SJon Hunter return -EBUSY; 20183fe57710SJon Hunter } 20193fe57710SJon Hunter 20208d87ae48SKees Cook if (!list_empty(&genpd->parent_links) || genpd->device_count) { 202135241d12SLina Iyer genpd_unlock(genpd); 20223fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 20233fe57710SJon Hunter return -EBUSY; 20243fe57710SJon Hunter } 20253fe57710SJon Hunter 20268d87ae48SKees Cook list_for_each_entry_safe(link, l, &genpd->child_links, child_node) { 20278d87ae48SKees Cook list_del(&link->parent_node); 20288d87ae48SKees Cook list_del(&link->child_node); 20293fe57710SJon Hunter kfree(link); 20303fe57710SJon Hunter } 20313fe57710SJon Hunter 2032718072ceSThierry Strudel genpd_debug_remove(genpd); 20333fe57710SJon Hunter list_del(&genpd->gpd_list_node); 203435241d12SLina Iyer genpd_unlock(genpd); 20353fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 2036eb594b73SUlf Hansson if (genpd_is_cpu_domain(genpd)) 2037eb594b73SUlf Hansson free_cpumask_var(genpd->cpus); 203849a27e27SUlf Hansson if (genpd->free_states) 203949a27e27SUlf Hansson genpd->free_states(genpd->states, genpd->state_count); 204049a27e27SUlf Hansson 20413fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 20423fe57710SJon Hunter 20433fe57710SJon Hunter return 0; 20443fe57710SJon Hunter } 20453fe57710SJon Hunter 20463fe57710SJon Hunter /** 20473fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 20483fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 20493fe57710SJon Hunter * 20503fe57710SJon Hunter * To remove the PM domain, this function: 20513fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 20523fe57710SJon Hunter * if it was added. 20533fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 20543fe57710SJon Hunter * 20553fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 20563fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 20573fe57710SJon Hunter * devices associated with it. 20583fe57710SJon Hunter */ 20593fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 20603fe57710SJon Hunter { 20613fe57710SJon Hunter int ret; 20623fe57710SJon Hunter 20633fe57710SJon Hunter mutex_lock(&gpd_list_lock); 20643fe57710SJon Hunter ret = genpd_remove(genpd); 20653fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 20663fe57710SJon Hunter 20673fe57710SJon Hunter return ret; 20683fe57710SJon Hunter } 20693fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 20703fe57710SJon Hunter 2071aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 2072892ebdccSJon Hunter 2073aa42240aSTomasz Figa /* 2074aa42240aSTomasz Figa * Device Tree based PM domain providers. 2075aa42240aSTomasz Figa * 2076aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 2077aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 2078aa42240aSTomasz Figa * 2079aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 2080aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 2081aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 2082aa42240aSTomasz Figa * 2083aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 2084892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 2085892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 2086aa42240aSTomasz Figa * index. 2087aa42240aSTomasz Figa */ 2088aa42240aSTomasz Figa 2089aa42240aSTomasz Figa /** 2090aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 2091aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 2092aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 2093aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 2094aa42240aSTomasz Figa * into a PM domain. 2095aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 2096aa42240aSTomasz Figa */ 2097aa42240aSTomasz Figa struct of_genpd_provider { 2098aa42240aSTomasz Figa struct list_head link; 2099aa42240aSTomasz Figa struct device_node *node; 2100aa42240aSTomasz Figa genpd_xlate_t xlate; 2101aa42240aSTomasz Figa void *data; 2102aa42240aSTomasz Figa }; 2103aa42240aSTomasz Figa 2104aa42240aSTomasz Figa /* List of registered PM domain providers. */ 2105aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 2106aa42240aSTomasz Figa /* Mutex to protect the list above. */ 2107aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 2108aa42240aSTomasz Figa 2109aa42240aSTomasz Figa /** 2110892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 2111aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 2112aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 2113aa42240aSTomasz Figa * 2114aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 2115aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 2116aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 2117aa42240aSTomasz Figa */ 2118892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 2119aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 2120aa42240aSTomasz Figa void *data) 2121aa42240aSTomasz Figa { 2122aa42240aSTomasz Figa return data; 2123aa42240aSTomasz Figa } 2124aa42240aSTomasz Figa 2125aa42240aSTomasz Figa /** 2126892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 2127aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 2128aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 2129aa42240aSTomasz Figa * 2130aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 2131aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 2132aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 2133aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 2134aa42240aSTomasz Figa */ 2135892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 2136aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 2137aa42240aSTomasz Figa void *data) 2138aa42240aSTomasz Figa { 2139aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 2140aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 2141aa42240aSTomasz Figa 2142aa42240aSTomasz Figa if (genpdspec->args_count != 1) 2143aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 2144aa42240aSTomasz Figa 2145aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 2146aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 2147aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 2148aa42240aSTomasz Figa } 2149aa42240aSTomasz Figa 2150aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 2151aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 2152aa42240aSTomasz Figa 2153aa42240aSTomasz Figa return genpd_data->domains[idx]; 2154aa42240aSTomasz Figa } 2155aa42240aSTomasz Figa 2156aa42240aSTomasz Figa /** 2157892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 2158aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 2159aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 2160aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 2161aa42240aSTomasz Figa */ 2162892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 2163aa42240aSTomasz Figa void *data) 2164aa42240aSTomasz Figa { 2165aa42240aSTomasz Figa struct of_genpd_provider *cp; 2166aa42240aSTomasz Figa 2167aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 2168aa42240aSTomasz Figa if (!cp) 2169aa42240aSTomasz Figa return -ENOMEM; 2170aa42240aSTomasz Figa 2171aa42240aSTomasz Figa cp->node = of_node_get(np); 2172aa42240aSTomasz Figa cp->data = data; 2173aa42240aSTomasz Figa cp->xlate = xlate; 2174bab2d712SSaravana Kannan fwnode_dev_initialized(&np->fwnode, true); 2175aa42240aSTomasz Figa 2176aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 2177aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 2178aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 2179ea11e94bSRob Herring pr_debug("Added domain provider from %pOF\n", np); 2180aa42240aSTomasz Figa 2181aa42240aSTomasz Figa return 0; 2182aa42240aSTomasz Figa } 2183892ebdccSJon Hunter 2184fe0c2baaSUlf Hansson static bool genpd_present(const struct generic_pm_domain *genpd) 2185fe0c2baaSUlf Hansson { 2186fe0c2baaSUlf Hansson const struct generic_pm_domain *gpd; 2187fe0c2baaSUlf Hansson 2188fe0c2baaSUlf Hansson list_for_each_entry(gpd, &gpd_list, gpd_list_node) 2189fe0c2baaSUlf Hansson if (gpd == genpd) 2190fe0c2baaSUlf Hansson return true; 2191fe0c2baaSUlf Hansson return false; 2192fe0c2baaSUlf Hansson } 2193fe0c2baaSUlf Hansson 2194892ebdccSJon Hunter /** 2195892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 2196892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 2197892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 2198892ebdccSJon Hunter */ 2199892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 2200892ebdccSJon Hunter struct generic_pm_domain *genpd) 2201892ebdccSJon Hunter { 22020159ec67SJon Hunter int ret = -EINVAL; 22030159ec67SJon Hunter 22040159ec67SJon Hunter if (!np || !genpd) 22050159ec67SJon Hunter return -EINVAL; 22060159ec67SJon Hunter 22070159ec67SJon Hunter mutex_lock(&gpd_list_lock); 22080159ec67SJon Hunter 22096a0ae73dSViresh Kumar if (!genpd_present(genpd)) 22106a0ae73dSViresh Kumar goto unlock; 22116a0ae73dSViresh Kumar 22126a0ae73dSViresh Kumar genpd->dev.of_node = np; 22136a0ae73dSViresh Kumar 22146a0ae73dSViresh Kumar /* Parse genpd OPP table */ 22156a0ae73dSViresh Kumar if (genpd->set_performance_state) { 22166a0ae73dSViresh Kumar ret = dev_pm_opp_of_add_table(&genpd->dev); 22176a0ae73dSViresh Kumar if (ret) { 2218dd461cd9SStephan Gerhold if (ret != -EPROBE_DEFER) 22196a0ae73dSViresh Kumar dev_err(&genpd->dev, "Failed to add OPP table: %d\n", 22206a0ae73dSViresh Kumar ret); 22216a0ae73dSViresh Kumar goto unlock; 2222de0aa06dSJon Hunter } 22231067ae3eSViresh Kumar 22241067ae3eSViresh Kumar /* 22251067ae3eSViresh Kumar * Save table for faster processing while setting performance 22261067ae3eSViresh Kumar * state. 22271067ae3eSViresh Kumar */ 22281067ae3eSViresh Kumar genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev); 2229dd461cd9SStephan Gerhold WARN_ON(IS_ERR(genpd->opp_table)); 22308ce95844SViresh Kumar } 2231de0aa06dSJon Hunter 22326a0ae73dSViresh Kumar ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 22336a0ae73dSViresh Kumar if (ret) { 22341067ae3eSViresh Kumar if (genpd->set_performance_state) { 22351067ae3eSViresh Kumar dev_pm_opp_put_opp_table(genpd->opp_table); 22366a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&genpd->dev); 22371067ae3eSViresh Kumar } 22386a0ae73dSViresh Kumar 22396a0ae73dSViresh Kumar goto unlock; 22406a0ae73dSViresh Kumar } 22416a0ae73dSViresh Kumar 22426a0ae73dSViresh Kumar genpd->provider = &np->fwnode; 22436a0ae73dSViresh Kumar genpd->has_provider = true; 22446a0ae73dSViresh Kumar 22456a0ae73dSViresh Kumar unlock: 22460159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 22470159ec67SJon Hunter 22480159ec67SJon Hunter return ret; 2249892ebdccSJon Hunter } 2250892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 2251892ebdccSJon Hunter 2252892ebdccSJon Hunter /** 2253892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 2254892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 2255892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 2256892ebdccSJon Hunter */ 2257892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 2258892ebdccSJon Hunter struct genpd_onecell_data *data) 2259892ebdccSJon Hunter { 22606a0ae73dSViresh Kumar struct generic_pm_domain *genpd; 22610159ec67SJon Hunter unsigned int i; 2262de0aa06dSJon Hunter int ret = -EINVAL; 22630159ec67SJon Hunter 22640159ec67SJon Hunter if (!np || !data) 22650159ec67SJon Hunter return -EINVAL; 22660159ec67SJon Hunter 22670159ec67SJon Hunter mutex_lock(&gpd_list_lock); 22680159ec67SJon Hunter 226940845524SThierry Reding if (!data->xlate) 227040845524SThierry Reding data->xlate = genpd_xlate_onecell; 227140845524SThierry Reding 22720159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 22736a0ae73dSViresh Kumar genpd = data->domains[i]; 22746a0ae73dSViresh Kumar 22756a0ae73dSViresh Kumar if (!genpd) 2276609bed67STomeu Vizoso continue; 22776a0ae73dSViresh Kumar if (!genpd_present(genpd)) 2278de0aa06dSJon Hunter goto error; 2279de0aa06dSJon Hunter 22806a0ae73dSViresh Kumar genpd->dev.of_node = np; 22816a0ae73dSViresh Kumar 22826a0ae73dSViresh Kumar /* Parse genpd OPP table */ 22836a0ae73dSViresh Kumar if (genpd->set_performance_state) { 22846a0ae73dSViresh Kumar ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); 22856a0ae73dSViresh Kumar if (ret) { 2286dd461cd9SStephan Gerhold if (ret != -EPROBE_DEFER) 22876a0ae73dSViresh Kumar dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n", 22886a0ae73dSViresh Kumar i, ret); 22896a0ae73dSViresh Kumar goto error; 22906a0ae73dSViresh Kumar } 22911067ae3eSViresh Kumar 22921067ae3eSViresh Kumar /* 22931067ae3eSViresh Kumar * Save table for faster processing while setting 22941067ae3eSViresh Kumar * performance state. 22951067ae3eSViresh Kumar */ 2296e77dcb0bSViresh Kumar genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev); 2297dd461cd9SStephan Gerhold WARN_ON(IS_ERR(genpd->opp_table)); 22986a0ae73dSViresh Kumar } 22996a0ae73dSViresh Kumar 23006a0ae73dSViresh Kumar genpd->provider = &np->fwnode; 23016a0ae73dSViresh Kumar genpd->has_provider = true; 23020159ec67SJon Hunter } 23030159ec67SJon Hunter 230440845524SThierry Reding ret = genpd_add_provider(np, data->xlate, data); 2305de0aa06dSJon Hunter if (ret < 0) 2306de0aa06dSJon Hunter goto error; 2307de0aa06dSJon Hunter 2308de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 2309de0aa06dSJon Hunter 2310de0aa06dSJon Hunter return 0; 2311de0aa06dSJon Hunter 2312de0aa06dSJon Hunter error: 2313de0aa06dSJon Hunter while (i--) { 23146a0ae73dSViresh Kumar genpd = data->domains[i]; 23156a0ae73dSViresh Kumar 23166a0ae73dSViresh Kumar if (!genpd) 2317609bed67STomeu Vizoso continue; 23186a0ae73dSViresh Kumar 23196a0ae73dSViresh Kumar genpd->provider = NULL; 23206a0ae73dSViresh Kumar genpd->has_provider = false; 23216a0ae73dSViresh Kumar 23221067ae3eSViresh Kumar if (genpd->set_performance_state) { 23231067ae3eSViresh Kumar dev_pm_opp_put_opp_table(genpd->opp_table); 23246a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&genpd->dev); 2325de0aa06dSJon Hunter } 23261067ae3eSViresh Kumar } 23270159ec67SJon Hunter 23280159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 23290159ec67SJon Hunter 23300159ec67SJon Hunter return ret; 2331892ebdccSJon Hunter } 2332892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 2333aa42240aSTomasz Figa 2334aa42240aSTomasz Figa /** 2335aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 2336aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 2337aa42240aSTomasz Figa */ 2338aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 2339aa42240aSTomasz Figa { 2340b556b15dSKrzysztof Kozlowski struct of_genpd_provider *cp, *tmp; 2341de0aa06dSJon Hunter struct generic_pm_domain *gpd; 2342aa42240aSTomasz Figa 2343de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 2344aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 2345b556b15dSKrzysztof Kozlowski list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { 2346aa42240aSTomasz Figa if (cp->node == np) { 2347de0aa06dSJon Hunter /* 2348de0aa06dSJon Hunter * For each PM domain associated with the 2349de0aa06dSJon Hunter * provider, set the 'has_provider' to false 2350de0aa06dSJon Hunter * so that the PM domain can be safely removed. 2351de0aa06dSJon Hunter */ 23526a0ae73dSViresh Kumar list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 23536a0ae73dSViresh Kumar if (gpd->provider == &np->fwnode) { 2354de0aa06dSJon Hunter gpd->has_provider = false; 2355de0aa06dSJon Hunter 23566a0ae73dSViresh Kumar if (!gpd->set_performance_state) 23576a0ae73dSViresh Kumar continue; 23586a0ae73dSViresh Kumar 23591067ae3eSViresh Kumar dev_pm_opp_put_opp_table(gpd->opp_table); 23606a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&gpd->dev); 23616a0ae73dSViresh Kumar } 23626a0ae73dSViresh Kumar } 23636a0ae73dSViresh Kumar 2364bab2d712SSaravana Kannan fwnode_dev_initialized(&cp->node->fwnode, false); 2365aa42240aSTomasz Figa list_del(&cp->link); 2366aa42240aSTomasz Figa of_node_put(cp->node); 2367aa42240aSTomasz Figa kfree(cp); 2368aa42240aSTomasz Figa break; 2369aa42240aSTomasz Figa } 2370aa42240aSTomasz Figa } 2371aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 2372de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 2373aa42240aSTomasz Figa } 2374aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 2375aa42240aSTomasz Figa 2376aa42240aSTomasz Figa /** 2377f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 2378aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 2379aa42240aSTomasz Figa * 2380aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 2381aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 2382aa42240aSTomasz Figa * domain. 2383aa42240aSTomasz Figa * 2384aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 2385aa42240aSTomasz Figa * on failure. 2386aa42240aSTomasz Figa */ 2387f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 2388aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 2389aa42240aSTomasz Figa { 2390aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 2391aa42240aSTomasz Figa struct of_genpd_provider *provider; 2392aa42240aSTomasz Figa 239341795a8aSJon Hunter if (!genpdspec) 239441795a8aSJon Hunter return ERR_PTR(-EINVAL); 239541795a8aSJon Hunter 2396aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 2397aa42240aSTomasz Figa 2398aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 2399aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 2400aa42240aSTomasz Figa if (provider->node == genpdspec->np) 2401aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 2402aa42240aSTomasz Figa if (!IS_ERR(genpd)) 2403aa42240aSTomasz Figa break; 2404aa42240aSTomasz Figa } 2405aa42240aSTomasz Figa 2406aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 2407aa42240aSTomasz Figa 2408aa42240aSTomasz Figa return genpd; 2409aa42240aSTomasz Figa } 2410aa42240aSTomasz Figa 2411aa42240aSTomasz Figa /** 2412ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 2413ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 2414ec69572bSJon Hunter * @dev: Device to be added. 2415ec69572bSJon Hunter * 2416ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 2417ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 2418ec69572bSJon Hunter */ 2419ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 2420ec69572bSJon Hunter { 2421ec69572bSJon Hunter struct generic_pm_domain *genpd; 242219efa5ffSJon Hunter int ret; 242319efa5ffSJon Hunter 242419efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2425ec69572bSJon Hunter 2426f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 242719efa5ffSJon Hunter if (IS_ERR(genpd)) { 242819efa5ffSJon Hunter ret = PTR_ERR(genpd); 242919efa5ffSJon Hunter goto out; 243019efa5ffSJon Hunter } 2431ec69572bSJon Hunter 2432f9ccd7c3SUlf Hansson ret = genpd_add_device(genpd, dev, dev); 243319efa5ffSJon Hunter 243419efa5ffSJon Hunter out: 243519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 243619efa5ffSJon Hunter 243719efa5ffSJon Hunter return ret; 2438ec69572bSJon Hunter } 2439ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 2440ec69572bSJon Hunter 2441ec69572bSJon Hunter /** 2442ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 2443ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 2444ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 2445ec69572bSJon Hunter * 2446ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 2447ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 2448ec69572bSJon Hunter * negative error code on failure. 2449ec69572bSJon Hunter */ 2450ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 2451ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 2452ec69572bSJon Hunter { 2453ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 245419efa5ffSJon Hunter int ret; 245519efa5ffSJon Hunter 245619efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2457ec69572bSJon Hunter 2458f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 245919efa5ffSJon Hunter if (IS_ERR(parent)) { 246019efa5ffSJon Hunter ret = PTR_ERR(parent); 246119efa5ffSJon Hunter goto out; 246219efa5ffSJon Hunter } 2463ec69572bSJon Hunter 2464f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 246519efa5ffSJon Hunter if (IS_ERR(subdomain)) { 246619efa5ffSJon Hunter ret = PTR_ERR(subdomain); 246719efa5ffSJon Hunter goto out; 246819efa5ffSJon Hunter } 2469ec69572bSJon Hunter 247019efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 247119efa5ffSJon Hunter 247219efa5ffSJon Hunter out: 247319efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 247419efa5ffSJon Hunter 247518027d6fSDmitry Osipenko return ret == -ENOENT ? -EPROBE_DEFER : ret; 2476ec69572bSJon Hunter } 2477ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 2478ec69572bSJon Hunter 2479ec69572bSJon Hunter /** 2480dedd1492SUlf Hansson * of_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 2481dedd1492SUlf Hansson * @parent_spec: OF phandle args to use for parent PM domain look-up 2482dedd1492SUlf Hansson * @subdomain_spec: OF phandle args to use for subdomain look-up 2483dedd1492SUlf Hansson * 2484dedd1492SUlf Hansson * Looks-up a parent PM domain and subdomain based upon phandle args 2485dedd1492SUlf Hansson * provided and removes the subdomain from the parent PM domain. Returns a 2486dedd1492SUlf Hansson * negative error code on failure. 2487dedd1492SUlf Hansson */ 2488dedd1492SUlf Hansson int of_genpd_remove_subdomain(struct of_phandle_args *parent_spec, 2489dedd1492SUlf Hansson struct of_phandle_args *subdomain_spec) 2490dedd1492SUlf Hansson { 2491dedd1492SUlf Hansson struct generic_pm_domain *parent, *subdomain; 2492dedd1492SUlf Hansson int ret; 2493dedd1492SUlf Hansson 2494dedd1492SUlf Hansson mutex_lock(&gpd_list_lock); 2495dedd1492SUlf Hansson 2496dedd1492SUlf Hansson parent = genpd_get_from_provider(parent_spec); 2497dedd1492SUlf Hansson if (IS_ERR(parent)) { 2498dedd1492SUlf Hansson ret = PTR_ERR(parent); 2499dedd1492SUlf Hansson goto out; 2500dedd1492SUlf Hansson } 2501dedd1492SUlf Hansson 2502dedd1492SUlf Hansson subdomain = genpd_get_from_provider(subdomain_spec); 2503dedd1492SUlf Hansson if (IS_ERR(subdomain)) { 2504dedd1492SUlf Hansson ret = PTR_ERR(subdomain); 2505dedd1492SUlf Hansson goto out; 2506dedd1492SUlf Hansson } 2507dedd1492SUlf Hansson 2508dedd1492SUlf Hansson ret = pm_genpd_remove_subdomain(parent, subdomain); 2509dedd1492SUlf Hansson 2510dedd1492SUlf Hansson out: 2511dedd1492SUlf Hansson mutex_unlock(&gpd_list_lock); 2512dedd1492SUlf Hansson 2513dedd1492SUlf Hansson return ret; 2514dedd1492SUlf Hansson } 2515dedd1492SUlf Hansson EXPORT_SYMBOL_GPL(of_genpd_remove_subdomain); 2516dedd1492SUlf Hansson 2517dedd1492SUlf Hansson /** 251817926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 2519763663c9SYang Yingliang * @np: Pointer to device node associated with provider 252017926551SJon Hunter * 252117926551SJon Hunter * Find the last PM domain that was added by a particular provider and 252217926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 252317926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 252417926551SJon Hunter * domain will only be removed, if the provider associated with domain 252517926551SJon Hunter * has been removed. 252617926551SJon Hunter * 252717926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 252817926551SJon Hunter * ERR_PTR() on failure. 252917926551SJon Hunter */ 253017926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 253117926551SJon Hunter { 2532a7e2d1bcSKrzysztof Kozlowski struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT); 253317926551SJon Hunter int ret; 253417926551SJon Hunter 253517926551SJon Hunter if (IS_ERR_OR_NULL(np)) 253617926551SJon Hunter return ERR_PTR(-EINVAL); 253717926551SJon Hunter 253817926551SJon Hunter mutex_lock(&gpd_list_lock); 2539a7e2d1bcSKrzysztof Kozlowski list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { 254017926551SJon Hunter if (gpd->provider == &np->fwnode) { 254117926551SJon Hunter ret = genpd_remove(gpd); 254217926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 254317926551SJon Hunter break; 254417926551SJon Hunter } 254517926551SJon Hunter } 254617926551SJon Hunter mutex_unlock(&gpd_list_lock); 254717926551SJon Hunter 254817926551SJon Hunter return genpd; 254917926551SJon Hunter } 255017926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 255117926551SJon Hunter 25523c095f32SUlf Hansson static void genpd_release_dev(struct device *dev) 25533c095f32SUlf Hansson { 2554e8b04de9SUlf Hansson of_node_put(dev->of_node); 25553c095f32SUlf Hansson kfree(dev); 25563c095f32SUlf Hansson } 25573c095f32SUlf Hansson 25583c095f32SUlf Hansson static struct bus_type genpd_bus_type = { 25593c095f32SUlf Hansson .name = "genpd", 25603c095f32SUlf Hansson }; 25613c095f32SUlf Hansson 256217926551SJon Hunter /** 2563aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 25648bb6944eSJon Hunter * @dev: Device to detach. 2565aa42240aSTomasz Figa * @power_off: Currently not used 2566aa42240aSTomasz Figa * 2567aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 2568aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 2569aa42240aSTomasz Figa */ 2570aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 2571aa42240aSTomasz Figa { 2572446d999cSRussell King struct generic_pm_domain *pd; 257393af5e93SGeert Uytterhoeven unsigned int i; 2574aa42240aSTomasz Figa int ret = 0; 2575aa42240aSTomasz Figa 257685168d56SUlf Hansson pd = dev_to_genpd(dev); 257785168d56SUlf Hansson if (IS_ERR(pd)) 2578aa42240aSTomasz Figa return; 2579aa42240aSTomasz Figa 2580aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 2581aa42240aSTomasz Figa 258293af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 258385168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 2584aa42240aSTomasz Figa if (ret != -EAGAIN) 2585aa42240aSTomasz Figa break; 258693af5e93SGeert Uytterhoeven 258793af5e93SGeert Uytterhoeven mdelay(i); 2588aa42240aSTomasz Figa cond_resched(); 2589aa42240aSTomasz Figa } 2590aa42240aSTomasz Figa 2591aa42240aSTomasz Figa if (ret < 0) { 2592aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 2593aa42240aSTomasz Figa pd->name, ret); 2594aa42240aSTomasz Figa return; 2595aa42240aSTomasz Figa } 2596aa42240aSTomasz Figa 2597aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 2598aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 25993c095f32SUlf Hansson 26003c095f32SUlf Hansson /* Unregister the device if it was created by genpd. */ 26013c095f32SUlf Hansson if (dev->bus == &genpd_bus_type) 26023c095f32SUlf Hansson device_unregister(dev); 2603aa42240aSTomasz Figa } 2604aa42240aSTomasz Figa 2605632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 2606632f7ce3SRussell King { 2607632f7ce3SRussell King struct generic_pm_domain *pd; 2608632f7ce3SRussell King 2609632f7ce3SRussell King pd = dev_to_genpd(dev); 2610632f7ce3SRussell King if (IS_ERR(pd)) 2611632f7ce3SRussell King return; 2612632f7ce3SRussell King 2613632f7ce3SRussell King genpd_queue_power_off_work(pd); 2614632f7ce3SRussell King } 2615632f7ce3SRussell King 261651dcf748SUlf Hansson static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, 261751dcf748SUlf Hansson unsigned int index, bool power_on) 2618aa42240aSTomasz Figa { 2619aa42240aSTomasz Figa struct of_phandle_args pd_args; 2620aa42240aSTomasz Figa struct generic_pm_domain *pd; 2621aa42240aSTomasz Figa int ret; 2622aa42240aSTomasz Figa 2623e8b04de9SUlf Hansson ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 26248cb1cbd6SUlf Hansson "#power-domain-cells", index, &pd_args); 2625001d50c9SGeert Uytterhoeven if (ret < 0) 2626bcd931f2SUlf Hansson return ret; 2627aa42240aSTomasz Figa 262819efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2629f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2630265e2cf6SEric Anholt of_node_put(pd_args.np); 2631aa42240aSTomasz Figa if (IS_ERR(pd)) { 263219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2633aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2634aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 263551dcf748SUlf Hansson return driver_deferred_probe_check_state(base_dev); 2636aa42240aSTomasz Figa } 2637aa42240aSTomasz Figa 2638aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2639aa42240aSTomasz Figa 2640f9ccd7c3SUlf Hansson ret = genpd_add_device(pd, dev, base_dev); 264119efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2642aa42240aSTomasz Figa 2643aa42240aSTomasz Figa if (ret < 0) { 264434994692SGeert Uytterhoeven if (ret != -EPROBE_DEFER) 2645aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2646aa42240aSTomasz Figa pd->name, ret); 2647919b7308SUlf Hansson return ret; 2648aa42240aSTomasz Figa } 2649aa42240aSTomasz Figa 2650aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2651632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2652aa42240aSTomasz Figa 2653895b6612SUlf Hansson if (power_on) { 265435241d12SLina Iyer genpd_lock(pd); 265586e12eacSUlf Hansson ret = genpd_power_on(pd, 0); 265635241d12SLina Iyer genpd_unlock(pd); 2657895b6612SUlf Hansson } 265872038df3SUlf Hansson 265972038df3SUlf Hansson if (ret) 266072038df3SUlf Hansson genpd_remove_device(pd, dev); 2661919b7308SUlf Hansson 2662919b7308SUlf Hansson return ret ? -EPROBE_DEFER : 1; 2663aa42240aSTomasz Figa } 26648cb1cbd6SUlf Hansson 26658cb1cbd6SUlf Hansson /** 26668cb1cbd6SUlf Hansson * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 26678cb1cbd6SUlf Hansson * @dev: Device to attach. 26688cb1cbd6SUlf Hansson * 26698cb1cbd6SUlf Hansson * Parse device's OF node to find a PM domain specifier. If such is found, 26708cb1cbd6SUlf Hansson * attaches the device to retrieved pm_domain ops. 26718cb1cbd6SUlf Hansson * 26728cb1cbd6SUlf Hansson * Returns 1 on successfully attached PM domain, 0 when the device don't need a 26738cb1cbd6SUlf Hansson * PM domain or when multiple power-domains exists for it, else a negative error 26748cb1cbd6SUlf Hansson * code. Note that if a power-domain exists for the device, but it cannot be 26758cb1cbd6SUlf Hansson * found or turned on, then return -EPROBE_DEFER to ensure that the device is 26768cb1cbd6SUlf Hansson * not probed and to re-try again later. 26778cb1cbd6SUlf Hansson */ 26788cb1cbd6SUlf Hansson int genpd_dev_pm_attach(struct device *dev) 26798cb1cbd6SUlf Hansson { 26808cb1cbd6SUlf Hansson if (!dev->of_node) 26818cb1cbd6SUlf Hansson return 0; 26828cb1cbd6SUlf Hansson 26838cb1cbd6SUlf Hansson /* 26848cb1cbd6SUlf Hansson * Devices with multiple PM domains must be attached separately, as we 26858cb1cbd6SUlf Hansson * can only attach one PM domain per device. 26868cb1cbd6SUlf Hansson */ 26878cb1cbd6SUlf Hansson if (of_count_phandle_with_args(dev->of_node, "power-domains", 26888cb1cbd6SUlf Hansson "#power-domain-cells") != 1) 26898cb1cbd6SUlf Hansson return 0; 26908cb1cbd6SUlf Hansson 269151dcf748SUlf Hansson return __genpd_dev_pm_attach(dev, dev, 0, true); 26928cb1cbd6SUlf Hansson } 2693aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 269430f60428SLina Iyer 26953c095f32SUlf Hansson /** 26963c095f32SUlf Hansson * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains. 26973c095f32SUlf Hansson * @dev: The device used to lookup the PM domain. 26983c095f32SUlf Hansson * @index: The index of the PM domain. 26993c095f32SUlf Hansson * 27003c095f32SUlf Hansson * Parse device's OF node to find a PM domain specifier at the provided @index. 27013c095f32SUlf Hansson * If such is found, creates a virtual device and attaches it to the retrieved 27023c095f32SUlf Hansson * pm_domain ops. To deal with detaching of the virtual device, the ->detach() 27033c095f32SUlf Hansson * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach(). 27043c095f32SUlf Hansson * 27053c095f32SUlf Hansson * Returns the created virtual device if successfully attached PM domain, NULL 27063c095f32SUlf Hansson * when the device don't need a PM domain, else an ERR_PTR() in case of 27073c095f32SUlf Hansson * failures. If a power-domain exists for the device, but cannot be found or 27083c095f32SUlf Hansson * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device 27093c095f32SUlf Hansson * is not probed and to re-try again later. 27103c095f32SUlf Hansson */ 27113c095f32SUlf Hansson struct device *genpd_dev_pm_attach_by_id(struct device *dev, 27123c095f32SUlf Hansson unsigned int index) 27133c095f32SUlf Hansson { 2714560928b2SViresh Kumar struct device *virt_dev; 27153c095f32SUlf Hansson int num_domains; 27163c095f32SUlf Hansson int ret; 27173c095f32SUlf Hansson 27183c095f32SUlf Hansson if (!dev->of_node) 27193c095f32SUlf Hansson return NULL; 27203c095f32SUlf Hansson 27213ccf3f0cSUlf Hansson /* Verify that the index is within a valid range. */ 27223c095f32SUlf Hansson num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", 27233c095f32SUlf Hansson "#power-domain-cells"); 27243ccf3f0cSUlf Hansson if (index >= num_domains) 27253c095f32SUlf Hansson return NULL; 27263c095f32SUlf Hansson 27273c095f32SUlf Hansson /* Allocate and register device on the genpd bus. */ 2728560928b2SViresh Kumar virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); 2729560928b2SViresh Kumar if (!virt_dev) 27303c095f32SUlf Hansson return ERR_PTR(-ENOMEM); 27313c095f32SUlf Hansson 2732560928b2SViresh Kumar dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev)); 2733560928b2SViresh Kumar virt_dev->bus = &genpd_bus_type; 2734560928b2SViresh Kumar virt_dev->release = genpd_release_dev; 2735e8b04de9SUlf Hansson virt_dev->of_node = of_node_get(dev->of_node); 27363c095f32SUlf Hansson 2737560928b2SViresh Kumar ret = device_register(virt_dev); 27383c095f32SUlf Hansson if (ret) { 273971b77697SUlf Hansson put_device(virt_dev); 27403c095f32SUlf Hansson return ERR_PTR(ret); 27413c095f32SUlf Hansson } 27423c095f32SUlf Hansson 27433c095f32SUlf Hansson /* Try to attach the device to the PM domain at the specified index. */ 274451dcf748SUlf Hansson ret = __genpd_dev_pm_attach(virt_dev, dev, index, false); 27453c095f32SUlf Hansson if (ret < 1) { 2746560928b2SViresh Kumar device_unregister(virt_dev); 27473c095f32SUlf Hansson return ret ? ERR_PTR(ret) : NULL; 27483c095f32SUlf Hansson } 27493c095f32SUlf Hansson 2750560928b2SViresh Kumar pm_runtime_enable(virt_dev); 2751560928b2SViresh Kumar genpd_queue_power_off_work(dev_to_genpd(virt_dev)); 27523c095f32SUlf Hansson 2753560928b2SViresh Kumar return virt_dev; 27543c095f32SUlf Hansson } 27553c095f32SUlf Hansson EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id); 27563c095f32SUlf Hansson 27575d6be70aSUlf Hansson /** 27585d6be70aSUlf Hansson * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains. 27595d6be70aSUlf Hansson * @dev: The device used to lookup the PM domain. 27605d6be70aSUlf Hansson * @name: The name of the PM domain. 27615d6be70aSUlf Hansson * 27625d6be70aSUlf Hansson * Parse device's OF node to find a PM domain specifier using the 27635d6be70aSUlf Hansson * power-domain-names DT property. For further description see 27645d6be70aSUlf Hansson * genpd_dev_pm_attach_by_id(). 27655d6be70aSUlf Hansson */ 27667416f1f2SDouglas Anderson struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name) 27675d6be70aSUlf Hansson { 27685d6be70aSUlf Hansson int index; 27695d6be70aSUlf Hansson 27705d6be70aSUlf Hansson if (!dev->of_node) 27715d6be70aSUlf Hansson return NULL; 27725d6be70aSUlf Hansson 27735d6be70aSUlf Hansson index = of_property_match_string(dev->of_node, "power-domain-names", 27745d6be70aSUlf Hansson name); 27755d6be70aSUlf Hansson if (index < 0) 27765d6be70aSUlf Hansson return NULL; 27775d6be70aSUlf Hansson 27785d6be70aSUlf Hansson return genpd_dev_pm_attach_by_id(dev, index); 27795d6be70aSUlf Hansson } 27805d6be70aSUlf Hansson 278130f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 2782598da548SLina Iyer { .compatible = "domain-idle-state", }, 278330f60428SLina Iyer { } 278430f60428SLina Iyer }; 278530f60428SLina Iyer 278630f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 278730f60428SLina Iyer struct device_node *state_node) 278830f60428SLina Iyer { 278930f60428SLina Iyer int err; 279030f60428SLina Iyer u32 residency; 279130f60428SLina Iyer u32 entry_latency, exit_latency; 279230f60428SLina Iyer 279330f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 279430f60428SLina Iyer &entry_latency); 279530f60428SLina Iyer if (err) { 2796ea11e94bSRob Herring pr_debug(" * %pOF missing entry-latency-us property\n", 2797ea11e94bSRob Herring state_node); 279830f60428SLina Iyer return -EINVAL; 279930f60428SLina Iyer } 280030f60428SLina Iyer 280130f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 280230f60428SLina Iyer &exit_latency); 280330f60428SLina Iyer if (err) { 2804ea11e94bSRob Herring pr_debug(" * %pOF missing exit-latency-us property\n", 2805ea11e94bSRob Herring state_node); 280630f60428SLina Iyer return -EINVAL; 280730f60428SLina Iyer } 280830f60428SLina Iyer 280930f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 281030f60428SLina Iyer if (!err) 281130f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 281230f60428SLina Iyer 281330f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 281430f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 28150c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 281630f60428SLina Iyer 281730f60428SLina Iyer return 0; 281830f60428SLina Iyer } 281930f60428SLina Iyer 2820a3381e3aSUlf Hansson static int genpd_iterate_idle_states(struct device_node *dn, 2821a3381e3aSUlf Hansson struct genpd_power_state *states) 2822a3381e3aSUlf Hansson { 2823a3381e3aSUlf Hansson int ret; 2824a3381e3aSUlf Hansson struct of_phandle_iterator it; 2825a3381e3aSUlf Hansson struct device_node *np; 2826a3381e3aSUlf Hansson int i = 0; 2827a3381e3aSUlf Hansson 2828a3381e3aSUlf Hansson ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2829a3381e3aSUlf Hansson if (ret <= 0) 283056cb2689SUlf Hansson return ret == -ENOENT ? 0 : ret; 2831a3381e3aSUlf Hansson 2832a3381e3aSUlf Hansson /* Loop over the phandles until all the requested entry is found */ 2833a3381e3aSUlf Hansson of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) { 2834a3381e3aSUlf Hansson np = it.node; 2835a3381e3aSUlf Hansson if (!of_match_node(idle_state_match, np)) 2836a3381e3aSUlf Hansson continue; 2837a3381e3aSUlf Hansson if (states) { 2838a3381e3aSUlf Hansson ret = genpd_parse_state(&states[i], np); 2839a3381e3aSUlf Hansson if (ret) { 2840a3381e3aSUlf Hansson pr_err("Parsing idle state node %pOF failed with err %d\n", 2841a3381e3aSUlf Hansson np, ret); 2842a3381e3aSUlf Hansson of_node_put(np); 2843a3381e3aSUlf Hansson return ret; 2844a3381e3aSUlf Hansson } 2845a3381e3aSUlf Hansson } 2846a3381e3aSUlf Hansson i++; 2847a3381e3aSUlf Hansson } 2848a3381e3aSUlf Hansson 2849a3381e3aSUlf Hansson return i; 2850a3381e3aSUlf Hansson } 2851a3381e3aSUlf Hansson 285230f60428SLina Iyer /** 285330f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 285430f60428SLina Iyer * 285530f60428SLina Iyer * @dn: The genpd device node 285630f60428SLina Iyer * @states: The pointer to which the state array will be saved. 285730f60428SLina Iyer * @n: The count of elements in the array returned from this function. 285830f60428SLina Iyer * 285930f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 286030f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 28612c361684SUlf Hansson * free the memory after use. If any or zero compatible domain idle states is 28622c361684SUlf Hansson * found it returns 0 and in case of errors, a negative error code is returned. 286330f60428SLina Iyer */ 286430f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 286530f60428SLina Iyer struct genpd_power_state **states, int *n) 286630f60428SLina Iyer { 286730f60428SLina Iyer struct genpd_power_state *st; 2868a3381e3aSUlf Hansson int ret; 286930f60428SLina Iyer 2870a3381e3aSUlf Hansson ret = genpd_iterate_idle_states(dn, NULL); 28712c361684SUlf Hansson if (ret < 0) 28722c361684SUlf Hansson return ret; 28732c361684SUlf Hansson 28742c361684SUlf Hansson if (!ret) { 28752c361684SUlf Hansson *states = NULL; 28762c361684SUlf Hansson *n = 0; 28772c361684SUlf Hansson return 0; 28782c361684SUlf Hansson } 287930f60428SLina Iyer 2880a3381e3aSUlf Hansson st = kcalloc(ret, sizeof(*st), GFP_KERNEL); 288130f60428SLina Iyer if (!st) 288230f60428SLina Iyer return -ENOMEM; 288330f60428SLina Iyer 2884a3381e3aSUlf Hansson ret = genpd_iterate_idle_states(dn, st); 2885a3381e3aSUlf Hansson if (ret <= 0) { 288630f60428SLina Iyer kfree(st); 2887a3381e3aSUlf Hansson return ret < 0 ? ret : -EINVAL; 288830f60428SLina Iyer } 288930f60428SLina Iyer 289030f60428SLina Iyer *states = st; 2891a3381e3aSUlf Hansson *n = ret; 289230f60428SLina Iyer 289330f60428SLina Iyer return 0; 289430f60428SLina Iyer } 289530f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 289630f60428SLina Iyer 28976e41766aSViresh Kumar /** 2898e38f89d3SViresh Kumar * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node. 2899e38f89d3SViresh Kumar * 2900e38f89d3SViresh Kumar * @genpd_dev: Genpd's device for which the performance-state needs to be found. 2901e38f89d3SViresh Kumar * @opp: struct dev_pm_opp of the OPP for which we need to find performance 2902e38f89d3SViresh Kumar * state. 2903e38f89d3SViresh Kumar * 2904e38f89d3SViresh Kumar * Returns performance state encoded in the OPP of the genpd. This calls 2905e38f89d3SViresh Kumar * platform specific genpd->opp_to_performance_state() callback to translate 2906e38f89d3SViresh Kumar * power domain OPP to performance state. 2907e38f89d3SViresh Kumar * 2908e38f89d3SViresh Kumar * Returns performance state on success and 0 on failure. 2909e38f89d3SViresh Kumar */ 2910e38f89d3SViresh Kumar unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev, 2911e38f89d3SViresh Kumar struct dev_pm_opp *opp) 2912e38f89d3SViresh Kumar { 2913e38f89d3SViresh Kumar struct generic_pm_domain *genpd = NULL; 2914e38f89d3SViresh Kumar int state; 2915e38f89d3SViresh Kumar 2916e38f89d3SViresh Kumar genpd = container_of(genpd_dev, struct generic_pm_domain, dev); 2917e38f89d3SViresh Kumar 2918e38f89d3SViresh Kumar if (unlikely(!genpd->opp_to_performance_state)) 2919e38f89d3SViresh Kumar return 0; 2920e38f89d3SViresh Kumar 2921e38f89d3SViresh Kumar genpd_lock(genpd); 2922e38f89d3SViresh Kumar state = genpd->opp_to_performance_state(genpd, opp); 2923e38f89d3SViresh Kumar genpd_unlock(genpd); 2924e38f89d3SViresh Kumar 2925e38f89d3SViresh Kumar return state; 2926e38f89d3SViresh Kumar } 2927e38f89d3SViresh Kumar EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state); 2928e38f89d3SViresh Kumar 29293c095f32SUlf Hansson static int __init genpd_bus_init(void) 29303c095f32SUlf Hansson { 29313c095f32SUlf Hansson return bus_register(&genpd_bus_type); 29323c095f32SUlf Hansson } 29333c095f32SUlf Hansson core_initcall(genpd_bus_init); 29343c095f32SUlf Hansson 2935d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 29362bd5306aSMaciej Matraszek 29372bd5306aSMaciej Matraszek 29382bd5306aSMaciej Matraszek /*** debugfs support ***/ 29392bd5306aSMaciej Matraszek 29408b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 29412bd5306aSMaciej Matraszek /* 29422bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2943d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 29442bd5306aSMaciej Matraszek */ 29452bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 29462bd5306aSMaciej Matraszek { 29472bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 29482bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 29492bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 29502bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 29512bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 29522bd5306aSMaciej Matraszek }; 29532bd5306aSMaciej Matraszek const char *p = ""; 29542bd5306aSMaciej Matraszek 29552bd5306aSMaciej Matraszek if (dev->power.runtime_error) 29562bd5306aSMaciej Matraszek p = "error"; 29572bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 29582bd5306aSMaciej Matraszek p = "unsupported"; 29592bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 29602bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 29612bd5306aSMaciej Matraszek else 29622bd5306aSMaciej Matraszek WARN_ON(1); 29632bd5306aSMaciej Matraszek 296445fbc464SDmitry Osipenko seq_printf(s, "%-25s ", p); 296545fbc464SDmitry Osipenko } 296645fbc464SDmitry Osipenko 296745fbc464SDmitry Osipenko static void perf_status_str(struct seq_file *s, struct device *dev) 296845fbc464SDmitry Osipenko { 296945fbc464SDmitry Osipenko struct generic_pm_domain_data *gpd_data; 297045fbc464SDmitry Osipenko 297145fbc464SDmitry Osipenko gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 297245fbc464SDmitry Osipenko seq_put_decimal_ull(s, "", gpd_data->performance_state); 29732bd5306aSMaciej Matraszek } 29742bd5306aSMaciej Matraszek 29759e9704eaSUlf Hansson static int genpd_summary_one(struct seq_file *s, 297666a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 29772bd5306aSMaciej Matraszek { 29782bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 297949f618e1SUlf Hansson [GENPD_STATE_ON] = "on", 298049f618e1SUlf Hansson [GENPD_STATE_OFF] = "off" 29812bd5306aSMaciej Matraszek }; 29822bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 29832bd5306aSMaciej Matraszek const char *kobj_path; 29842bd5306aSMaciej Matraszek struct gpd_link *link; 29856954d432SGeert Uytterhoeven char state[16]; 29862bd5306aSMaciej Matraszek int ret; 29872bd5306aSMaciej Matraszek 298835241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 29892bd5306aSMaciej Matraszek if (ret) 29902bd5306aSMaciej Matraszek return -ERESTARTSYS; 29912bd5306aSMaciej Matraszek 299266a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 29932bd5306aSMaciej Matraszek goto exit; 299441e2c8e0SUlf Hansson if (!genpd_status_on(genpd)) 29950ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 29966954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2997fc5cbf0cSAxel Haslam else 29986954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 29996954d432SGeert Uytterhoeven status_lookup[genpd->status]); 300045fbc464SDmitry Osipenko seq_printf(s, "%-30s %-50s %u", genpd->name, state, genpd->performance_state); 30012bd5306aSMaciej Matraszek 30022bd5306aSMaciej Matraszek /* 30032bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 30048d87ae48SKees Cook * parent and child, so we are safe. 300566a5ca4bSKevin Hilman * Also genpd->name is immutable. 30062bd5306aSMaciej Matraszek */ 30078d87ae48SKees Cook list_for_each_entry(link, &genpd->parent_links, parent_node) { 300845fbc464SDmitry Osipenko if (list_is_first(&link->parent_node, &genpd->parent_links)) 300945fbc464SDmitry Osipenko seq_printf(s, "\n%48s", " "); 30108d87ae48SKees Cook seq_printf(s, "%s", link->child->name); 30118d87ae48SKees Cook if (!list_is_last(&link->parent_node, &genpd->parent_links)) 30122bd5306aSMaciej Matraszek seq_puts(s, ", "); 30132bd5306aSMaciej Matraszek } 30142bd5306aSMaciej Matraszek 301566a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 3016d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 3017d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 3018d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 30192bd5306aSMaciej Matraszek if (kobj_path == NULL) 30202bd5306aSMaciej Matraszek continue; 30212bd5306aSMaciej Matraszek 30222bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 30232bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 302445fbc464SDmitry Osipenko perf_status_str(s, pm_data->dev); 30252bd5306aSMaciej Matraszek kfree(kobj_path); 30262bd5306aSMaciej Matraszek } 30272bd5306aSMaciej Matraszek 30282bd5306aSMaciej Matraszek seq_puts(s, "\n"); 30292bd5306aSMaciej Matraszek exit: 303035241d12SLina Iyer genpd_unlock(genpd); 30312bd5306aSMaciej Matraszek 30322bd5306aSMaciej Matraszek return 0; 30332bd5306aSMaciej Matraszek } 30342bd5306aSMaciej Matraszek 3035d32dcc6cSYangtao Li static int summary_show(struct seq_file *s, void *data) 30362bd5306aSMaciej Matraszek { 303766a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 30382bd5306aSMaciej Matraszek int ret = 0; 30392bd5306aSMaciej Matraszek 304045fbc464SDmitry Osipenko seq_puts(s, "domain status children performance\n"); 30412bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 304245fbc464SDmitry Osipenko seq_puts(s, "----------------------------------------------------------------------------------------------\n"); 30432bd5306aSMaciej Matraszek 30442bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 30452bd5306aSMaciej Matraszek if (ret) 30462bd5306aSMaciej Matraszek return -ERESTARTSYS; 30472bd5306aSMaciej Matraszek 304866a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 30499e9704eaSUlf Hansson ret = genpd_summary_one(s, genpd); 30502bd5306aSMaciej Matraszek if (ret) 30512bd5306aSMaciej Matraszek break; 30522bd5306aSMaciej Matraszek } 30532bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 30542bd5306aSMaciej Matraszek 30552bd5306aSMaciej Matraszek return ret; 30562bd5306aSMaciej Matraszek } 30572bd5306aSMaciej Matraszek 3058d32dcc6cSYangtao Li static int status_show(struct seq_file *s, void *data) 30592bd5306aSMaciej Matraszek { 3060b6a1d093SThara Gopinath static const char * const status_lookup[] = { 306149f618e1SUlf Hansson [GENPD_STATE_ON] = "on", 306249f618e1SUlf Hansson [GENPD_STATE_OFF] = "off" 3063b6a1d093SThara Gopinath }; 3064b6a1d093SThara Gopinath 3065b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3066b6a1d093SThara Gopinath int ret = 0; 3067b6a1d093SThara Gopinath 3068b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3069b6a1d093SThara Gopinath if (ret) 3070b6a1d093SThara Gopinath return -ERESTARTSYS; 3071b6a1d093SThara Gopinath 3072b6a1d093SThara Gopinath if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup))) 3073b6a1d093SThara Gopinath goto exit; 3074b6a1d093SThara Gopinath 307549f618e1SUlf Hansson if (genpd->status == GENPD_STATE_OFF) 3076b6a1d093SThara Gopinath seq_printf(s, "%s-%u\n", status_lookup[genpd->status], 3077b6a1d093SThara Gopinath genpd->state_idx); 3078b6a1d093SThara Gopinath else 3079b6a1d093SThara Gopinath seq_printf(s, "%s\n", status_lookup[genpd->status]); 3080b6a1d093SThara Gopinath exit: 3081b6a1d093SThara Gopinath genpd_unlock(genpd); 3082b6a1d093SThara Gopinath return ret; 30832bd5306aSMaciej Matraszek } 30842bd5306aSMaciej Matraszek 3085d32dcc6cSYangtao Li static int sub_domains_show(struct seq_file *s, void *data) 3086b6a1d093SThara Gopinath { 3087b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3088b6a1d093SThara Gopinath struct gpd_link *link; 3089b6a1d093SThara Gopinath int ret = 0; 3090b6a1d093SThara Gopinath 3091b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3092b6a1d093SThara Gopinath if (ret) 3093b6a1d093SThara Gopinath return -ERESTARTSYS; 3094b6a1d093SThara Gopinath 30958d87ae48SKees Cook list_for_each_entry(link, &genpd->parent_links, parent_node) 30968d87ae48SKees Cook seq_printf(s, "%s\n", link->child->name); 3097b6a1d093SThara Gopinath 3098b6a1d093SThara Gopinath genpd_unlock(genpd); 3099b6a1d093SThara Gopinath return ret; 3100b6a1d093SThara Gopinath } 3101b6a1d093SThara Gopinath 3102d32dcc6cSYangtao Li static int idle_states_show(struct seq_file *s, void *data) 3103b6a1d093SThara Gopinath { 3104b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3105b6a1d093SThara Gopinath unsigned int i; 3106b6a1d093SThara Gopinath int ret = 0; 3107b6a1d093SThara Gopinath 3108b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3109b6a1d093SThara Gopinath if (ret) 3110b6a1d093SThara Gopinath return -ERESTARTSYS; 3111b6a1d093SThara Gopinath 3112c6a113b5SLina Iyer seq_puts(s, "State Time Spent(ms) Usage Rejected\n"); 3113b6a1d093SThara Gopinath 3114b6a1d093SThara Gopinath for (i = 0; i < genpd->state_count; i++) { 3115b6a1d093SThara Gopinath ktime_t delta = 0; 3116b6a1d093SThara Gopinath s64 msecs; 3117b6a1d093SThara Gopinath 311849f618e1SUlf Hansson if ((genpd->status == GENPD_STATE_OFF) && 3119b6a1d093SThara Gopinath (genpd->state_idx == i)) 3120b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 3121b6a1d093SThara Gopinath 3122b6a1d093SThara Gopinath msecs = ktime_to_ms( 3123b6a1d093SThara Gopinath ktime_add(genpd->states[i].idle_time, delta)); 3124c6a113b5SLina Iyer seq_printf(s, "S%-13i %-14lld %-14llu %llu\n", i, msecs, 3125c6a113b5SLina Iyer genpd->states[i].usage, genpd->states[i].rejected); 3126b6a1d093SThara Gopinath } 3127b6a1d093SThara Gopinath 3128b6a1d093SThara Gopinath genpd_unlock(genpd); 3129b6a1d093SThara Gopinath return ret; 3130b6a1d093SThara Gopinath } 3131b6a1d093SThara Gopinath 3132d32dcc6cSYangtao Li static int active_time_show(struct seq_file *s, void *data) 3133b6a1d093SThara Gopinath { 3134b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3135b6a1d093SThara Gopinath ktime_t delta = 0; 3136b6a1d093SThara Gopinath int ret = 0; 3137b6a1d093SThara Gopinath 3138b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3139b6a1d093SThara Gopinath if (ret) 3140b6a1d093SThara Gopinath return -ERESTARTSYS; 3141b6a1d093SThara Gopinath 314249f618e1SUlf Hansson if (genpd->status == GENPD_STATE_ON) 3143b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 3144b6a1d093SThara Gopinath 3145b6a1d093SThara Gopinath seq_printf(s, "%lld ms\n", ktime_to_ms( 3146b6a1d093SThara Gopinath ktime_add(genpd->on_time, delta))); 3147b6a1d093SThara Gopinath 3148b6a1d093SThara Gopinath genpd_unlock(genpd); 3149b6a1d093SThara Gopinath return ret; 3150b6a1d093SThara Gopinath } 3151b6a1d093SThara Gopinath 3152d32dcc6cSYangtao Li static int total_idle_time_show(struct seq_file *s, void *data) 3153b6a1d093SThara Gopinath { 3154b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3155b6a1d093SThara Gopinath ktime_t delta = 0, total = 0; 3156b6a1d093SThara Gopinath unsigned int i; 3157b6a1d093SThara Gopinath int ret = 0; 3158b6a1d093SThara Gopinath 3159b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3160b6a1d093SThara Gopinath if (ret) 3161b6a1d093SThara Gopinath return -ERESTARTSYS; 3162b6a1d093SThara Gopinath 3163b6a1d093SThara Gopinath for (i = 0; i < genpd->state_count; i++) { 3164b6a1d093SThara Gopinath 316549f618e1SUlf Hansson if ((genpd->status == GENPD_STATE_OFF) && 3166b6a1d093SThara Gopinath (genpd->state_idx == i)) 3167b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 3168b6a1d093SThara Gopinath 3169b6a1d093SThara Gopinath total = ktime_add(total, genpd->states[i].idle_time); 3170b6a1d093SThara Gopinath } 3171b6a1d093SThara Gopinath total = ktime_add(total, delta); 3172b6a1d093SThara Gopinath 3173b6a1d093SThara Gopinath seq_printf(s, "%lld ms\n", ktime_to_ms(total)); 3174b6a1d093SThara Gopinath 3175b6a1d093SThara Gopinath genpd_unlock(genpd); 3176b6a1d093SThara Gopinath return ret; 3177b6a1d093SThara Gopinath } 3178b6a1d093SThara Gopinath 3179b6a1d093SThara Gopinath 3180d32dcc6cSYangtao Li static int devices_show(struct seq_file *s, void *data) 3181b6a1d093SThara Gopinath { 3182b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 3183b6a1d093SThara Gopinath struct pm_domain_data *pm_data; 3184b6a1d093SThara Gopinath const char *kobj_path; 3185b6a1d093SThara Gopinath int ret = 0; 3186b6a1d093SThara Gopinath 3187b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 3188b6a1d093SThara Gopinath if (ret) 3189b6a1d093SThara Gopinath return -ERESTARTSYS; 3190b6a1d093SThara Gopinath 3191b6a1d093SThara Gopinath list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 3192b6a1d093SThara Gopinath kobj_path = kobject_get_path(&pm_data->dev->kobj, 3193b6a1d093SThara Gopinath genpd_is_irq_safe(genpd) ? 3194b6a1d093SThara Gopinath GFP_ATOMIC : GFP_KERNEL); 3195b6a1d093SThara Gopinath if (kobj_path == NULL) 3196b6a1d093SThara Gopinath continue; 3197b6a1d093SThara Gopinath 3198b6a1d093SThara Gopinath seq_printf(s, "%s\n", kobj_path); 3199b6a1d093SThara Gopinath kfree(kobj_path); 3200b6a1d093SThara Gopinath } 3201b6a1d093SThara Gopinath 3202b6a1d093SThara Gopinath genpd_unlock(genpd); 3203b6a1d093SThara Gopinath return ret; 3204b6a1d093SThara Gopinath } 3205b6a1d093SThara Gopinath 3206d32dcc6cSYangtao Li static int perf_state_show(struct seq_file *s, void *data) 3207e8912812SRajendra Nayak { 3208e8912812SRajendra Nayak struct generic_pm_domain *genpd = s->private; 3209e8912812SRajendra Nayak 3210e8912812SRajendra Nayak if (genpd_lock_interruptible(genpd)) 3211e8912812SRajendra Nayak return -ERESTARTSYS; 3212e8912812SRajendra Nayak 3213e8912812SRajendra Nayak seq_printf(s, "%u\n", genpd->performance_state); 3214e8912812SRajendra Nayak 3215e8912812SRajendra Nayak genpd_unlock(genpd); 3216e8912812SRajendra Nayak return 0; 3217e8912812SRajendra Nayak } 3218e8912812SRajendra Nayak 3219d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(summary); 3220d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(status); 3221d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(sub_domains); 3222d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(idle_states); 3223d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(active_time); 3224d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(total_idle_time); 3225d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(devices); 3226d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(perf_state); 32272bd5306aSMaciej Matraszek 3228718072ceSThierry Strudel static void genpd_debug_add(struct generic_pm_domain *genpd) 32292bd5306aSMaciej Matraszek { 32302bd5306aSMaciej Matraszek struct dentry *d; 32312bd5306aSMaciej Matraszek 3232718072ceSThierry Strudel if (!genpd_debugfs_dir) 3233718072ceSThierry Strudel return; 32342bd5306aSMaciej Matraszek 32359e9704eaSUlf Hansson d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); 3236b6a1d093SThara Gopinath 3237b6a1d093SThara Gopinath debugfs_create_file("current_state", 0444, 3238d32dcc6cSYangtao Li d, genpd, &status_fops); 3239b6a1d093SThara Gopinath debugfs_create_file("sub_domains", 0444, 3240d32dcc6cSYangtao Li d, genpd, &sub_domains_fops); 3241b6a1d093SThara Gopinath debugfs_create_file("idle_states", 0444, 3242d32dcc6cSYangtao Li d, genpd, &idle_states_fops); 3243b6a1d093SThara Gopinath debugfs_create_file("active_time", 0444, 3244d32dcc6cSYangtao Li d, genpd, &active_time_fops); 3245b6a1d093SThara Gopinath debugfs_create_file("total_idle_time", 0444, 3246d32dcc6cSYangtao Li d, genpd, &total_idle_time_fops); 3247b6a1d093SThara Gopinath debugfs_create_file("devices", 0444, 3248d32dcc6cSYangtao Li d, genpd, &devices_fops); 3249e8912812SRajendra Nayak if (genpd->set_performance_state) 3250e8912812SRajendra Nayak debugfs_create_file("perf_state", 0444, 3251d32dcc6cSYangtao Li d, genpd, &perf_state_fops); 3252b6a1d093SThara Gopinath } 3253b6a1d093SThara Gopinath 3254718072ceSThierry Strudel static int __init genpd_debug_init(void) 3255718072ceSThierry Strudel { 3256718072ceSThierry Strudel struct generic_pm_domain *genpd; 3257718072ceSThierry Strudel 3258718072ceSThierry Strudel genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 3259718072ceSThierry Strudel 3260718072ceSThierry Strudel debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir, 3261718072ceSThierry Strudel NULL, &summary_fops); 3262718072ceSThierry Strudel 3263718072ceSThierry Strudel list_for_each_entry(genpd, &gpd_list, gpd_list_node) 3264718072ceSThierry Strudel genpd_debug_add(genpd); 3265718072ceSThierry Strudel 32662bd5306aSMaciej Matraszek return 0; 32672bd5306aSMaciej Matraszek } 32689e9704eaSUlf Hansson late_initcall(genpd_debug_init); 32692bd5306aSMaciej Matraszek 32709e9704eaSUlf Hansson static void __exit genpd_debug_exit(void) 32712bd5306aSMaciej Matraszek { 32729e9704eaSUlf Hansson debugfs_remove_recursive(genpd_debugfs_dir); 32732bd5306aSMaciej Matraszek } 32749e9704eaSUlf Hansson __exitcall(genpd_debug_exit); 32758b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 3276