1f721889fSRafael J. Wysocki /* 2f721889fSRafael J. Wysocki * drivers/base/power/domain.c - Common code related to device power domains. 3f721889fSRafael J. Wysocki * 4f721889fSRafael J. Wysocki * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 5f721889fSRafael J. Wysocki * 6f721889fSRafael J. Wysocki * This file is released under the GPLv2. 7f721889fSRafael J. Wysocki */ 8f721889fSRafael J. Wysocki 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> 23d5e4cbfeSRafael J. Wysocki 24aa8e54b5STomeu Vizoso #include "power.h" 25aa8e54b5STomeu Vizoso 2693af5e93SGeert Uytterhoeven #define GENPD_RETRY_MAX_MS 250 /* Approximate */ 2793af5e93SGeert Uytterhoeven 28d5e4cbfeSRafael J. Wysocki #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 29d5e4cbfeSRafael J. Wysocki ({ \ 30d5e4cbfeSRafael J. Wysocki type (*__routine)(struct device *__d); \ 31d5e4cbfeSRafael J. Wysocki type __ret = (type)0; \ 32d5e4cbfeSRafael J. Wysocki \ 33d5e4cbfeSRafael J. Wysocki __routine = genpd->dev_ops.callback; \ 34d5e4cbfeSRafael J. Wysocki if (__routine) { \ 35d5e4cbfeSRafael J. Wysocki __ret = __routine(dev); \ 36d5e4cbfeSRafael J. Wysocki } \ 37d5e4cbfeSRafael J. Wysocki __ret; \ 38d5e4cbfeSRafael J. Wysocki }) 39f721889fSRafael J. Wysocki 405125bbf3SRafael J. Wysocki static LIST_HEAD(gpd_list); 415125bbf3SRafael J. Wysocki static DEFINE_MUTEX(gpd_list_lock); 425125bbf3SRafael J. Wysocki 4335241d12SLina Iyer struct genpd_lock_ops { 4435241d12SLina Iyer void (*lock)(struct generic_pm_domain *genpd); 4535241d12SLina Iyer void (*lock_nested)(struct generic_pm_domain *genpd, int depth); 4635241d12SLina Iyer int (*lock_interruptible)(struct generic_pm_domain *genpd); 4735241d12SLina Iyer void (*unlock)(struct generic_pm_domain *genpd); 4835241d12SLina Iyer }; 4935241d12SLina Iyer 5035241d12SLina Iyer static void genpd_lock_mtx(struct generic_pm_domain *genpd) 5135241d12SLina Iyer { 5235241d12SLina Iyer mutex_lock(&genpd->mlock); 5335241d12SLina Iyer } 5435241d12SLina Iyer 5535241d12SLina Iyer static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd, 5635241d12SLina Iyer int depth) 5735241d12SLina Iyer { 5835241d12SLina Iyer mutex_lock_nested(&genpd->mlock, depth); 5935241d12SLina Iyer } 6035241d12SLina Iyer 6135241d12SLina Iyer static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd) 6235241d12SLina Iyer { 6335241d12SLina Iyer return mutex_lock_interruptible(&genpd->mlock); 6435241d12SLina Iyer } 6535241d12SLina Iyer 6635241d12SLina Iyer static void genpd_unlock_mtx(struct generic_pm_domain *genpd) 6735241d12SLina Iyer { 6835241d12SLina Iyer return mutex_unlock(&genpd->mlock); 6935241d12SLina Iyer } 7035241d12SLina Iyer 7135241d12SLina Iyer static const struct genpd_lock_ops genpd_mtx_ops = { 7235241d12SLina Iyer .lock = genpd_lock_mtx, 7335241d12SLina Iyer .lock_nested = genpd_lock_nested_mtx, 7435241d12SLina Iyer .lock_interruptible = genpd_lock_interruptible_mtx, 7535241d12SLina Iyer .unlock = genpd_unlock_mtx, 7635241d12SLina Iyer }; 7735241d12SLina Iyer 78d716f479SLina Iyer static void genpd_lock_spin(struct generic_pm_domain *genpd) 79d716f479SLina Iyer __acquires(&genpd->slock) 80d716f479SLina Iyer { 81d716f479SLina Iyer unsigned long flags; 82d716f479SLina Iyer 83d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 84d716f479SLina Iyer genpd->lock_flags = flags; 85d716f479SLina Iyer } 86d716f479SLina Iyer 87d716f479SLina Iyer static void genpd_lock_nested_spin(struct generic_pm_domain *genpd, 88d716f479SLina Iyer int depth) 89d716f479SLina Iyer __acquires(&genpd->slock) 90d716f479SLina Iyer { 91d716f479SLina Iyer unsigned long flags; 92d716f479SLina Iyer 93d716f479SLina Iyer spin_lock_irqsave_nested(&genpd->slock, flags, depth); 94d716f479SLina Iyer genpd->lock_flags = flags; 95d716f479SLina Iyer } 96d716f479SLina Iyer 97d716f479SLina Iyer static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd) 98d716f479SLina Iyer __acquires(&genpd->slock) 99d716f479SLina Iyer { 100d716f479SLina Iyer unsigned long flags; 101d716f479SLina Iyer 102d716f479SLina Iyer spin_lock_irqsave(&genpd->slock, flags); 103d716f479SLina Iyer genpd->lock_flags = flags; 104d716f479SLina Iyer return 0; 105d716f479SLina Iyer } 106d716f479SLina Iyer 107d716f479SLina Iyer static void genpd_unlock_spin(struct generic_pm_domain *genpd) 108d716f479SLina Iyer __releases(&genpd->slock) 109d716f479SLina Iyer { 110d716f479SLina Iyer spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags); 111d716f479SLina Iyer } 112d716f479SLina Iyer 113d716f479SLina Iyer static const struct genpd_lock_ops genpd_spin_ops = { 114d716f479SLina Iyer .lock = genpd_lock_spin, 115d716f479SLina Iyer .lock_nested = genpd_lock_nested_spin, 116d716f479SLina Iyer .lock_interruptible = genpd_lock_interruptible_spin, 117d716f479SLina Iyer .unlock = genpd_unlock_spin, 118d716f479SLina Iyer }; 119d716f479SLina Iyer 12035241d12SLina Iyer #define genpd_lock(p) p->lock_ops->lock(p) 12135241d12SLina Iyer #define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d) 12235241d12SLina Iyer #define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p) 12335241d12SLina Iyer #define genpd_unlock(p) p->lock_ops->unlock(p) 12435241d12SLina Iyer 12541e2c8e0SUlf Hansson #define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) 126d716f479SLina Iyer #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 127ffaa42e8SUlf Hansson #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) 12895a20ef6SGeert Uytterhoeven #define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) 129d716f479SLina Iyer 130d716f479SLina Iyer static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 131d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd) 132d716f479SLina Iyer { 133d716f479SLina Iyer bool ret; 134d716f479SLina Iyer 135d716f479SLina Iyer ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd); 136d716f479SLina Iyer 137075c37d5SUlf Hansson /* 138075c37d5SUlf Hansson * Warn once if an IRQ safe device is attached to a no sleep domain, as 139075c37d5SUlf Hansson * to indicate a suboptimal configuration for PM. For an always on 140075c37d5SUlf Hansson * domain this isn't case, thus don't warn. 141075c37d5SUlf Hansson */ 142075c37d5SUlf Hansson if (ret && !genpd_is_always_on(genpd)) 143d716f479SLina Iyer dev_warn_once(dev, "PM domain %s will not be powered off\n", 144d716f479SLina Iyer genpd->name); 145d716f479SLina Iyer 146d716f479SLina Iyer return ret; 147d716f479SLina Iyer } 148d716f479SLina Iyer 149446d999cSRussell King /* 150446d999cSRussell King * Get the generic PM domain for a particular struct device. 151446d999cSRussell King * This validates the struct device pointer, the PM domain pointer, 152446d999cSRussell King * and checks that the PM domain pointer is a real generic PM domain. 153446d999cSRussell King * Any failure results in NULL being returned. 154446d999cSRussell King */ 155f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) 156446d999cSRussell King { 157446d999cSRussell King struct generic_pm_domain *genpd = NULL, *gpd; 158446d999cSRussell King 159446d999cSRussell King if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) 160446d999cSRussell King return NULL; 161446d999cSRussell King 162446d999cSRussell King mutex_lock(&gpd_list_lock); 163446d999cSRussell King list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 164446d999cSRussell King if (&gpd->domain == dev->pm_domain) { 165446d999cSRussell King genpd = gpd; 166446d999cSRussell King break; 167446d999cSRussell King } 168446d999cSRussell King } 169446d999cSRussell King mutex_unlock(&gpd_list_lock); 170446d999cSRussell King 171446d999cSRussell King return genpd; 172446d999cSRussell King } 173446d999cSRussell King 174446d999cSRussell King /* 175446d999cSRussell King * This should only be used where we are certain that the pm_domain 176446d999cSRussell King * attached to the device is a genpd domain. 177446d999cSRussell King */ 178446d999cSRussell King static struct generic_pm_domain *dev_to_genpd(struct device *dev) 1795248051bSRafael J. Wysocki { 1805248051bSRafael J. Wysocki if (IS_ERR_OR_NULL(dev->pm_domain)) 1815248051bSRafael J. Wysocki return ERR_PTR(-EINVAL); 1825248051bSRafael J. Wysocki 183596ba34bSRafael J. Wysocki return pd_to_genpd(dev->pm_domain); 1845248051bSRafael J. Wysocki } 185f721889fSRafael J. Wysocki 186d8600c8bSKrzysztof Kozlowski static int genpd_stop_dev(const struct generic_pm_domain *genpd, 187d8600c8bSKrzysztof Kozlowski struct device *dev) 188d5e4cbfeSRafael J. Wysocki { 18951cda844SUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, stop, dev); 190d5e4cbfeSRafael J. Wysocki } 191d5e4cbfeSRafael J. Wysocki 192d8600c8bSKrzysztof Kozlowski static int genpd_start_dev(const struct generic_pm_domain *genpd, 193d8600c8bSKrzysztof Kozlowski struct device *dev) 194d5e4cbfeSRafael J. Wysocki { 195ba2bbfbfSUlf Hansson return GENPD_DEV_CALLBACK(genpd, int, start, dev); 196d5e4cbfeSRafael J. Wysocki } 197d5e4cbfeSRafael J. Wysocki 198c4bb3160SRafael J. Wysocki static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) 199f721889fSRafael J. Wysocki { 200c4bb3160SRafael J. Wysocki bool ret = false; 201c4bb3160SRafael J. Wysocki 202c4bb3160SRafael J. Wysocki if (!WARN_ON(atomic_read(&genpd->sd_count) == 0)) 203c4bb3160SRafael J. Wysocki ret = !!atomic_dec_and_test(&genpd->sd_count); 204c4bb3160SRafael J. Wysocki 205c4bb3160SRafael J. Wysocki return ret; 206c4bb3160SRafael J. Wysocki } 207c4bb3160SRafael J. Wysocki 208c4bb3160SRafael J. Wysocki static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) 209c4bb3160SRafael J. Wysocki { 210c4bb3160SRafael J. Wysocki atomic_inc(&genpd->sd_count); 2114e857c58SPeter Zijlstra smp_mb__after_atomic(); 212f721889fSRafael J. Wysocki } 213f721889fSRafael J. Wysocki 214afece3abSThara Gopinath #ifdef CONFIG_DEBUG_FS 215afece3abSThara Gopinath static void genpd_update_accounting(struct generic_pm_domain *genpd) 216afece3abSThara Gopinath { 217afece3abSThara Gopinath ktime_t delta, now; 218afece3abSThara Gopinath 219afece3abSThara Gopinath now = ktime_get(); 220afece3abSThara Gopinath delta = ktime_sub(now, genpd->accounting_time); 221afece3abSThara Gopinath 222afece3abSThara Gopinath /* 223afece3abSThara Gopinath * If genpd->status is active, it means we are just 224afece3abSThara Gopinath * out of off and so update the idle time and vice 225afece3abSThara Gopinath * versa. 226afece3abSThara Gopinath */ 227afece3abSThara Gopinath if (genpd->status == GPD_STATE_ACTIVE) { 228afece3abSThara Gopinath int state_idx = genpd->state_idx; 229afece3abSThara Gopinath 230afece3abSThara Gopinath genpd->states[state_idx].idle_time = 231afece3abSThara Gopinath ktime_add(genpd->states[state_idx].idle_time, delta); 232afece3abSThara Gopinath } else { 233afece3abSThara Gopinath genpd->on_time = ktime_add(genpd->on_time, delta); 234afece3abSThara Gopinath } 235afece3abSThara Gopinath 236afece3abSThara Gopinath genpd->accounting_time = now; 237afece3abSThara Gopinath } 238afece3abSThara Gopinath #else 239afece3abSThara Gopinath static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} 240afece3abSThara Gopinath #endif 241afece3abSThara Gopinath 242cd50c6d3SViresh Kumar static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd, 243cd50c6d3SViresh Kumar unsigned int state) 244cd50c6d3SViresh Kumar { 245cd50c6d3SViresh Kumar struct generic_pm_domain_data *pd_data; 246cd50c6d3SViresh Kumar struct pm_domain_data *pdd; 24718edf49cSViresh Kumar struct gpd_link *link; 248cd50c6d3SViresh Kumar 249cd50c6d3SViresh Kumar /* New requested state is same as Max requested state */ 250cd50c6d3SViresh Kumar if (state == genpd->performance_state) 251cd50c6d3SViresh Kumar return state; 252cd50c6d3SViresh Kumar 253cd50c6d3SViresh Kumar /* New requested state is higher than Max requested state */ 254cd50c6d3SViresh Kumar if (state > genpd->performance_state) 255cd50c6d3SViresh Kumar return state; 256cd50c6d3SViresh Kumar 257cd50c6d3SViresh Kumar /* Traverse all devices within the domain */ 258cd50c6d3SViresh Kumar list_for_each_entry(pdd, &genpd->dev_list, list_node) { 259cd50c6d3SViresh Kumar pd_data = to_gpd_data(pdd); 260cd50c6d3SViresh Kumar 261cd50c6d3SViresh Kumar if (pd_data->performance_state > state) 262cd50c6d3SViresh Kumar state = pd_data->performance_state; 263cd50c6d3SViresh Kumar } 264cd50c6d3SViresh Kumar 265cd50c6d3SViresh Kumar /* 26618edf49cSViresh Kumar * Traverse all sub-domains within the domain. This can be 26718edf49cSViresh Kumar * done without any additional locking as the link->performance_state 26818edf49cSViresh Kumar * field is protected by the master genpd->lock, which is already taken. 26918edf49cSViresh Kumar * 27018edf49cSViresh Kumar * Also note that link->performance_state (subdomain's performance state 27118edf49cSViresh Kumar * requirement to master domain) is different from 27218edf49cSViresh Kumar * link->slave->performance_state (current performance state requirement 27318edf49cSViresh Kumar * of the devices/sub-domains of the subdomain) and so can have a 27418edf49cSViresh Kumar * different value. 27518edf49cSViresh Kumar * 27618edf49cSViresh Kumar * Note that we also take vote from powered-off sub-domains into account 27718edf49cSViresh Kumar * as the same is done for devices right now. 278cd50c6d3SViresh Kumar */ 27918edf49cSViresh Kumar list_for_each_entry(link, &genpd->master_links, master_node) { 28018edf49cSViresh Kumar if (link->performance_state > state) 28118edf49cSViresh Kumar state = link->performance_state; 28218edf49cSViresh Kumar } 28318edf49cSViresh Kumar 284cd50c6d3SViresh Kumar return state; 285cd50c6d3SViresh Kumar } 286cd50c6d3SViresh Kumar 287cd50c6d3SViresh Kumar static int _genpd_set_performance_state(struct generic_pm_domain *genpd, 28818edf49cSViresh Kumar unsigned int state, int depth) 289cd50c6d3SViresh Kumar { 29018edf49cSViresh Kumar struct generic_pm_domain *master; 29118edf49cSViresh Kumar struct gpd_link *link; 29218edf49cSViresh Kumar int master_state, ret; 293cd50c6d3SViresh Kumar 294cd50c6d3SViresh Kumar if (state == genpd->performance_state) 295cd50c6d3SViresh Kumar return 0; 296cd50c6d3SViresh Kumar 29718edf49cSViresh Kumar /* Propagate to masters of genpd */ 29818edf49cSViresh Kumar list_for_each_entry(link, &genpd->slave_links, slave_node) { 29918edf49cSViresh Kumar master = link->master; 30018edf49cSViresh Kumar 30118edf49cSViresh Kumar if (!master->set_performance_state) 30218edf49cSViresh Kumar continue; 30318edf49cSViresh Kumar 30418edf49cSViresh Kumar /* Find master's performance state */ 30518edf49cSViresh Kumar ret = dev_pm_opp_xlate_performance_state(genpd->opp_table, 30618edf49cSViresh Kumar master->opp_table, 30718edf49cSViresh Kumar state); 30818edf49cSViresh Kumar if (unlikely(ret < 0)) 30918edf49cSViresh Kumar goto err; 31018edf49cSViresh Kumar 31118edf49cSViresh Kumar master_state = ret; 31218edf49cSViresh Kumar 31318edf49cSViresh Kumar genpd_lock_nested(master, depth + 1); 31418edf49cSViresh Kumar 31518edf49cSViresh Kumar link->prev_performance_state = link->performance_state; 31618edf49cSViresh Kumar link->performance_state = master_state; 31718edf49cSViresh Kumar master_state = _genpd_reeval_performance_state(master, 31818edf49cSViresh Kumar master_state); 31918edf49cSViresh Kumar ret = _genpd_set_performance_state(master, master_state, depth + 1); 32018edf49cSViresh Kumar if (ret) 32118edf49cSViresh Kumar link->performance_state = link->prev_performance_state; 32218edf49cSViresh Kumar 32318edf49cSViresh Kumar genpd_unlock(master); 32418edf49cSViresh Kumar 32518edf49cSViresh Kumar if (ret) 32618edf49cSViresh Kumar goto err; 32718edf49cSViresh Kumar } 32818edf49cSViresh Kumar 329cd50c6d3SViresh Kumar ret = genpd->set_performance_state(genpd, state); 330cd50c6d3SViresh Kumar if (ret) 33118edf49cSViresh Kumar goto err; 332cd50c6d3SViresh Kumar 333cd50c6d3SViresh Kumar genpd->performance_state = state; 334cd50c6d3SViresh Kumar return 0; 33518edf49cSViresh Kumar 33618edf49cSViresh Kumar err: 33718edf49cSViresh Kumar /* Encountered an error, lets rollback */ 33818edf49cSViresh Kumar list_for_each_entry_continue_reverse(link, &genpd->slave_links, 33918edf49cSViresh Kumar slave_node) { 34018edf49cSViresh Kumar master = link->master; 34118edf49cSViresh Kumar 34218edf49cSViresh Kumar if (!master->set_performance_state) 34318edf49cSViresh Kumar continue; 34418edf49cSViresh Kumar 34518edf49cSViresh Kumar genpd_lock_nested(master, depth + 1); 34618edf49cSViresh Kumar 34718edf49cSViresh Kumar master_state = link->prev_performance_state; 34818edf49cSViresh Kumar link->performance_state = master_state; 34918edf49cSViresh Kumar 35018edf49cSViresh Kumar master_state = _genpd_reeval_performance_state(master, 35118edf49cSViresh Kumar master_state); 35218edf49cSViresh Kumar if (_genpd_set_performance_state(master, master_state, depth + 1)) { 35318edf49cSViresh Kumar pr_err("%s: Failed to roll back to %d performance state\n", 35418edf49cSViresh Kumar master->name, master_state); 35518edf49cSViresh Kumar } 35618edf49cSViresh Kumar 35718edf49cSViresh Kumar genpd_unlock(master); 35818edf49cSViresh Kumar } 35918edf49cSViresh Kumar 36018edf49cSViresh Kumar return ret; 361cd50c6d3SViresh Kumar } 362cd50c6d3SViresh Kumar 36342f6284aSViresh Kumar /** 36442f6284aSViresh Kumar * dev_pm_genpd_set_performance_state- Set performance state of device's power 36542f6284aSViresh Kumar * domain. 36642f6284aSViresh Kumar * 36742f6284aSViresh Kumar * @dev: Device for which the performance-state needs to be set. 36842f6284aSViresh Kumar * @state: Target performance state of the device. This can be set as 0 when the 36942f6284aSViresh Kumar * device doesn't have any performance state constraints left (And so 37042f6284aSViresh Kumar * the device wouldn't participate anymore to find the target 37142f6284aSViresh Kumar * performance state of the genpd). 37242f6284aSViresh Kumar * 37342f6284aSViresh Kumar * It is assumed that the users guarantee that the genpd wouldn't be detached 37442f6284aSViresh Kumar * while this routine is getting called. 37542f6284aSViresh Kumar * 37642f6284aSViresh Kumar * Returns 0 on success and negative error values on failures. 37742f6284aSViresh Kumar */ 37842f6284aSViresh Kumar int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) 37942f6284aSViresh Kumar { 38042f6284aSViresh Kumar struct generic_pm_domain *genpd; 381cd50c6d3SViresh Kumar struct generic_pm_domain_data *gpd_data; 38242f6284aSViresh Kumar unsigned int prev; 383cd50c6d3SViresh Kumar int ret; 38442f6284aSViresh Kumar 38542f6284aSViresh Kumar genpd = dev_to_genpd(dev); 38642f6284aSViresh Kumar if (IS_ERR(genpd)) 38742f6284aSViresh Kumar return -ENODEV; 38842f6284aSViresh Kumar 38942f6284aSViresh Kumar if (unlikely(!genpd->set_performance_state)) 39042f6284aSViresh Kumar return -EINVAL; 39142f6284aSViresh Kumar 39242f6284aSViresh Kumar if (unlikely(!dev->power.subsys_data || 39342f6284aSViresh Kumar !dev->power.subsys_data->domain_data)) { 39442f6284aSViresh Kumar WARN_ON(1); 39542f6284aSViresh Kumar return -EINVAL; 39642f6284aSViresh Kumar } 39742f6284aSViresh Kumar 39842f6284aSViresh Kumar genpd_lock(genpd); 39942f6284aSViresh Kumar 40042f6284aSViresh Kumar gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); 40142f6284aSViresh Kumar prev = gpd_data->performance_state; 40242f6284aSViresh Kumar gpd_data->performance_state = state; 40342f6284aSViresh Kumar 404cd50c6d3SViresh Kumar state = _genpd_reeval_performance_state(genpd, state); 40518edf49cSViresh Kumar ret = _genpd_set_performance_state(genpd, state, 0); 406cd50c6d3SViresh Kumar if (ret) 40742f6284aSViresh Kumar gpd_data->performance_state = prev; 40842f6284aSViresh Kumar 40942f6284aSViresh Kumar genpd_unlock(genpd); 41042f6284aSViresh Kumar 41142f6284aSViresh Kumar return ret; 41242f6284aSViresh Kumar } 41342f6284aSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); 41442f6284aSViresh Kumar 41586e12eacSUlf Hansson static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 416c8f0ea45SGeert Uytterhoeven { 417fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 418c8f0ea45SGeert Uytterhoeven ktime_t time_start; 419c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 420c8f0ea45SGeert Uytterhoeven int ret; 421c8f0ea45SGeert Uytterhoeven 422c8f0ea45SGeert Uytterhoeven if (!genpd->power_on) 423c8f0ea45SGeert Uytterhoeven return 0; 424c8f0ea45SGeert Uytterhoeven 425a4630c61SGeert Uytterhoeven if (!timed) 426a4630c61SGeert Uytterhoeven return genpd->power_on(genpd); 427a4630c61SGeert Uytterhoeven 428c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 429c8f0ea45SGeert Uytterhoeven ret = genpd->power_on(genpd); 430c8f0ea45SGeert Uytterhoeven if (ret) 431c8f0ea45SGeert Uytterhoeven return ret; 432c8f0ea45SGeert Uytterhoeven 433c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 434fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) 435c8f0ea45SGeert Uytterhoeven return ret; 436c8f0ea45SGeert Uytterhoeven 437fc5cbf0cSAxel Haslam genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 438c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 4396d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 440c8f0ea45SGeert Uytterhoeven genpd->name, "on", elapsed_ns); 441c8f0ea45SGeert Uytterhoeven 442c8f0ea45SGeert Uytterhoeven return ret; 443c8f0ea45SGeert Uytterhoeven } 444c8f0ea45SGeert Uytterhoeven 44586e12eacSUlf Hansson static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed) 446c8f0ea45SGeert Uytterhoeven { 447fc5cbf0cSAxel Haslam unsigned int state_idx = genpd->state_idx; 448c8f0ea45SGeert Uytterhoeven ktime_t time_start; 449c8f0ea45SGeert Uytterhoeven s64 elapsed_ns; 450c8f0ea45SGeert Uytterhoeven int ret; 451c8f0ea45SGeert Uytterhoeven 452c8f0ea45SGeert Uytterhoeven if (!genpd->power_off) 453c8f0ea45SGeert Uytterhoeven return 0; 454c8f0ea45SGeert Uytterhoeven 455a4630c61SGeert Uytterhoeven if (!timed) 456a4630c61SGeert Uytterhoeven return genpd->power_off(genpd); 457a4630c61SGeert Uytterhoeven 458c8f0ea45SGeert Uytterhoeven time_start = ktime_get(); 459c8f0ea45SGeert Uytterhoeven ret = genpd->power_off(genpd); 460c8f0ea45SGeert Uytterhoeven if (ret == -EBUSY) 461c8f0ea45SGeert Uytterhoeven return ret; 462c8f0ea45SGeert Uytterhoeven 463c8f0ea45SGeert Uytterhoeven elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 464fc5cbf0cSAxel Haslam if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns) 465c8f0ea45SGeert Uytterhoeven return ret; 466c8f0ea45SGeert Uytterhoeven 467fc5cbf0cSAxel Haslam genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 468c8f0ea45SGeert Uytterhoeven genpd->max_off_time_changed = true; 4696d7d5c32SRussell King pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 470c8f0ea45SGeert Uytterhoeven genpd->name, "off", elapsed_ns); 471c8f0ea45SGeert Uytterhoeven 472c8f0ea45SGeert Uytterhoeven return ret; 473c8f0ea45SGeert Uytterhoeven } 474c8f0ea45SGeert Uytterhoeven 475f721889fSRafael J. Wysocki /** 47686e12eacSUlf Hansson * genpd_queue_power_off_work - Queue up the execution of genpd_power_off(). 477a3d09c73SMoritz Fischer * @genpd: PM domain to power off. 47829e47e21SUlf Hansson * 47986e12eacSUlf Hansson * Queue up the execution of genpd_power_off() unless it's already been done 48029e47e21SUlf Hansson * before. 48129e47e21SUlf Hansson */ 48229e47e21SUlf Hansson static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) 48329e47e21SUlf Hansson { 48429e47e21SUlf Hansson queue_work(pm_wq, &genpd->power_off_work); 48529e47e21SUlf Hansson } 48629e47e21SUlf Hansson 48729e47e21SUlf Hansson /** 4881f8728b7SUlf Hansson * genpd_power_off - Remove power from a given PM domain. 4891f8728b7SUlf Hansson * @genpd: PM domain to power down. 4903c64649dSUlf Hansson * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the 4913c64649dSUlf Hansson * RPM status of the releated device is in an intermediate state, not yet turned 4923c64649dSUlf Hansson * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not 4933c64649dSUlf Hansson * be RPM_SUSPENDED, while it tries to power off the PM domain. 4941f8728b7SUlf Hansson * 4951f8728b7SUlf Hansson * If all of the @genpd's devices have been suspended and all of its subdomains 4961f8728b7SUlf Hansson * have been powered down, remove power from @genpd. 4971f8728b7SUlf Hansson */ 4982da83545SUlf Hansson static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, 4992da83545SUlf Hansson unsigned int depth) 5001f8728b7SUlf Hansson { 5011f8728b7SUlf Hansson struct pm_domain_data *pdd; 5021f8728b7SUlf Hansson struct gpd_link *link; 5031f8728b7SUlf Hansson unsigned int not_suspended = 0; 5041f8728b7SUlf Hansson 5051f8728b7SUlf Hansson /* 5061f8728b7SUlf Hansson * Do not try to power off the domain in the following situations: 5071f8728b7SUlf Hansson * (1) The domain is already in the "power off" state. 5081f8728b7SUlf Hansson * (2) System suspend is in progress. 5091f8728b7SUlf Hansson */ 51041e2c8e0SUlf Hansson if (!genpd_status_on(genpd) || genpd->prepared_count > 0) 5111f8728b7SUlf Hansson return 0; 5121f8728b7SUlf Hansson 513ffaa42e8SUlf Hansson /* 514ffaa42e8SUlf Hansson * Abort power off for the PM domain in the following situations: 515ffaa42e8SUlf Hansson * (1) The domain is configured as always on. 516ffaa42e8SUlf Hansson * (2) When the domain has a subdomain being powered on. 517ffaa42e8SUlf Hansson */ 518ffaa42e8SUlf Hansson if (genpd_is_always_on(genpd) || atomic_read(&genpd->sd_count) > 0) 5191f8728b7SUlf Hansson return -EBUSY; 5201f8728b7SUlf Hansson 5211f8728b7SUlf Hansson list_for_each_entry(pdd, &genpd->dev_list, list_node) { 5221f8728b7SUlf Hansson enum pm_qos_flags_status stat; 5231f8728b7SUlf Hansson 52420f97cafSRafael J. Wysocki stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF); 5251f8728b7SUlf Hansson if (stat > PM_QOS_FLAGS_NONE) 5261f8728b7SUlf Hansson return -EBUSY; 5271f8728b7SUlf Hansson 5281f8728b7SUlf Hansson /* 5291f8728b7SUlf Hansson * Do not allow PM domain to be powered off, when an IRQ safe 5301f8728b7SUlf Hansson * device is part of a non-IRQ safe domain. 5311f8728b7SUlf Hansson */ 5321f8728b7SUlf Hansson if (!pm_runtime_suspended(pdd->dev) || 5331f8728b7SUlf Hansson irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) 5341f8728b7SUlf Hansson not_suspended++; 5351f8728b7SUlf Hansson } 5361f8728b7SUlf Hansson 5373c64649dSUlf Hansson if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) 5381f8728b7SUlf Hansson return -EBUSY; 5391f8728b7SUlf Hansson 5401f8728b7SUlf Hansson if (genpd->gov && genpd->gov->power_down_ok) { 5411f8728b7SUlf Hansson if (!genpd->gov->power_down_ok(&genpd->domain)) 5421f8728b7SUlf Hansson return -EAGAIN; 5431f8728b7SUlf Hansson } 5441f8728b7SUlf Hansson 5452c9b7f87SUlf Hansson /* Default to shallowest state. */ 5462c9b7f87SUlf Hansson if (!genpd->gov) 5472c9b7f87SUlf Hansson genpd->state_idx = 0; 5482c9b7f87SUlf Hansson 5491f8728b7SUlf Hansson if (genpd->power_off) { 5501f8728b7SUlf Hansson int ret; 5511f8728b7SUlf Hansson 5521f8728b7SUlf Hansson if (atomic_read(&genpd->sd_count) > 0) 5531f8728b7SUlf Hansson return -EBUSY; 5541f8728b7SUlf Hansson 5551f8728b7SUlf Hansson /* 5561f8728b7SUlf Hansson * If sd_count > 0 at this point, one of the subdomains hasn't 5571f8728b7SUlf Hansson * managed to call genpd_power_on() for the master yet after 5581f8728b7SUlf Hansson * incrementing it. In that case genpd_power_on() will wait 5591f8728b7SUlf Hansson * for us to drop the lock, so we can call .power_off() and let 5601f8728b7SUlf Hansson * the genpd_power_on() restore power for us (this shouldn't 5611f8728b7SUlf Hansson * happen very often). 5621f8728b7SUlf Hansson */ 5631f8728b7SUlf Hansson ret = _genpd_power_off(genpd, true); 5641f8728b7SUlf Hansson if (ret) 5651f8728b7SUlf Hansson return ret; 5661f8728b7SUlf Hansson } 5671f8728b7SUlf Hansson 5681f8728b7SUlf Hansson genpd->status = GPD_STATE_POWER_OFF; 569afece3abSThara Gopinath genpd_update_accounting(genpd); 5701f8728b7SUlf Hansson 5711f8728b7SUlf Hansson list_for_each_entry(link, &genpd->slave_links, slave_node) { 5721f8728b7SUlf Hansson genpd_sd_counter_dec(link->master); 5732da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 5742da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 5752da83545SUlf Hansson genpd_unlock(link->master); 5761f8728b7SUlf Hansson } 5771f8728b7SUlf Hansson 5781f8728b7SUlf Hansson return 0; 5791f8728b7SUlf Hansson } 5801f8728b7SUlf Hansson 5811f8728b7SUlf Hansson /** 58286e12eacSUlf Hansson * genpd_power_on - Restore power to a given PM domain and its masters. 5835248051bSRafael J. Wysocki * @genpd: PM domain to power up. 5840106ef51SMarek Szyprowski * @depth: nesting count for lockdep. 5855248051bSRafael J. Wysocki * 5865063ce15SRafael J. Wysocki * Restore power to @genpd and all of its masters so that it is possible to 5875248051bSRafael J. Wysocki * resume a device belonging to it. 5885248051bSRafael J. Wysocki */ 58986e12eacSUlf Hansson static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) 5905248051bSRafael J. Wysocki { 5915063ce15SRafael J. Wysocki struct gpd_link *link; 5925248051bSRafael J. Wysocki int ret = 0; 5935248051bSRafael J. Wysocki 59441e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 5953f241775SRafael J. Wysocki return 0; 5965248051bSRafael J. Wysocki 5975063ce15SRafael J. Wysocki /* 5985063ce15SRafael J. Wysocki * The list is guaranteed not to change while the loop below is being 5995063ce15SRafael J. Wysocki * executed, unless one of the masters' .power_on() callbacks fiddles 6005063ce15SRafael J. Wysocki * with it. 6015063ce15SRafael J. Wysocki */ 6025063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 6030106ef51SMarek Szyprowski struct generic_pm_domain *master = link->master; 6045248051bSRafael J. Wysocki 6050106ef51SMarek Szyprowski genpd_sd_counter_inc(master); 6060106ef51SMarek Szyprowski 60735241d12SLina Iyer genpd_lock_nested(master, depth + 1); 60886e12eacSUlf Hansson ret = genpd_power_on(master, depth + 1); 60935241d12SLina Iyer genpd_unlock(master); 6100106ef51SMarek Szyprowski 6115063ce15SRafael J. Wysocki if (ret) { 6120106ef51SMarek Szyprowski genpd_sd_counter_dec(master); 6139e08cf42SRafael J. Wysocki goto err; 6145248051bSRafael J. Wysocki } 6155063ce15SRafael J. Wysocki } 6165248051bSRafael J. Wysocki 61786e12eacSUlf Hansson ret = _genpd_power_on(genpd, true); 6189e08cf42SRafael J. Wysocki if (ret) 6199e08cf42SRafael J. Wysocki goto err; 6200140d8bdSRafael J. Wysocki 621ba2bbfbfSUlf Hansson genpd->status = GPD_STATE_ACTIVE; 622afece3abSThara Gopinath genpd_update_accounting(genpd); 623afece3abSThara Gopinath 6243f241775SRafael J. Wysocki return 0; 6259e08cf42SRafael J. Wysocki 6269e08cf42SRafael J. Wysocki err: 62729e47e21SUlf Hansson list_for_each_entry_continue_reverse(link, 62829e47e21SUlf Hansson &genpd->slave_links, 62929e47e21SUlf Hansson slave_node) { 6305063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 6312da83545SUlf Hansson genpd_lock_nested(link->master, depth + 1); 6322da83545SUlf Hansson genpd_power_off(link->master, false, depth + 1); 6332da83545SUlf Hansson genpd_unlock(link->master); 63429e47e21SUlf Hansson } 6359e08cf42SRafael J. Wysocki 6363f241775SRafael J. Wysocki return ret; 6373f241775SRafael J. Wysocki } 6383f241775SRafael J. Wysocki 6396ff7bb0dSRafael J. Wysocki static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, 6406ff7bb0dSRafael J. Wysocki unsigned long val, void *ptr) 6416ff7bb0dSRafael J. Wysocki { 6426ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 6436ff7bb0dSRafael J. Wysocki struct device *dev; 6446ff7bb0dSRafael J. Wysocki 6456ff7bb0dSRafael J. Wysocki gpd_data = container_of(nb, struct generic_pm_domain_data, nb); 6466ff7bb0dSRafael J. Wysocki dev = gpd_data->base.dev; 6476ff7bb0dSRafael J. Wysocki 6486ff7bb0dSRafael J. Wysocki for (;;) { 6496ff7bb0dSRafael J. Wysocki struct generic_pm_domain *genpd; 6506ff7bb0dSRafael J. Wysocki struct pm_domain_data *pdd; 6516ff7bb0dSRafael J. Wysocki 6526ff7bb0dSRafael J. Wysocki spin_lock_irq(&dev->power.lock); 6536ff7bb0dSRafael J. Wysocki 6546ff7bb0dSRafael J. Wysocki pdd = dev->power.subsys_data ? 6556ff7bb0dSRafael J. Wysocki dev->power.subsys_data->domain_data : NULL; 656b4883ca4SViresh Kumar if (pdd) { 6576ff7bb0dSRafael J. Wysocki to_gpd_data(pdd)->td.constraint_changed = true; 6586ff7bb0dSRafael J. Wysocki genpd = dev_to_genpd(dev); 6596ff7bb0dSRafael J. Wysocki } else { 6606ff7bb0dSRafael J. Wysocki genpd = ERR_PTR(-ENODATA); 6616ff7bb0dSRafael J. Wysocki } 6626ff7bb0dSRafael J. Wysocki 6636ff7bb0dSRafael J. Wysocki spin_unlock_irq(&dev->power.lock); 6646ff7bb0dSRafael J. Wysocki 6656ff7bb0dSRafael J. Wysocki if (!IS_ERR(genpd)) { 66635241d12SLina Iyer genpd_lock(genpd); 6676ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 66835241d12SLina Iyer genpd_unlock(genpd); 6696ff7bb0dSRafael J. Wysocki } 6706ff7bb0dSRafael J. Wysocki 6716ff7bb0dSRafael J. Wysocki dev = dev->parent; 6726ff7bb0dSRafael J. Wysocki if (!dev || dev->power.ignore_children) 6736ff7bb0dSRafael J. Wysocki break; 6746ff7bb0dSRafael J. Wysocki } 6756ff7bb0dSRafael J. Wysocki 6766ff7bb0dSRafael J. Wysocki return NOTIFY_DONE; 6776ff7bb0dSRafael J. Wysocki } 6786ff7bb0dSRafael J. Wysocki 6795248051bSRafael J. Wysocki /** 680f721889fSRafael J. Wysocki * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. 681f721889fSRafael J. Wysocki * @work: Work structure used for scheduling the execution of this function. 682f721889fSRafael J. Wysocki */ 683f721889fSRafael J. Wysocki static void genpd_power_off_work_fn(struct work_struct *work) 684f721889fSRafael J. Wysocki { 685f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 686f721889fSRafael J. Wysocki 687f721889fSRafael J. Wysocki genpd = container_of(work, struct generic_pm_domain, power_off_work); 688f721889fSRafael J. Wysocki 68935241d12SLina Iyer genpd_lock(genpd); 6902da83545SUlf Hansson genpd_power_off(genpd, false, 0); 69135241d12SLina Iyer genpd_unlock(genpd); 692f721889fSRafael J. Wysocki } 693f721889fSRafael J. Wysocki 694f721889fSRafael J. Wysocki /** 69554eeddbfSUlf Hansson * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks 69654eeddbfSUlf Hansson * @dev: Device to handle. 69754eeddbfSUlf Hansson */ 69854eeddbfSUlf Hansson static int __genpd_runtime_suspend(struct device *dev) 69954eeddbfSUlf Hansson { 70054eeddbfSUlf Hansson int (*cb)(struct device *__dev); 70154eeddbfSUlf Hansson 70254eeddbfSUlf Hansson if (dev->type && dev->type->pm) 70354eeddbfSUlf Hansson cb = dev->type->pm->runtime_suspend; 70454eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 70554eeddbfSUlf Hansson cb = dev->class->pm->runtime_suspend; 70654eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 70754eeddbfSUlf Hansson cb = dev->bus->pm->runtime_suspend; 70854eeddbfSUlf Hansson else 70954eeddbfSUlf Hansson cb = NULL; 71054eeddbfSUlf Hansson 71154eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 71254eeddbfSUlf Hansson cb = dev->driver->pm->runtime_suspend; 71354eeddbfSUlf Hansson 71454eeddbfSUlf Hansson return cb ? cb(dev) : 0; 71554eeddbfSUlf Hansson } 71654eeddbfSUlf Hansson 71754eeddbfSUlf Hansson /** 71854eeddbfSUlf Hansson * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks 71954eeddbfSUlf Hansson * @dev: Device to handle. 72054eeddbfSUlf Hansson */ 72154eeddbfSUlf Hansson static int __genpd_runtime_resume(struct device *dev) 72254eeddbfSUlf Hansson { 72354eeddbfSUlf Hansson int (*cb)(struct device *__dev); 72454eeddbfSUlf Hansson 72554eeddbfSUlf Hansson if (dev->type && dev->type->pm) 72654eeddbfSUlf Hansson cb = dev->type->pm->runtime_resume; 72754eeddbfSUlf Hansson else if (dev->class && dev->class->pm) 72854eeddbfSUlf Hansson cb = dev->class->pm->runtime_resume; 72954eeddbfSUlf Hansson else if (dev->bus && dev->bus->pm) 73054eeddbfSUlf Hansson cb = dev->bus->pm->runtime_resume; 73154eeddbfSUlf Hansson else 73254eeddbfSUlf Hansson cb = NULL; 73354eeddbfSUlf Hansson 73454eeddbfSUlf Hansson if (!cb && dev->driver && dev->driver->pm) 73554eeddbfSUlf Hansson cb = dev->driver->pm->runtime_resume; 73654eeddbfSUlf Hansson 73754eeddbfSUlf Hansson return cb ? cb(dev) : 0; 73854eeddbfSUlf Hansson } 73954eeddbfSUlf Hansson 74054eeddbfSUlf Hansson /** 741795bd2e7SUlf Hansson * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. 742f721889fSRafael J. Wysocki * @dev: Device to suspend. 743f721889fSRafael J. Wysocki * 744f721889fSRafael J. Wysocki * Carry out a runtime suspend of a device under the assumption that its 745f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 746f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 747f721889fSRafael J. Wysocki */ 748795bd2e7SUlf Hansson static int genpd_runtime_suspend(struct device *dev) 749f721889fSRafael J. Wysocki { 750f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 7519df3921eSUlf Hansson bool (*suspend_ok)(struct device *__dev); 7522b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 753ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 7542b1d88cdSUlf Hansson ktime_t time_start; 7552b1d88cdSUlf Hansson s64 elapsed_ns; 756d5e4cbfeSRafael J. Wysocki int ret; 757f721889fSRafael J. Wysocki 758f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 759f721889fSRafael J. Wysocki 7605248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 7615248051bSRafael J. Wysocki if (IS_ERR(genpd)) 762f721889fSRafael J. Wysocki return -EINVAL; 763f721889fSRafael J. Wysocki 764ffe12855SUlf Hansson /* 765ffe12855SUlf Hansson * A runtime PM centric subsystem/driver may re-use the runtime PM 766ffe12855SUlf Hansson * callbacks for other purposes than runtime PM. In those scenarios 767ffe12855SUlf Hansson * runtime PM is disabled. Under these circumstances, we shall skip 768ffe12855SUlf Hansson * validating/measuring the PM QoS latency. 769ffe12855SUlf Hansson */ 7709df3921eSUlf Hansson suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; 7719df3921eSUlf Hansson if (runtime_pm && suspend_ok && !suspend_ok(dev)) 772b02c999aSRafael J. Wysocki return -EBUSY; 773b02c999aSRafael J. Wysocki 7742b1d88cdSUlf Hansson /* Measure suspend latency. */ 775d33d5a6cSLinus Torvalds time_start = 0; 776ffe12855SUlf Hansson if (runtime_pm) 7772b1d88cdSUlf Hansson time_start = ktime_get(); 7782b1d88cdSUlf Hansson 77954eeddbfSUlf Hansson ret = __genpd_runtime_suspend(dev); 780f721889fSRafael J. Wysocki if (ret) 78117b75ecaSRafael J. Wysocki return ret; 78217b75ecaSRafael J. Wysocki 7832b1d88cdSUlf Hansson ret = genpd_stop_dev(genpd, dev); 784ba2bbfbfSUlf Hansson if (ret) { 78554eeddbfSUlf Hansson __genpd_runtime_resume(dev); 786ba2bbfbfSUlf Hansson return ret; 787ba2bbfbfSUlf Hansson } 788ba2bbfbfSUlf Hansson 7892b1d88cdSUlf Hansson /* Update suspend latency value if the measured time exceeds it. */ 790ffe12855SUlf Hansson if (runtime_pm) { 7912b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 7922b1d88cdSUlf Hansson if (elapsed_ns > td->suspend_latency_ns) { 7932b1d88cdSUlf Hansson td->suspend_latency_ns = elapsed_ns; 7942b1d88cdSUlf Hansson dev_dbg(dev, "suspend latency exceeded, %lld ns\n", 7952b1d88cdSUlf Hansson elapsed_ns); 7962b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 7972b1d88cdSUlf Hansson td->constraint_changed = true; 7982b1d88cdSUlf Hansson } 799ffe12855SUlf Hansson } 8002b1d88cdSUlf Hansson 8010aa2a221SRafael J. Wysocki /* 802d716f479SLina Iyer * If power.irq_safe is set, this routine may be run with 803d716f479SLina Iyer * IRQs disabled, so suspend only if the PM domain also is irq_safe. 8040aa2a221SRafael J. Wysocki */ 805d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) 8060aa2a221SRafael J. Wysocki return 0; 8070aa2a221SRafael J. Wysocki 80835241d12SLina Iyer genpd_lock(genpd); 8092da83545SUlf Hansson genpd_power_off(genpd, true, 0); 81035241d12SLina Iyer genpd_unlock(genpd); 811f721889fSRafael J. Wysocki 812f721889fSRafael J. Wysocki return 0; 813f721889fSRafael J. Wysocki } 814f721889fSRafael J. Wysocki 815f721889fSRafael J. Wysocki /** 816795bd2e7SUlf Hansson * genpd_runtime_resume - Resume a device belonging to I/O PM domain. 817f721889fSRafael J. Wysocki * @dev: Device to resume. 818f721889fSRafael J. Wysocki * 819f721889fSRafael J. Wysocki * Carry out a runtime resume of a device under the assumption that its 820f721889fSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 821f721889fSRafael J. Wysocki * struct generic_pm_domain representing a PM domain consisting of I/O devices. 822f721889fSRafael J. Wysocki */ 823795bd2e7SUlf Hansson static int genpd_runtime_resume(struct device *dev) 824f721889fSRafael J. Wysocki { 825f721889fSRafael J. Wysocki struct generic_pm_domain *genpd; 8262b1d88cdSUlf Hansson struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 827ffe12855SUlf Hansson bool runtime_pm = pm_runtime_enabled(dev); 8282b1d88cdSUlf Hansson ktime_t time_start; 8292b1d88cdSUlf Hansson s64 elapsed_ns; 830f721889fSRafael J. Wysocki int ret; 831ba2bbfbfSUlf Hansson bool timed = true; 832f721889fSRafael J. Wysocki 833f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 834f721889fSRafael J. Wysocki 8355248051bSRafael J. Wysocki genpd = dev_to_genpd(dev); 8365248051bSRafael J. Wysocki if (IS_ERR(genpd)) 837f721889fSRafael J. Wysocki return -EINVAL; 838f721889fSRafael J. Wysocki 839d716f479SLina Iyer /* 840d716f479SLina Iyer * As we don't power off a non IRQ safe domain, which holds 841d716f479SLina Iyer * an IRQ safe device, we don't need to restore power to it. 842d716f479SLina Iyer */ 843d716f479SLina Iyer if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) { 844ba2bbfbfSUlf Hansson timed = false; 845ba2bbfbfSUlf Hansson goto out; 846ba2bbfbfSUlf Hansson } 8470aa2a221SRafael J. Wysocki 84835241d12SLina Iyer genpd_lock(genpd); 84986e12eacSUlf Hansson ret = genpd_power_on(genpd, 0); 85035241d12SLina Iyer genpd_unlock(genpd); 851ba2bbfbfSUlf Hansson 852ba2bbfbfSUlf Hansson if (ret) 8533f241775SRafael J. Wysocki return ret; 854c6d22b37SRafael J. Wysocki 855ba2bbfbfSUlf Hansson out: 8562b1d88cdSUlf Hansson /* Measure resume latency. */ 857ab51e6baSAugusto Mecking Caringi time_start = 0; 858ffe12855SUlf Hansson if (timed && runtime_pm) 8592b1d88cdSUlf Hansson time_start = ktime_get(); 8602b1d88cdSUlf Hansson 861076395caSLaurent Pinchart ret = genpd_start_dev(genpd, dev); 862076395caSLaurent Pinchart if (ret) 863076395caSLaurent Pinchart goto err_poweroff; 864076395caSLaurent Pinchart 86554eeddbfSUlf Hansson ret = __genpd_runtime_resume(dev); 866076395caSLaurent Pinchart if (ret) 867076395caSLaurent Pinchart goto err_stop; 8682b1d88cdSUlf Hansson 8692b1d88cdSUlf Hansson /* Update resume latency value if the measured time exceeds it. */ 870ffe12855SUlf Hansson if (timed && runtime_pm) { 8712b1d88cdSUlf Hansson elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); 8722b1d88cdSUlf Hansson if (elapsed_ns > td->resume_latency_ns) { 8732b1d88cdSUlf Hansson td->resume_latency_ns = elapsed_ns; 8742b1d88cdSUlf Hansson dev_dbg(dev, "resume latency exceeded, %lld ns\n", 8752b1d88cdSUlf Hansson elapsed_ns); 8762b1d88cdSUlf Hansson genpd->max_off_time_changed = true; 8772b1d88cdSUlf Hansson td->constraint_changed = true; 8782b1d88cdSUlf Hansson } 8792b1d88cdSUlf Hansson } 88017b75ecaSRafael J. Wysocki 881f721889fSRafael J. Wysocki return 0; 882076395caSLaurent Pinchart 883076395caSLaurent Pinchart err_stop: 884076395caSLaurent Pinchart genpd_stop_dev(genpd, dev); 885076395caSLaurent Pinchart err_poweroff: 886d716f479SLina Iyer if (!pm_runtime_is_irq_safe(dev) || 887d716f479SLina Iyer (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { 88835241d12SLina Iyer genpd_lock(genpd); 8892da83545SUlf Hansson genpd_power_off(genpd, true, 0); 89035241d12SLina Iyer genpd_unlock(genpd); 891076395caSLaurent Pinchart } 892076395caSLaurent Pinchart 893076395caSLaurent Pinchart return ret; 894f721889fSRafael J. Wysocki } 895f721889fSRafael J. Wysocki 89639ac5ba5STushar Behera static bool pd_ignore_unused; 89739ac5ba5STushar Behera static int __init pd_ignore_unused_setup(char *__unused) 89839ac5ba5STushar Behera { 89939ac5ba5STushar Behera pd_ignore_unused = true; 90039ac5ba5STushar Behera return 1; 90139ac5ba5STushar Behera } 90239ac5ba5STushar Behera __setup("pd_ignore_unused", pd_ignore_unused_setup); 90339ac5ba5STushar Behera 90417f2ae7fSRafael J. Wysocki /** 90586e12eacSUlf Hansson * genpd_power_off_unused - Power off all PM domains with no devices in use. 90617f2ae7fSRafael J. Wysocki */ 90786e12eacSUlf Hansson static int __init genpd_power_off_unused(void) 90817f2ae7fSRafael J. Wysocki { 90917f2ae7fSRafael J. Wysocki struct generic_pm_domain *genpd; 91017f2ae7fSRafael J. Wysocki 91139ac5ba5STushar Behera if (pd_ignore_unused) { 91239ac5ba5STushar Behera pr_warn("genpd: Not disabling unused power domains\n"); 913bb4b72fcSUlf Hansson return 0; 91439ac5ba5STushar Behera } 91539ac5ba5STushar Behera 91617f2ae7fSRafael J. Wysocki mutex_lock(&gpd_list_lock); 91717f2ae7fSRafael J. Wysocki 91817f2ae7fSRafael J. Wysocki list_for_each_entry(genpd, &gpd_list, gpd_list_node) 91917f2ae7fSRafael J. Wysocki genpd_queue_power_off_work(genpd); 92017f2ae7fSRafael J. Wysocki 92117f2ae7fSRafael J. Wysocki mutex_unlock(&gpd_list_lock); 92217f2ae7fSRafael J. Wysocki 9232fe71dcdSUlf Hansson return 0; 9242fe71dcdSUlf Hansson } 92586e12eacSUlf Hansson late_initcall(genpd_power_off_unused); 9262fe71dcdSUlf Hansson 9270159ec67SJon Hunter #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) 928596ba34bSRafael J. Wysocki 9299e9704eaSUlf Hansson static bool genpd_present(const struct generic_pm_domain *genpd) 93077f827deSRafael J. Wysocki { 931895b31f3SGeert Uytterhoeven const struct generic_pm_domain *gpd; 93277f827deSRafael J. Wysocki 93377f827deSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 93477f827deSRafael J. Wysocki return false; 93577f827deSRafael J. Wysocki 93677f827deSRafael J. Wysocki list_for_each_entry(gpd, &gpd_list, gpd_list_node) 93777f827deSRafael J. Wysocki if (gpd == genpd) 93877f827deSRafael J. Wysocki return true; 93977f827deSRafael J. Wysocki 94077f827deSRafael J. Wysocki return false; 94177f827deSRafael J. Wysocki } 94277f827deSRafael J. Wysocki 9430159ec67SJon Hunter #endif 9440159ec67SJon Hunter 9450159ec67SJon Hunter #ifdef CONFIG_PM_SLEEP 9460159ec67SJon Hunter 947596ba34bSRafael J. Wysocki /** 94886e12eacSUlf Hansson * genpd_sync_power_off - Synchronously power off a PM domain and its masters. 949596ba34bSRafael J. Wysocki * @genpd: PM domain to power off, if possible. 9500883ac03SUlf Hansson * @use_lock: use the lock. 9510883ac03SUlf Hansson * @depth: nesting count for lockdep. 952596ba34bSRafael J. Wysocki * 953596ba34bSRafael J. Wysocki * Check if the given PM domain can be powered off (during system suspend or 9545063ce15SRafael J. Wysocki * hibernation) and do that if so. Also, in that case propagate to its masters. 955596ba34bSRafael J. Wysocki * 95677f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 9570883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 9580883ac03SUlf Hansson * these cases the lock must be held. 959596ba34bSRafael J. Wysocki */ 9600883ac03SUlf Hansson static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, 9610883ac03SUlf Hansson unsigned int depth) 962596ba34bSRafael J. Wysocki { 9635063ce15SRafael J. Wysocki struct gpd_link *link; 964596ba34bSRafael J. Wysocki 965ffaa42e8SUlf Hansson if (!genpd_status_on(genpd) || genpd_is_always_on(genpd)) 966596ba34bSRafael J. Wysocki return; 967596ba34bSRafael J. Wysocki 968c4bb3160SRafael J. Wysocki if (genpd->suspended_count != genpd->device_count 969c4bb3160SRafael J. Wysocki || atomic_read(&genpd->sd_count) > 0) 970596ba34bSRafael J. Wysocki return; 971596ba34bSRafael J. Wysocki 972fc5cbf0cSAxel Haslam /* Choose the deepest state when suspending */ 973fc5cbf0cSAxel Haslam genpd->state_idx = genpd->state_count - 1; 9741c14967cSUlf Hansson if (_genpd_power_off(genpd, false)) 9751c14967cSUlf Hansson return; 976596ba34bSRafael J. Wysocki 97717b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 9785063ce15SRafael J. Wysocki 9795063ce15SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 9805063ce15SRafael J. Wysocki genpd_sd_counter_dec(link->master); 9810883ac03SUlf Hansson 9820883ac03SUlf Hansson if (use_lock) 9830883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 9840883ac03SUlf Hansson 9850883ac03SUlf Hansson genpd_sync_power_off(link->master, use_lock, depth + 1); 9860883ac03SUlf Hansson 9870883ac03SUlf Hansson if (use_lock) 9880883ac03SUlf Hansson genpd_unlock(link->master); 989596ba34bSRafael J. Wysocki } 990596ba34bSRafael J. Wysocki } 991596ba34bSRafael J. Wysocki 992596ba34bSRafael J. Wysocki /** 99386e12eacSUlf Hansson * genpd_sync_power_on - Synchronously power on a PM domain and its masters. 994802d8b49SRafael J. Wysocki * @genpd: PM domain to power on. 9950883ac03SUlf Hansson * @use_lock: use the lock. 9960883ac03SUlf Hansson * @depth: nesting count for lockdep. 997802d8b49SRafael J. Wysocki * 99877f827deSRafael J. Wysocki * This function is only called in "noirq" and "syscore" stages of system power 9990883ac03SUlf Hansson * transitions. The "noirq" callbacks may be executed asynchronously, thus in 10000883ac03SUlf Hansson * these cases the lock must be held. 1001802d8b49SRafael J. Wysocki */ 10020883ac03SUlf Hansson static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, 10030883ac03SUlf Hansson unsigned int depth) 1004802d8b49SRafael J. Wysocki { 1005802d8b49SRafael J. Wysocki struct gpd_link *link; 1006802d8b49SRafael J. Wysocki 100741e2c8e0SUlf Hansson if (genpd_status_on(genpd)) 1008802d8b49SRafael J. Wysocki return; 1009802d8b49SRafael J. Wysocki 1010802d8b49SRafael J. Wysocki list_for_each_entry(link, &genpd->slave_links, slave_node) { 1011802d8b49SRafael J. Wysocki genpd_sd_counter_inc(link->master); 10120883ac03SUlf Hansson 10130883ac03SUlf Hansson if (use_lock) 10140883ac03SUlf Hansson genpd_lock_nested(link->master, depth + 1); 10150883ac03SUlf Hansson 10160883ac03SUlf Hansson genpd_sync_power_on(link->master, use_lock, depth + 1); 10170883ac03SUlf Hansson 10180883ac03SUlf Hansson if (use_lock) 10190883ac03SUlf Hansson genpd_unlock(link->master); 1020802d8b49SRafael J. Wysocki } 1021802d8b49SRafael J. Wysocki 102286e12eacSUlf Hansson _genpd_power_on(genpd, false); 1023802d8b49SRafael J. Wysocki 1024802d8b49SRafael J. Wysocki genpd->status = GPD_STATE_ACTIVE; 1025802d8b49SRafael J. Wysocki } 1026802d8b49SRafael J. Wysocki 1027802d8b49SRafael J. Wysocki /** 10284ecd6e65SRafael J. Wysocki * resume_needed - Check whether to resume a device before system suspend. 10294ecd6e65SRafael J. Wysocki * @dev: Device to check. 10304ecd6e65SRafael J. Wysocki * @genpd: PM domain the device belongs to. 10314ecd6e65SRafael J. Wysocki * 10324ecd6e65SRafael J. Wysocki * There are two cases in which a device that can wake up the system from sleep 10339e9704eaSUlf Hansson * states should be resumed by genpd_prepare(): (1) if the device is enabled 10344ecd6e65SRafael J. Wysocki * to wake up the system and it has to remain active for this purpose while the 10354ecd6e65SRafael J. Wysocki * system is in the sleep state and (2) if the device is not enabled to wake up 10364ecd6e65SRafael J. Wysocki * the system from sleep states and it generally doesn't generate wakeup signals 10374ecd6e65SRafael J. Wysocki * by itself (those signals are generated on its behalf by other parts of the 10384ecd6e65SRafael J. Wysocki * system). In the latter case it may be necessary to reconfigure the device's 10394ecd6e65SRafael J. Wysocki * wakeup settings during system suspend, because it may have been set up to 10404ecd6e65SRafael J. Wysocki * signal remote wakeup from the system's working state as needed by runtime PM. 10414ecd6e65SRafael J. Wysocki * Return 'true' in either of the above cases. 10424ecd6e65SRafael J. Wysocki */ 1043d8600c8bSKrzysztof Kozlowski static bool resume_needed(struct device *dev, 1044d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd) 10454ecd6e65SRafael J. Wysocki { 10464ecd6e65SRafael J. Wysocki bool active_wakeup; 10474ecd6e65SRafael J. Wysocki 10484ecd6e65SRafael J. Wysocki if (!device_can_wakeup(dev)) 10494ecd6e65SRafael J. Wysocki return false; 10504ecd6e65SRafael J. Wysocki 1051d0af45f1SGeert Uytterhoeven active_wakeup = genpd_is_active_wakeup(genpd); 10524ecd6e65SRafael J. Wysocki return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; 10534ecd6e65SRafael J. Wysocki } 10544ecd6e65SRafael J. Wysocki 10554ecd6e65SRafael J. Wysocki /** 10569e9704eaSUlf Hansson * genpd_prepare - Start power transition of a device in a PM domain. 1057596ba34bSRafael J. Wysocki * @dev: Device to start the transition of. 1058596ba34bSRafael J. Wysocki * 1059596ba34bSRafael J. Wysocki * Start a power transition of a device (during a system-wide power transition) 1060596ba34bSRafael J. Wysocki * under the assumption that its pm_domain field points to the domain member of 1061596ba34bSRafael J. Wysocki * an object of type struct generic_pm_domain representing a PM domain 1062596ba34bSRafael J. Wysocki * consisting of I/O devices. 1063596ba34bSRafael J. Wysocki */ 10649e9704eaSUlf Hansson static int genpd_prepare(struct device *dev) 1065596ba34bSRafael J. Wysocki { 1066596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1067b6c10c84SRafael J. Wysocki int ret; 1068596ba34bSRafael J. Wysocki 1069596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1070596ba34bSRafael J. Wysocki 1071596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1072596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1073596ba34bSRafael J. Wysocki return -EINVAL; 1074596ba34bSRafael J. Wysocki 107517b75ecaSRafael J. Wysocki /* 107617b75ecaSRafael J. Wysocki * If a wakeup request is pending for the device, it should be woken up 107717b75ecaSRafael J. Wysocki * at this point and a system wakeup event should be reported if it's 107817b75ecaSRafael J. Wysocki * set up to wake up the system from sleep states. 107917b75ecaSRafael J. Wysocki */ 10804ecd6e65SRafael J. Wysocki if (resume_needed(dev, genpd)) 10814ecd6e65SRafael J. Wysocki pm_runtime_resume(dev); 10824ecd6e65SRafael J. Wysocki 108335241d12SLina Iyer genpd_lock(genpd); 1084596ba34bSRafael J. Wysocki 108539dd0f23SUlf Hansson if (genpd->prepared_count++ == 0) 108665533bbfSRafael J. Wysocki genpd->suspended_count = 0; 108717b75ecaSRafael J. Wysocki 108835241d12SLina Iyer genpd_unlock(genpd); 1089596ba34bSRafael J. Wysocki 1090b6c10c84SRafael J. Wysocki ret = pm_generic_prepare(dev); 10915241ab40SUlf Hansson if (ret < 0) { 109235241d12SLina Iyer genpd_lock(genpd); 1093b6c10c84SRafael J. Wysocki 109439dd0f23SUlf Hansson genpd->prepared_count--; 1095b6c10c84SRafael J. Wysocki 109635241d12SLina Iyer genpd_unlock(genpd); 1097b6c10c84SRafael J. Wysocki } 109817b75ecaSRafael J. Wysocki 10995241ab40SUlf Hansson /* Never return 1, as genpd don't cope with the direct_complete path. */ 11005241ab40SUlf Hansson return ret >= 0 ? 0 : ret; 1101596ba34bSRafael J. Wysocki } 1102596ba34bSRafael J. Wysocki 1103596ba34bSRafael J. Wysocki /** 110410da6542SMikko Perttunen * genpd_finish_suspend - Completion of suspend or hibernation of device in an 110510da6542SMikko Perttunen * I/O pm domain. 11060496c8aeSRafael J. Wysocki * @dev: Device to suspend. 110710da6542SMikko Perttunen * @poweroff: Specifies if this is a poweroff_noirq or suspend_noirq callback. 11080496c8aeSRafael J. Wysocki * 11090496c8aeSRafael J. Wysocki * Stop the device and remove power from the domain if all devices in it have 11100496c8aeSRafael J. Wysocki * been stopped. 11110496c8aeSRafael J. Wysocki */ 111210da6542SMikko Perttunen static int genpd_finish_suspend(struct device *dev, bool poweroff) 11130496c8aeSRafael J. Wysocki { 11140496c8aeSRafael J. Wysocki struct generic_pm_domain *genpd; 1115a935424bSUlf Hansson int ret = 0; 1116596ba34bSRafael J. Wysocki 11170496c8aeSRafael J. Wysocki genpd = dev_to_genpd(dev); 11180496c8aeSRafael J. Wysocki if (IS_ERR(genpd)) 11190496c8aeSRafael J. Wysocki return -EINVAL; 11200496c8aeSRafael J. Wysocki 112110da6542SMikko Perttunen if (poweroff) 112210da6542SMikko Perttunen ret = pm_generic_poweroff_noirq(dev); 112310da6542SMikko Perttunen else 112410da6542SMikko Perttunen ret = pm_generic_suspend_noirq(dev); 112510da6542SMikko Perttunen if (ret) 112610da6542SMikko Perttunen return ret; 112710da6542SMikko Perttunen 1128a935424bSUlf Hansson if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) 1129a935424bSUlf Hansson return 0; 1130a935424bSUlf Hansson 113117218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 113217218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 113317218e00SRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 1134a935424bSUlf Hansson if (ret) { 1135a935424bSUlf Hansson if (poweroff) 1136a935424bSUlf Hansson pm_generic_restore_noirq(dev); 1137a935424bSUlf Hansson else 1138a935424bSUlf Hansson pm_generic_resume_noirq(dev); 1139122a2237SUlf Hansson return ret; 1140122a2237SUlf Hansson } 1141a935424bSUlf Hansson } 1142122a2237SUlf Hansson 11430883ac03SUlf Hansson genpd_lock(genpd); 1144596ba34bSRafael J. Wysocki genpd->suspended_count++; 11450883ac03SUlf Hansson genpd_sync_power_off(genpd, true, 0); 11460883ac03SUlf Hansson genpd_unlock(genpd); 1147596ba34bSRafael J. Wysocki 1148596ba34bSRafael J. Wysocki return 0; 1149596ba34bSRafael J. Wysocki } 1150596ba34bSRafael J. Wysocki 1151596ba34bSRafael J. Wysocki /** 11529e9704eaSUlf Hansson * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. 115310da6542SMikko Perttunen * @dev: Device to suspend. 115410da6542SMikko Perttunen * 115510da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 115610da6542SMikko Perttunen * been stopped. 115710da6542SMikko Perttunen */ 11589e9704eaSUlf Hansson static int genpd_suspend_noirq(struct device *dev) 115910da6542SMikko Perttunen { 116010da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 116110da6542SMikko Perttunen 116210da6542SMikko Perttunen return genpd_finish_suspend(dev, false); 116310da6542SMikko Perttunen } 116410da6542SMikko Perttunen 116510da6542SMikko Perttunen /** 11669e9704eaSUlf Hansson * genpd_resume_noirq - Start of resume of device in an I/O PM domain. 1167596ba34bSRafael J. Wysocki * @dev: Device to resume. 1168596ba34bSRafael J. Wysocki * 11690496c8aeSRafael J. Wysocki * Restore power to the device's PM domain, if necessary, and start the device. 1170596ba34bSRafael J. Wysocki */ 11719e9704eaSUlf Hansson static int genpd_resume_noirq(struct device *dev) 1172596ba34bSRafael J. Wysocki { 1173596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1174a935424bSUlf Hansson int ret; 1175596ba34bSRafael J. Wysocki 1176596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1177596ba34bSRafael J. Wysocki 1178596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1179596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1180596ba34bSRafael J. Wysocki return -EINVAL; 1181596ba34bSRafael J. Wysocki 1182d0af45f1SGeert Uytterhoeven if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) 1183a935424bSUlf Hansson return pm_generic_resume_noirq(dev); 1184596ba34bSRafael J. Wysocki 11850883ac03SUlf Hansson genpd_lock(genpd); 11860883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 1187596ba34bSRafael J. Wysocki genpd->suspended_count--; 11880883ac03SUlf Hansson genpd_unlock(genpd); 1189596ba34bSRafael J. Wysocki 119017218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 119117218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 119217218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 119310da6542SMikko Perttunen if (ret) 119410da6542SMikko Perttunen return ret; 1195a935424bSUlf Hansson } 119610da6542SMikko Perttunen 1197a935424bSUlf Hansson return pm_generic_resume_noirq(dev); 1198596ba34bSRafael J. Wysocki } 1199596ba34bSRafael J. Wysocki 1200596ba34bSRafael J. Wysocki /** 12019e9704eaSUlf Hansson * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. 1202596ba34bSRafael J. Wysocki * @dev: Device to freeze. 1203596ba34bSRafael J. Wysocki * 1204596ba34bSRafael J. Wysocki * Carry out a late freeze of a device under the assumption that its 1205596ba34bSRafael J. Wysocki * pm_domain field points to the domain member of an object of type 1206596ba34bSRafael J. Wysocki * struct generic_pm_domain representing a power domain consisting of I/O 1207596ba34bSRafael J. Wysocki * devices. 1208596ba34bSRafael J. Wysocki */ 12099e9704eaSUlf Hansson static int genpd_freeze_noirq(struct device *dev) 1210596ba34bSRafael J. Wysocki { 1211d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1212122a2237SUlf Hansson int ret = 0; 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 122010da6542SMikko Perttunen ret = pm_generic_freeze_noirq(dev); 122110da6542SMikko Perttunen if (ret) 122210da6542SMikko Perttunen return ret; 122310da6542SMikko Perttunen 122417218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 122517218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) 122617218e00SRafael J. Wysocki ret = genpd_stop_dev(genpd, dev); 1227122a2237SUlf Hansson 1228122a2237SUlf Hansson return ret; 1229596ba34bSRafael J. Wysocki } 1230596ba34bSRafael J. Wysocki 1231596ba34bSRafael J. Wysocki /** 12329e9704eaSUlf Hansson * genpd_thaw_noirq - Early thaw of device in an I/O PM domain. 1233596ba34bSRafael J. Wysocki * @dev: Device to thaw. 1234596ba34bSRafael J. Wysocki * 12350496c8aeSRafael J. Wysocki * Start the device, unless power has been removed from the domain already 12360496c8aeSRafael J. Wysocki * before the system transition. 1237596ba34bSRafael J. Wysocki */ 12389e9704eaSUlf Hansson static int genpd_thaw_noirq(struct device *dev) 1239596ba34bSRafael J. Wysocki { 1240d8600c8bSKrzysztof Kozlowski const struct generic_pm_domain *genpd; 1241122a2237SUlf Hansson int ret = 0; 1242596ba34bSRafael J. Wysocki 1243596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1244596ba34bSRafael J. Wysocki 1245596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1246596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1247596ba34bSRafael J. Wysocki return -EINVAL; 1248596ba34bSRafael J. Wysocki 124917218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 125017218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 125117218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 125210da6542SMikko Perttunen if (ret) 1253122a2237SUlf Hansson return ret; 12540496c8aeSRafael J. Wysocki } 1255596ba34bSRafael J. Wysocki 125610da6542SMikko Perttunen return pm_generic_thaw_noirq(dev); 125710da6542SMikko Perttunen } 125810da6542SMikko Perttunen 125910da6542SMikko Perttunen /** 12609e9704eaSUlf Hansson * genpd_poweroff_noirq - Completion of hibernation of device in an 126110da6542SMikko Perttunen * I/O PM domain. 126210da6542SMikko Perttunen * @dev: Device to poweroff. 126310da6542SMikko Perttunen * 126410da6542SMikko Perttunen * Stop the device and remove power from the domain if all devices in it have 126510da6542SMikko Perttunen * been stopped. 126610da6542SMikko Perttunen */ 12679e9704eaSUlf Hansson static int genpd_poweroff_noirq(struct device *dev) 126810da6542SMikko Perttunen { 126910da6542SMikko Perttunen dev_dbg(dev, "%s()\n", __func__); 127010da6542SMikko Perttunen 127110da6542SMikko Perttunen return genpd_finish_suspend(dev, true); 127210da6542SMikko Perttunen } 127310da6542SMikko Perttunen 12740496c8aeSRafael J. Wysocki /** 12759e9704eaSUlf Hansson * genpd_restore_noirq - Start of restore of device in an I/O PM domain. 1276596ba34bSRafael J. Wysocki * @dev: Device to resume. 1277596ba34bSRafael J. Wysocki * 12780496c8aeSRafael J. Wysocki * Make sure the domain will be in the same power state as before the 12790496c8aeSRafael J. Wysocki * hibernation the system is resuming from and start the device if necessary. 1280596ba34bSRafael J. Wysocki */ 12819e9704eaSUlf Hansson static int genpd_restore_noirq(struct device *dev) 1282596ba34bSRafael J. Wysocki { 1283596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1284122a2237SUlf Hansson int ret = 0; 1285596ba34bSRafael J. Wysocki 1286596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1287596ba34bSRafael J. Wysocki 1288596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1289596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1290596ba34bSRafael J. Wysocki return -EINVAL; 1291596ba34bSRafael J. Wysocki 1292596ba34bSRafael J. Wysocki /* 129365533bbfSRafael J. Wysocki * At this point suspended_count == 0 means we are being run for the 129465533bbfSRafael J. Wysocki * first time for the given domain in the present cycle. 129565533bbfSRafael J. Wysocki */ 12960883ac03SUlf Hansson genpd_lock(genpd); 129739dd0f23SUlf Hansson if (genpd->suspended_count++ == 0) 129865533bbfSRafael J. Wysocki /* 129965533bbfSRafael J. Wysocki * The boot kernel might put the domain into arbitrary state, 130086e12eacSUlf Hansson * so make it appear as powered off to genpd_sync_power_on(), 1301802d8b49SRafael J. Wysocki * so that it tries to power it on in case it was really off. 1302596ba34bSRafael J. Wysocki */ 130317b75ecaSRafael J. Wysocki genpd->status = GPD_STATE_POWER_OFF; 130418dd2eceSRafael J. Wysocki 13050883ac03SUlf Hansson genpd_sync_power_on(genpd, true, 0); 13060883ac03SUlf Hansson genpd_unlock(genpd); 1307596ba34bSRafael J. Wysocki 130817218e00SRafael J. Wysocki if (genpd->dev_ops.stop && genpd->dev_ops.start && 130917218e00SRafael J. Wysocki !pm_runtime_status_suspended(dev)) { 131017218e00SRafael J. Wysocki ret = genpd_start_dev(genpd, dev); 131110da6542SMikko Perttunen if (ret) 1312122a2237SUlf Hansson return ret; 1313596ba34bSRafael J. Wysocki } 1314596ba34bSRafael J. Wysocki 131510da6542SMikko Perttunen return pm_generic_restore_noirq(dev); 131610da6542SMikko Perttunen } 131710da6542SMikko Perttunen 1318596ba34bSRafael J. Wysocki /** 13199e9704eaSUlf Hansson * genpd_complete - Complete power transition of a device in a power domain. 1320596ba34bSRafael J. Wysocki * @dev: Device to complete the transition of. 1321596ba34bSRafael J. Wysocki * 1322596ba34bSRafael J. Wysocki * Complete a power transition of a device (during a system-wide power 1323596ba34bSRafael J. Wysocki * transition) under the assumption that its pm_domain field points to the 1324596ba34bSRafael J. Wysocki * domain member of an object of type struct generic_pm_domain representing 1325596ba34bSRafael J. Wysocki * a power domain consisting of I/O devices. 1326596ba34bSRafael J. Wysocki */ 13279e9704eaSUlf Hansson static void genpd_complete(struct device *dev) 1328596ba34bSRafael J. Wysocki { 1329596ba34bSRafael J. Wysocki struct generic_pm_domain *genpd; 1330596ba34bSRafael J. Wysocki 1331596ba34bSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1332596ba34bSRafael J. Wysocki 1333596ba34bSRafael J. Wysocki genpd = dev_to_genpd(dev); 1334596ba34bSRafael J. Wysocki if (IS_ERR(genpd)) 1335596ba34bSRafael J. Wysocki return; 1336596ba34bSRafael J. Wysocki 13374d23a5e8SUlf Hansson pm_generic_complete(dev); 13384d23a5e8SUlf Hansson 133935241d12SLina Iyer genpd_lock(genpd); 1340596ba34bSRafael J. Wysocki 134139dd0f23SUlf Hansson genpd->prepared_count--; 13424d23a5e8SUlf Hansson if (!genpd->prepared_count) 13434d23a5e8SUlf Hansson genpd_queue_power_off_work(genpd); 1344596ba34bSRafael J. Wysocki 134535241d12SLina Iyer genpd_unlock(genpd); 1346596ba34bSRafael J. Wysocki } 1347596ba34bSRafael J. Wysocki 134877f827deSRafael J. Wysocki /** 1349d47e6464SUlf Hansson * genpd_syscore_switch - Switch power during system core suspend or resume. 135077f827deSRafael J. Wysocki * @dev: Device that normally is marked as "always on" to switch power for. 135177f827deSRafael J. Wysocki * 135277f827deSRafael J. Wysocki * This routine may only be called during the system core (syscore) suspend or 135377f827deSRafael J. Wysocki * resume phase for devices whose "always on" flags are set. 135477f827deSRafael J. Wysocki */ 1355d47e6464SUlf Hansson static void genpd_syscore_switch(struct device *dev, bool suspend) 135677f827deSRafael J. Wysocki { 135777f827deSRafael J. Wysocki struct generic_pm_domain *genpd; 135877f827deSRafael J. Wysocki 135923c6d2c7SRafael J. Wysocki genpd = dev_to_genpd(dev); 13609e9704eaSUlf Hansson if (!genpd_present(genpd)) 136177f827deSRafael J. Wysocki return; 136277f827deSRafael J. Wysocki 136377f827deSRafael J. Wysocki if (suspend) { 136477f827deSRafael J. Wysocki genpd->suspended_count++; 13650883ac03SUlf Hansson genpd_sync_power_off(genpd, false, 0); 136677f827deSRafael J. Wysocki } else { 13670883ac03SUlf Hansson genpd_sync_power_on(genpd, false, 0); 136877f827deSRafael J. Wysocki genpd->suspended_count--; 136977f827deSRafael J. Wysocki } 137077f827deSRafael J. Wysocki } 1371d47e6464SUlf Hansson 1372d47e6464SUlf Hansson void pm_genpd_syscore_poweroff(struct device *dev) 1373d47e6464SUlf Hansson { 1374d47e6464SUlf Hansson genpd_syscore_switch(dev, true); 1375d47e6464SUlf Hansson } 1376d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweroff); 1377d47e6464SUlf Hansson 1378d47e6464SUlf Hansson void pm_genpd_syscore_poweron(struct device *dev) 1379d47e6464SUlf Hansson { 1380d47e6464SUlf Hansson genpd_syscore_switch(dev, false); 1381d47e6464SUlf Hansson } 1382d47e6464SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); 138377f827deSRafael J. Wysocki 1384d30d819dSRafael J. Wysocki #else /* !CONFIG_PM_SLEEP */ 1385596ba34bSRafael J. Wysocki 13869e9704eaSUlf Hansson #define genpd_prepare NULL 13879e9704eaSUlf Hansson #define genpd_suspend_noirq NULL 13889e9704eaSUlf Hansson #define genpd_resume_noirq NULL 13899e9704eaSUlf Hansson #define genpd_freeze_noirq NULL 13909e9704eaSUlf Hansson #define genpd_thaw_noirq NULL 13919e9704eaSUlf Hansson #define genpd_poweroff_noirq NULL 13929e9704eaSUlf Hansson #define genpd_restore_noirq NULL 13939e9704eaSUlf Hansson #define genpd_complete NULL 1394596ba34bSRafael J. Wysocki 1395596ba34bSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 1396596ba34bSRafael J. Wysocki 1397f104e1e5SUlf Hansson static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, 1398f104e1e5SUlf Hansson struct gpd_timing_data *td) 13991d5fcfecSRafael J. Wysocki { 14001d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 14013e235685SUlf Hansson int ret; 14023e235685SUlf Hansson 14033e235685SUlf Hansson ret = dev_pm_get_subsys_data(dev); 14043e235685SUlf Hansson if (ret) 14053e235685SUlf Hansson return ERR_PTR(ret); 14061d5fcfecSRafael J. Wysocki 14071d5fcfecSRafael J. Wysocki gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 14083e235685SUlf Hansson if (!gpd_data) { 14093e235685SUlf Hansson ret = -ENOMEM; 14103e235685SUlf Hansson goto err_put; 14113e235685SUlf Hansson } 14121d5fcfecSRafael J. Wysocki 1413f104e1e5SUlf Hansson if (td) 1414f104e1e5SUlf Hansson gpd_data->td = *td; 1415f104e1e5SUlf Hansson 1416f104e1e5SUlf Hansson gpd_data->base.dev = dev; 1417f104e1e5SUlf Hansson gpd_data->td.constraint_changed = true; 14180759e80bSRafael J. Wysocki gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; 1419f104e1e5SUlf Hansson gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; 1420f104e1e5SUlf Hansson 1421f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1422f104e1e5SUlf Hansson 1423f104e1e5SUlf Hansson if (dev->power.subsys_data->domain_data) { 1424f104e1e5SUlf Hansson ret = -EINVAL; 1425f104e1e5SUlf Hansson goto err_free; 1426f104e1e5SUlf Hansson } 1427f104e1e5SUlf Hansson 1428f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = &gpd_data->base; 1429f104e1e5SUlf Hansson 1430f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1431f104e1e5SUlf Hansson 14321d5fcfecSRafael J. Wysocki return gpd_data; 14333e235685SUlf Hansson 1434f104e1e5SUlf Hansson err_free: 1435f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1436f104e1e5SUlf Hansson kfree(gpd_data); 14373e235685SUlf Hansson err_put: 14383e235685SUlf Hansson dev_pm_put_subsys_data(dev); 14393e235685SUlf Hansson return ERR_PTR(ret); 14401d5fcfecSRafael J. Wysocki } 14411d5fcfecSRafael J. Wysocki 144249d400c7SUlf Hansson static void genpd_free_dev_data(struct device *dev, 14431d5fcfecSRafael J. Wysocki struct generic_pm_domain_data *gpd_data) 14441d5fcfecSRafael J. Wysocki { 1445f104e1e5SUlf Hansson spin_lock_irq(&dev->power.lock); 1446f104e1e5SUlf Hansson 1447f104e1e5SUlf Hansson dev->power.subsys_data->domain_data = NULL; 1448f104e1e5SUlf Hansson 1449f104e1e5SUlf Hansson spin_unlock_irq(&dev->power.lock); 1450f104e1e5SUlf Hansson 14511d5fcfecSRafael J. Wysocki kfree(gpd_data); 14523e235685SUlf Hansson dev_pm_put_subsys_data(dev); 14531d5fcfecSRafael J. Wysocki } 14541d5fcfecSRafael J. Wysocki 145519efa5ffSJon Hunter static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1456b02c999aSRafael J. Wysocki struct gpd_timing_data *td) 1457f721889fSRafael J. Wysocki { 1458c0356db7SUlf Hansson struct generic_pm_domain_data *gpd_data; 1459b56d9c91SUlf Hansson int ret; 1460f721889fSRafael J. Wysocki 1461f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1462f721889fSRafael J. Wysocki 1463f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1464f721889fSRafael J. Wysocki return -EINVAL; 1465f721889fSRafael J. Wysocki 146696c1bf68SUlf Hansson gpd_data = genpd_alloc_dev_data(dev, td); 14673e235685SUlf Hansson if (IS_ERR(gpd_data)) 14683e235685SUlf Hansson return PTR_ERR(gpd_data); 14696ff7bb0dSRafael J. Wysocki 147035241d12SLina Iyer genpd_lock(genpd); 1471f721889fSRafael J. Wysocki 1472b472c2faSUlf Hansson ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1473b472c2faSUlf Hansson if (ret) 1474b472c2faSUlf Hansson goto out; 1475d79b6fe1SGeert Uytterhoeven 1476975e83cfSSudeep Holla dev_pm_domain_set(dev, &genpd->domain); 1477975e83cfSSudeep Holla 147814b53064SUlf Hansson genpd->device_count++; 147914b53064SUlf Hansson genpd->max_off_time_changed = true; 148014b53064SUlf Hansson 14811d5fcfecSRafael J. Wysocki list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 14826ff7bb0dSRafael J. Wysocki 1483f721889fSRafael J. Wysocki out: 148435241d12SLina Iyer genpd_unlock(genpd); 1485f721889fSRafael J. Wysocki 1486c0356db7SUlf Hansson if (ret) 1487c0356db7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 1488c0356db7SUlf Hansson else 1489c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 14901d5fcfecSRafael J. Wysocki 1491f721889fSRafael J. Wysocki return ret; 1492f721889fSRafael J. Wysocki } 149319efa5ffSJon Hunter 149419efa5ffSJon Hunter /** 14951a7a6707SUlf Hansson * pm_genpd_add_device - Add a device to an I/O PM domain. 149619efa5ffSJon Hunter * @genpd: PM domain to add the device to. 149719efa5ffSJon Hunter * @dev: Device to be added. 149819efa5ffSJon Hunter */ 14991a7a6707SUlf Hansson int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) 150019efa5ffSJon Hunter { 150119efa5ffSJon Hunter int ret; 150219efa5ffSJon Hunter 150319efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 15041a7a6707SUlf Hansson ret = genpd_add_device(genpd, dev, NULL); 150519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 150619efa5ffSJon Hunter 150719efa5ffSJon Hunter return ret; 150819efa5ffSJon Hunter } 15091a7a6707SUlf Hansson EXPORT_SYMBOL_GPL(pm_genpd_add_device); 1510f721889fSRafael J. Wysocki 151185168d56SUlf Hansson static int genpd_remove_device(struct generic_pm_domain *genpd, 1512f721889fSRafael J. Wysocki struct device *dev) 1513f721889fSRafael J. Wysocki { 15146ff7bb0dSRafael J. Wysocki struct generic_pm_domain_data *gpd_data; 15154605ab65SRafael J. Wysocki struct pm_domain_data *pdd; 1516efa69025SRafael J. Wysocki int ret = 0; 1517f721889fSRafael J. Wysocki 1518f721889fSRafael J. Wysocki dev_dbg(dev, "%s()\n", __func__); 1519f721889fSRafael J. Wysocki 1520c0356db7SUlf Hansson pdd = dev->power.subsys_data->domain_data; 1521c0356db7SUlf Hansson gpd_data = to_gpd_data(pdd); 1522c0356db7SUlf Hansson dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1523c0356db7SUlf Hansson 152435241d12SLina Iyer genpd_lock(genpd); 1525f721889fSRafael J. Wysocki 1526596ba34bSRafael J. Wysocki if (genpd->prepared_count > 0) { 1527596ba34bSRafael J. Wysocki ret = -EAGAIN; 1528596ba34bSRafael J. Wysocki goto out; 1529596ba34bSRafael J. Wysocki } 1530596ba34bSRafael J. Wysocki 15316ff7bb0dSRafael J. Wysocki genpd->device_count--; 15326ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 15336ff7bb0dSRafael J. Wysocki 1534d79b6fe1SGeert Uytterhoeven if (genpd->detach_dev) 1535c16561e8SUlf Hansson genpd->detach_dev(genpd, dev); 1536d79b6fe1SGeert Uytterhoeven 1537975e83cfSSudeep Holla dev_pm_domain_set(dev, NULL); 1538975e83cfSSudeep Holla 1539efa69025SRafael J. Wysocki list_del_init(&pdd->list_node); 15406ff7bb0dSRafael J. Wysocki 154135241d12SLina Iyer genpd_unlock(genpd); 15426ff7bb0dSRafael J. Wysocki 154349d400c7SUlf Hansson genpd_free_dev_data(dev, gpd_data); 15441d5fcfecSRafael J. Wysocki 15456ff7bb0dSRafael J. Wysocki return 0; 1546f721889fSRafael J. Wysocki 1547596ba34bSRafael J. Wysocki out: 154835241d12SLina Iyer genpd_unlock(genpd); 1549c0356db7SUlf Hansson dev_pm_qos_add_notifier(dev, &gpd_data->nb); 1550f721889fSRafael J. Wysocki 1551f721889fSRafael J. Wysocki return ret; 1552f721889fSRafael J. Wysocki } 155385168d56SUlf Hansson 155485168d56SUlf Hansson /** 155585168d56SUlf Hansson * pm_genpd_remove_device - Remove a device from an I/O PM domain. 155685168d56SUlf Hansson * @dev: Device to be removed. 155785168d56SUlf Hansson */ 1558924f4486SUlf Hansson int pm_genpd_remove_device(struct device *dev) 155985168d56SUlf Hansson { 1560924f4486SUlf Hansson struct generic_pm_domain *genpd = genpd_lookup_dev(dev); 1561924f4486SUlf Hansson 1562924f4486SUlf Hansson if (!genpd) 156385168d56SUlf Hansson return -EINVAL; 156485168d56SUlf Hansson 156585168d56SUlf Hansson return genpd_remove_device(genpd, dev); 156685168d56SUlf Hansson } 156724c96dc7SMaruthi Bayyavarapu EXPORT_SYMBOL_GPL(pm_genpd_remove_device); 1568f721889fSRafael J. Wysocki 156919efa5ffSJon Hunter static int genpd_add_subdomain(struct generic_pm_domain *genpd, 1570bc0403ffSRafael J. Wysocki struct generic_pm_domain *subdomain) 1571f721889fSRafael J. Wysocki { 15722547923dSLina Iyer struct gpd_link *link, *itr; 1573f721889fSRafael J. Wysocki int ret = 0; 1574f721889fSRafael J. Wysocki 1575fb7268beSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain) 1576fb7268beSRafael J. Wysocki || genpd == subdomain) 1577f721889fSRafael J. Wysocki return -EINVAL; 1578f721889fSRafael J. Wysocki 1579d716f479SLina Iyer /* 1580d716f479SLina Iyer * If the domain can be powered on/off in an IRQ safe 1581d716f479SLina Iyer * context, ensure that the subdomain can also be 1582d716f479SLina Iyer * powered on/off in that context. 1583d716f479SLina Iyer */ 1584d716f479SLina Iyer if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 158544cae7d5SDan Carpenter WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 1586d716f479SLina Iyer genpd->name, subdomain->name); 1587d716f479SLina Iyer return -EINVAL; 1588d716f479SLina Iyer } 1589d716f479SLina Iyer 15902547923dSLina Iyer link = kzalloc(sizeof(*link), GFP_KERNEL); 15912547923dSLina Iyer if (!link) 15922547923dSLina Iyer return -ENOMEM; 15932547923dSLina Iyer 159435241d12SLina Iyer genpd_lock(subdomain); 159535241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1596f721889fSRafael J. Wysocki 159741e2c8e0SUlf Hansson if (!genpd_status_on(genpd) && genpd_status_on(subdomain)) { 1598f721889fSRafael J. Wysocki ret = -EINVAL; 1599f721889fSRafael J. Wysocki goto out; 1600f721889fSRafael J. Wysocki } 1601f721889fSRafael J. Wysocki 16022547923dSLina Iyer list_for_each_entry(itr, &genpd->master_links, master_node) { 16032547923dSLina Iyer if (itr->slave == subdomain && itr->master == genpd) { 1604f721889fSRafael J. Wysocki ret = -EINVAL; 1605f721889fSRafael J. Wysocki goto out; 1606f721889fSRafael J. Wysocki } 1607f721889fSRafael J. Wysocki } 1608f721889fSRafael J. Wysocki 16095063ce15SRafael J. Wysocki link->master = genpd; 16105063ce15SRafael J. Wysocki list_add_tail(&link->master_node, &genpd->master_links); 1611bc0403ffSRafael J. Wysocki link->slave = subdomain; 1612bc0403ffSRafael J. Wysocki list_add_tail(&link->slave_node, &subdomain->slave_links); 161341e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1614c4bb3160SRafael J. Wysocki genpd_sd_counter_inc(genpd); 1615f721889fSRafael J. Wysocki 1616f721889fSRafael J. Wysocki out: 161735241d12SLina Iyer genpd_unlock(genpd); 161835241d12SLina Iyer genpd_unlock(subdomain); 16192547923dSLina Iyer if (ret) 16202547923dSLina Iyer kfree(link); 1621f721889fSRafael J. Wysocki return ret; 1622f721889fSRafael J. Wysocki } 162319efa5ffSJon Hunter 162419efa5ffSJon Hunter /** 162519efa5ffSJon Hunter * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 162619efa5ffSJon Hunter * @genpd: Master PM domain to add the subdomain to. 162719efa5ffSJon Hunter * @subdomain: Subdomain to be added. 162819efa5ffSJon Hunter */ 162919efa5ffSJon Hunter int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 163019efa5ffSJon Hunter struct generic_pm_domain *subdomain) 163119efa5ffSJon Hunter { 163219efa5ffSJon Hunter int ret; 163319efa5ffSJon Hunter 163419efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 163519efa5ffSJon Hunter ret = genpd_add_subdomain(genpd, subdomain); 163619efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 163719efa5ffSJon Hunter 163819efa5ffSJon Hunter return ret; 163919efa5ffSJon Hunter } 1640d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); 1641f721889fSRafael J. Wysocki 1642f721889fSRafael J. Wysocki /** 1643f721889fSRafael J. Wysocki * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1644f721889fSRafael J. Wysocki * @genpd: Master PM domain to remove the subdomain from. 16455063ce15SRafael J. Wysocki * @subdomain: Subdomain to be removed. 1646f721889fSRafael J. Wysocki */ 1647f721889fSRafael J. Wysocki int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 16485063ce15SRafael J. Wysocki struct generic_pm_domain *subdomain) 1649f721889fSRafael J. Wysocki { 1650c6e83cacSKrzysztof Kozlowski struct gpd_link *l, *link; 1651f721889fSRafael J. Wysocki int ret = -EINVAL; 1652f721889fSRafael J. Wysocki 16535063ce15SRafael J. Wysocki if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) 1654f721889fSRafael J. Wysocki return -EINVAL; 1655f721889fSRafael J. Wysocki 165635241d12SLina Iyer genpd_lock(subdomain); 165735241d12SLina Iyer genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING); 1658f721889fSRafael J. Wysocki 1659beda5fc1SJon Hunter if (!list_empty(&subdomain->master_links) || subdomain->device_count) { 166030e7a65bSJon Hunter pr_warn("%s: unable to remove subdomain %s\n", genpd->name, 166130e7a65bSJon Hunter subdomain->name); 166230e7a65bSJon Hunter ret = -EBUSY; 166330e7a65bSJon Hunter goto out; 166430e7a65bSJon Hunter } 166530e7a65bSJon Hunter 1666c6e83cacSKrzysztof Kozlowski list_for_each_entry_safe(link, l, &genpd->master_links, master_node) { 16675063ce15SRafael J. Wysocki if (link->slave != subdomain) 1668f721889fSRafael J. Wysocki continue; 1669f721889fSRafael J. Wysocki 16705063ce15SRafael J. Wysocki list_del(&link->master_node); 16715063ce15SRafael J. Wysocki list_del(&link->slave_node); 16725063ce15SRafael J. Wysocki kfree(link); 167341e2c8e0SUlf Hansson if (genpd_status_on(subdomain)) 1674f721889fSRafael J. Wysocki genpd_sd_counter_dec(genpd); 1675f721889fSRafael J. Wysocki 1676f721889fSRafael J. Wysocki ret = 0; 1677f721889fSRafael J. Wysocki break; 1678f721889fSRafael J. Wysocki } 1679f721889fSRafael J. Wysocki 168030e7a65bSJon Hunter out: 168135241d12SLina Iyer genpd_unlock(genpd); 168235241d12SLina Iyer genpd_unlock(subdomain); 1683f721889fSRafael J. Wysocki 1684f721889fSRafael J. Wysocki return ret; 1685f721889fSRafael J. Wysocki } 1686d60ee966SStephen Boyd EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); 1687f721889fSRafael J. Wysocki 168859d65b73SLina Iyer static int genpd_set_default_power_state(struct generic_pm_domain *genpd) 168959d65b73SLina Iyer { 169059d65b73SLina Iyer struct genpd_power_state *state; 169159d65b73SLina Iyer 169259d65b73SLina Iyer state = kzalloc(sizeof(*state), GFP_KERNEL); 169359d65b73SLina Iyer if (!state) 169459d65b73SLina Iyer return -ENOMEM; 169559d65b73SLina Iyer 169659d65b73SLina Iyer genpd->states = state; 169759d65b73SLina Iyer genpd->state_count = 1; 169859d65b73SLina Iyer genpd->free = state; 169959d65b73SLina Iyer 170059d65b73SLina Iyer return 0; 170159d65b73SLina Iyer } 170259d65b73SLina Iyer 1703d716f479SLina Iyer static void genpd_lock_init(struct generic_pm_domain *genpd) 1704d716f479SLina Iyer { 1705d716f479SLina Iyer if (genpd->flags & GENPD_FLAG_IRQ_SAFE) { 1706d716f479SLina Iyer spin_lock_init(&genpd->slock); 1707d716f479SLina Iyer genpd->lock_ops = &genpd_spin_ops; 1708d716f479SLina Iyer } else { 1709d716f479SLina Iyer mutex_init(&genpd->mlock); 1710d716f479SLina Iyer genpd->lock_ops = &genpd_mtx_ops; 1711d716f479SLina Iyer } 1712d716f479SLina Iyer } 1713d716f479SLina Iyer 1714d23b9b00SRafael J. Wysocki /** 1715f721889fSRafael J. Wysocki * pm_genpd_init - Initialize a generic I/O PM domain object. 1716f721889fSRafael J. Wysocki * @genpd: PM domain object to initialize. 1717f721889fSRafael J. Wysocki * @gov: PM domain governor to associate with the domain (may be NULL). 1718f721889fSRafael J. Wysocki * @is_off: Initial value of the domain's power_is_off field. 17197eb231c3SUlf Hansson * 17207eb231c3SUlf Hansson * Returns 0 on successful initialization, else a negative error code. 1721f721889fSRafael J. Wysocki */ 17227eb231c3SUlf Hansson int pm_genpd_init(struct generic_pm_domain *genpd, 1723f721889fSRafael J. Wysocki struct dev_power_governor *gov, bool is_off) 1724f721889fSRafael J. Wysocki { 172559d65b73SLina Iyer int ret; 172659d65b73SLina Iyer 1727f721889fSRafael J. Wysocki if (IS_ERR_OR_NULL(genpd)) 17287eb231c3SUlf Hansson return -EINVAL; 1729f721889fSRafael J. Wysocki 17305063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->master_links); 17315063ce15SRafael J. Wysocki INIT_LIST_HEAD(&genpd->slave_links); 1732f721889fSRafael J. Wysocki INIT_LIST_HEAD(&genpd->dev_list); 1733d716f479SLina Iyer genpd_lock_init(genpd); 1734f721889fSRafael J. Wysocki genpd->gov = gov; 1735f721889fSRafael J. Wysocki INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1736c4bb3160SRafael J. Wysocki atomic_set(&genpd->sd_count, 0); 173717b75ecaSRafael J. Wysocki genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; 1738596ba34bSRafael J. Wysocki genpd->device_count = 0; 1739221e9b58SRafael J. Wysocki genpd->max_off_time_ns = -1; 17406ff7bb0dSRafael J. Wysocki genpd->max_off_time_changed = true; 1741de0aa06dSJon Hunter genpd->provider = NULL; 1742de0aa06dSJon Hunter genpd->has_provider = false; 1743afece3abSThara Gopinath genpd->accounting_time = ktime_get(); 1744795bd2e7SUlf Hansson genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; 1745795bd2e7SUlf Hansson genpd->domain.ops.runtime_resume = genpd_runtime_resume; 17469e9704eaSUlf Hansson genpd->domain.ops.prepare = genpd_prepare; 17479e9704eaSUlf Hansson genpd->domain.ops.suspend_noirq = genpd_suspend_noirq; 17489e9704eaSUlf Hansson genpd->domain.ops.resume_noirq = genpd_resume_noirq; 17499e9704eaSUlf Hansson genpd->domain.ops.freeze_noirq = genpd_freeze_noirq; 17509e9704eaSUlf Hansson genpd->domain.ops.thaw_noirq = genpd_thaw_noirq; 17519e9704eaSUlf Hansson genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq; 17529e9704eaSUlf Hansson genpd->domain.ops.restore_noirq = genpd_restore_noirq; 17539e9704eaSUlf Hansson genpd->domain.ops.complete = genpd_complete; 1754c11f6f5bSUlf Hansson 1755c11f6f5bSUlf Hansson if (genpd->flags & GENPD_FLAG_PM_CLK) { 1756c11f6f5bSUlf Hansson genpd->dev_ops.stop = pm_clk_suspend; 1757c11f6f5bSUlf Hansson genpd->dev_ops.start = pm_clk_resume; 1758c11f6f5bSUlf Hansson } 1759c11f6f5bSUlf Hansson 1760ffaa42e8SUlf Hansson /* Always-on domains must be powered on at initialization. */ 1761ffaa42e8SUlf Hansson if (genpd_is_always_on(genpd) && !genpd_status_on(genpd)) 1762ffaa42e8SUlf Hansson return -EINVAL; 1763ffaa42e8SUlf Hansson 1764fc5cbf0cSAxel Haslam /* Use only one "off" state if there were no states declared */ 176559d65b73SLina Iyer if (genpd->state_count == 0) { 176659d65b73SLina Iyer ret = genpd_set_default_power_state(genpd); 176759d65b73SLina Iyer if (ret) 176859d65b73SLina Iyer return ret; 176946b7fe94SAisheng Dong } else if (!gov && genpd->state_count > 1) { 17702c9b7f87SUlf Hansson pr_warn("%s : no governor for states\n", genpd->name); 177159d65b73SLina Iyer } 1772fc5cbf0cSAxel Haslam 1773401ea157SViresh Kumar device_initialize(&genpd->dev); 1774401ea157SViresh Kumar dev_set_name(&genpd->dev, "%s", genpd->name); 1775401ea157SViresh Kumar 17765125bbf3SRafael J. Wysocki mutex_lock(&gpd_list_lock); 17775125bbf3SRafael J. Wysocki list_add(&genpd->gpd_list_node, &gpd_list); 17785125bbf3SRafael J. Wysocki mutex_unlock(&gpd_list_lock); 17797eb231c3SUlf Hansson 17807eb231c3SUlf Hansson return 0; 17815125bbf3SRafael J. Wysocki } 1782be5ed55dSRajendra Nayak EXPORT_SYMBOL_GPL(pm_genpd_init); 1783aa42240aSTomasz Figa 17843fe57710SJon Hunter static int genpd_remove(struct generic_pm_domain *genpd) 17853fe57710SJon Hunter { 17863fe57710SJon Hunter struct gpd_link *l, *link; 17873fe57710SJon Hunter 17883fe57710SJon Hunter if (IS_ERR_OR_NULL(genpd)) 17893fe57710SJon Hunter return -EINVAL; 17903fe57710SJon Hunter 179135241d12SLina Iyer genpd_lock(genpd); 17923fe57710SJon Hunter 17933fe57710SJon Hunter if (genpd->has_provider) { 179435241d12SLina Iyer genpd_unlock(genpd); 17953fe57710SJon Hunter pr_err("Provider present, unable to remove %s\n", genpd->name); 17963fe57710SJon Hunter return -EBUSY; 17973fe57710SJon Hunter } 17983fe57710SJon Hunter 17993fe57710SJon Hunter if (!list_empty(&genpd->master_links) || genpd->device_count) { 180035241d12SLina Iyer genpd_unlock(genpd); 18013fe57710SJon Hunter pr_err("%s: unable to remove %s\n", __func__, genpd->name); 18023fe57710SJon Hunter return -EBUSY; 18033fe57710SJon Hunter } 18043fe57710SJon Hunter 18053fe57710SJon Hunter list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { 18063fe57710SJon Hunter list_del(&link->master_node); 18073fe57710SJon Hunter list_del(&link->slave_node); 18083fe57710SJon Hunter kfree(link); 18093fe57710SJon Hunter } 18103fe57710SJon Hunter 18113fe57710SJon Hunter list_del(&genpd->gpd_list_node); 181235241d12SLina Iyer genpd_unlock(genpd); 18133fe57710SJon Hunter cancel_work_sync(&genpd->power_off_work); 181459d65b73SLina Iyer kfree(genpd->free); 18153fe57710SJon Hunter pr_debug("%s: removed %s\n", __func__, genpd->name); 18163fe57710SJon Hunter 18173fe57710SJon Hunter return 0; 18183fe57710SJon Hunter } 18193fe57710SJon Hunter 18203fe57710SJon Hunter /** 18213fe57710SJon Hunter * pm_genpd_remove - Remove a generic I/O PM domain 18223fe57710SJon Hunter * @genpd: Pointer to PM domain that is to be removed. 18233fe57710SJon Hunter * 18243fe57710SJon Hunter * To remove the PM domain, this function: 18253fe57710SJon Hunter * - Removes the PM domain as a subdomain to any parent domains, 18263fe57710SJon Hunter * if it was added. 18273fe57710SJon Hunter * - Removes the PM domain from the list of registered PM domains. 18283fe57710SJon Hunter * 18293fe57710SJon Hunter * The PM domain will only be removed, if the associated provider has 18303fe57710SJon Hunter * been removed, it is not a parent to any other PM domain and has no 18313fe57710SJon Hunter * devices associated with it. 18323fe57710SJon Hunter */ 18333fe57710SJon Hunter int pm_genpd_remove(struct generic_pm_domain *genpd) 18343fe57710SJon Hunter { 18353fe57710SJon Hunter int ret; 18363fe57710SJon Hunter 18373fe57710SJon Hunter mutex_lock(&gpd_list_lock); 18383fe57710SJon Hunter ret = genpd_remove(genpd); 18393fe57710SJon Hunter mutex_unlock(&gpd_list_lock); 18403fe57710SJon Hunter 18413fe57710SJon Hunter return ret; 18423fe57710SJon Hunter } 18433fe57710SJon Hunter EXPORT_SYMBOL_GPL(pm_genpd_remove); 18443fe57710SJon Hunter 1845aa42240aSTomasz Figa #ifdef CONFIG_PM_GENERIC_DOMAINS_OF 1846892ebdccSJon Hunter 1847aa42240aSTomasz Figa /* 1848aa42240aSTomasz Figa * Device Tree based PM domain providers. 1849aa42240aSTomasz Figa * 1850aa42240aSTomasz Figa * The code below implements generic device tree based PM domain providers that 1851aa42240aSTomasz Figa * bind device tree nodes with generic PM domains registered in the system. 1852aa42240aSTomasz Figa * 1853aa42240aSTomasz Figa * Any driver that registers generic PM domains and needs to support binding of 1854aa42240aSTomasz Figa * devices to these domains is supposed to register a PM domain provider, which 1855aa42240aSTomasz Figa * maps a PM domain specifier retrieved from the device tree to a PM domain. 1856aa42240aSTomasz Figa * 1857aa42240aSTomasz Figa * Two simple mapping functions have been provided for convenience: 1858892ebdccSJon Hunter * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. 1859892ebdccSJon Hunter * - genpd_xlate_onecell() for mapping of multiple PM domains per node by 1860aa42240aSTomasz Figa * index. 1861aa42240aSTomasz Figa */ 1862aa42240aSTomasz Figa 1863aa42240aSTomasz Figa /** 1864aa42240aSTomasz Figa * struct of_genpd_provider - PM domain provider registration structure 1865aa42240aSTomasz Figa * @link: Entry in global list of PM domain providers 1866aa42240aSTomasz Figa * @node: Pointer to device tree node of PM domain provider 1867aa42240aSTomasz Figa * @xlate: Provider-specific xlate callback mapping a set of specifier cells 1868aa42240aSTomasz Figa * into a PM domain. 1869aa42240aSTomasz Figa * @data: context pointer to be passed into @xlate callback 1870aa42240aSTomasz Figa */ 1871aa42240aSTomasz Figa struct of_genpd_provider { 1872aa42240aSTomasz Figa struct list_head link; 1873aa42240aSTomasz Figa struct device_node *node; 1874aa42240aSTomasz Figa genpd_xlate_t xlate; 1875aa42240aSTomasz Figa void *data; 1876aa42240aSTomasz Figa }; 1877aa42240aSTomasz Figa 1878aa42240aSTomasz Figa /* List of registered PM domain providers. */ 1879aa42240aSTomasz Figa static LIST_HEAD(of_genpd_providers); 1880aa42240aSTomasz Figa /* Mutex to protect the list above. */ 1881aa42240aSTomasz Figa static DEFINE_MUTEX(of_genpd_mutex); 1882aa42240aSTomasz Figa 1883aa42240aSTomasz Figa /** 1884892ebdccSJon Hunter * genpd_xlate_simple() - Xlate function for direct node-domain mapping 1885aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1886aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct generic_pm_domain 1887aa42240aSTomasz Figa * 1888aa42240aSTomasz Figa * This is a generic xlate function that can be used to model PM domains that 1889aa42240aSTomasz Figa * have their own device tree nodes. The private data of xlate function needs 1890aa42240aSTomasz Figa * to be a valid pointer to struct generic_pm_domain. 1891aa42240aSTomasz Figa */ 1892892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_simple( 1893aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1894aa42240aSTomasz Figa void *data) 1895aa42240aSTomasz Figa { 1896aa42240aSTomasz Figa return data; 1897aa42240aSTomasz Figa } 1898aa42240aSTomasz Figa 1899aa42240aSTomasz Figa /** 1900892ebdccSJon Hunter * genpd_xlate_onecell() - Xlate function using a single index. 1901aa42240aSTomasz Figa * @genpdspec: OF phandle args to map into a PM domain 1902aa42240aSTomasz Figa * @data: xlate function private data - pointer to struct genpd_onecell_data 1903aa42240aSTomasz Figa * 1904aa42240aSTomasz Figa * This is a generic xlate function that can be used to model simple PM domain 1905aa42240aSTomasz Figa * controllers that have one device tree node and provide multiple PM domains. 1906aa42240aSTomasz Figa * A single cell is used as an index into an array of PM domains specified in 1907aa42240aSTomasz Figa * the genpd_onecell_data struct when registering the provider. 1908aa42240aSTomasz Figa */ 1909892ebdccSJon Hunter static struct generic_pm_domain *genpd_xlate_onecell( 1910aa42240aSTomasz Figa struct of_phandle_args *genpdspec, 1911aa42240aSTomasz Figa void *data) 1912aa42240aSTomasz Figa { 1913aa42240aSTomasz Figa struct genpd_onecell_data *genpd_data = data; 1914aa42240aSTomasz Figa unsigned int idx = genpdspec->args[0]; 1915aa42240aSTomasz Figa 1916aa42240aSTomasz Figa if (genpdspec->args_count != 1) 1917aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1918aa42240aSTomasz Figa 1919aa42240aSTomasz Figa if (idx >= genpd_data->num_domains) { 1920aa42240aSTomasz Figa pr_err("%s: invalid domain index %u\n", __func__, idx); 1921aa42240aSTomasz Figa return ERR_PTR(-EINVAL); 1922aa42240aSTomasz Figa } 1923aa42240aSTomasz Figa 1924aa42240aSTomasz Figa if (!genpd_data->domains[idx]) 1925aa42240aSTomasz Figa return ERR_PTR(-ENOENT); 1926aa42240aSTomasz Figa 1927aa42240aSTomasz Figa return genpd_data->domains[idx]; 1928aa42240aSTomasz Figa } 1929aa42240aSTomasz Figa 1930aa42240aSTomasz Figa /** 1931892ebdccSJon Hunter * genpd_add_provider() - Register a PM domain provider for a node 1932aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider. 1933aa42240aSTomasz Figa * @xlate: Callback for decoding PM domain from phandle arguments. 1934aa42240aSTomasz Figa * @data: Context pointer for @xlate callback. 1935aa42240aSTomasz Figa */ 1936892ebdccSJon Hunter static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, 1937aa42240aSTomasz Figa void *data) 1938aa42240aSTomasz Figa { 1939aa42240aSTomasz Figa struct of_genpd_provider *cp; 1940aa42240aSTomasz Figa 1941aa42240aSTomasz Figa cp = kzalloc(sizeof(*cp), GFP_KERNEL); 1942aa42240aSTomasz Figa if (!cp) 1943aa42240aSTomasz Figa return -ENOMEM; 1944aa42240aSTomasz Figa 1945aa42240aSTomasz Figa cp->node = of_node_get(np); 1946aa42240aSTomasz Figa cp->data = data; 1947aa42240aSTomasz Figa cp->xlate = xlate; 1948aa42240aSTomasz Figa 1949aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 1950aa42240aSTomasz Figa list_add(&cp->link, &of_genpd_providers); 1951aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 1952ea11e94bSRob Herring pr_debug("Added domain provider from %pOF\n", np); 1953aa42240aSTomasz Figa 1954aa42240aSTomasz Figa return 0; 1955aa42240aSTomasz Figa } 1956892ebdccSJon Hunter 1957892ebdccSJon Hunter /** 1958892ebdccSJon Hunter * of_genpd_add_provider_simple() - Register a simple PM domain provider 1959892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 1960892ebdccSJon Hunter * @genpd: Pointer to PM domain associated with the PM domain provider. 1961892ebdccSJon Hunter */ 1962892ebdccSJon Hunter int of_genpd_add_provider_simple(struct device_node *np, 1963892ebdccSJon Hunter struct generic_pm_domain *genpd) 1964892ebdccSJon Hunter { 19650159ec67SJon Hunter int ret = -EINVAL; 19660159ec67SJon Hunter 19670159ec67SJon Hunter if (!np || !genpd) 19680159ec67SJon Hunter return -EINVAL; 19690159ec67SJon Hunter 19700159ec67SJon Hunter mutex_lock(&gpd_list_lock); 19710159ec67SJon Hunter 19726a0ae73dSViresh Kumar if (!genpd_present(genpd)) 19736a0ae73dSViresh Kumar goto unlock; 19746a0ae73dSViresh Kumar 19756a0ae73dSViresh Kumar genpd->dev.of_node = np; 19766a0ae73dSViresh Kumar 19776a0ae73dSViresh Kumar /* Parse genpd OPP table */ 19786a0ae73dSViresh Kumar if (genpd->set_performance_state) { 19796a0ae73dSViresh Kumar ret = dev_pm_opp_of_add_table(&genpd->dev); 19806a0ae73dSViresh Kumar if (ret) { 19816a0ae73dSViresh Kumar dev_err(&genpd->dev, "Failed to add OPP table: %d\n", 19826a0ae73dSViresh Kumar ret); 19836a0ae73dSViresh Kumar goto unlock; 1984de0aa06dSJon Hunter } 19851067ae3eSViresh Kumar 19861067ae3eSViresh Kumar /* 19871067ae3eSViresh Kumar * Save table for faster processing while setting performance 19881067ae3eSViresh Kumar * state. 19891067ae3eSViresh Kumar */ 19901067ae3eSViresh Kumar genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev); 19911067ae3eSViresh Kumar WARN_ON(!genpd->opp_table); 19928ce95844SViresh Kumar } 1993de0aa06dSJon Hunter 19946a0ae73dSViresh Kumar ret = genpd_add_provider(np, genpd_xlate_simple, genpd); 19956a0ae73dSViresh Kumar if (ret) { 19961067ae3eSViresh Kumar if (genpd->set_performance_state) { 19971067ae3eSViresh Kumar dev_pm_opp_put_opp_table(genpd->opp_table); 19986a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&genpd->dev); 19991067ae3eSViresh Kumar } 20006a0ae73dSViresh Kumar 20016a0ae73dSViresh Kumar goto unlock; 20026a0ae73dSViresh Kumar } 20036a0ae73dSViresh Kumar 20046a0ae73dSViresh Kumar genpd->provider = &np->fwnode; 20056a0ae73dSViresh Kumar genpd->has_provider = true; 20066a0ae73dSViresh Kumar 20076a0ae73dSViresh Kumar unlock: 20080159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 20090159ec67SJon Hunter 20100159ec67SJon Hunter return ret; 2011892ebdccSJon Hunter } 2012892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); 2013892ebdccSJon Hunter 2014892ebdccSJon Hunter /** 2015892ebdccSJon Hunter * of_genpd_add_provider_onecell() - Register a onecell PM domain provider 2016892ebdccSJon Hunter * @np: Device node pointer associated with the PM domain provider. 2017892ebdccSJon Hunter * @data: Pointer to the data associated with the PM domain provider. 2018892ebdccSJon Hunter */ 2019892ebdccSJon Hunter int of_genpd_add_provider_onecell(struct device_node *np, 2020892ebdccSJon Hunter struct genpd_onecell_data *data) 2021892ebdccSJon Hunter { 20226a0ae73dSViresh Kumar struct generic_pm_domain *genpd; 20230159ec67SJon Hunter unsigned int i; 2024de0aa06dSJon Hunter int ret = -EINVAL; 20250159ec67SJon Hunter 20260159ec67SJon Hunter if (!np || !data) 20270159ec67SJon Hunter return -EINVAL; 20280159ec67SJon Hunter 20290159ec67SJon Hunter mutex_lock(&gpd_list_lock); 20300159ec67SJon Hunter 203140845524SThierry Reding if (!data->xlate) 203240845524SThierry Reding data->xlate = genpd_xlate_onecell; 203340845524SThierry Reding 20340159ec67SJon Hunter for (i = 0; i < data->num_domains; i++) { 20356a0ae73dSViresh Kumar genpd = data->domains[i]; 20366a0ae73dSViresh Kumar 20376a0ae73dSViresh Kumar if (!genpd) 2038609bed67STomeu Vizoso continue; 20396a0ae73dSViresh Kumar if (!genpd_present(genpd)) 2040de0aa06dSJon Hunter goto error; 2041de0aa06dSJon Hunter 20426a0ae73dSViresh Kumar genpd->dev.of_node = np; 20436a0ae73dSViresh Kumar 20446a0ae73dSViresh Kumar /* Parse genpd OPP table */ 20456a0ae73dSViresh Kumar if (genpd->set_performance_state) { 20466a0ae73dSViresh Kumar ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); 20476a0ae73dSViresh Kumar if (ret) { 20486a0ae73dSViresh Kumar dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n", 20496a0ae73dSViresh Kumar i, ret); 20506a0ae73dSViresh Kumar goto error; 20516a0ae73dSViresh Kumar } 20521067ae3eSViresh Kumar 20531067ae3eSViresh Kumar /* 20541067ae3eSViresh Kumar * Save table for faster processing while setting 20551067ae3eSViresh Kumar * performance state. 20561067ae3eSViresh Kumar */ 20571067ae3eSViresh Kumar genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i); 20581067ae3eSViresh Kumar WARN_ON(!genpd->opp_table); 20596a0ae73dSViresh Kumar } 20606a0ae73dSViresh Kumar 20616a0ae73dSViresh Kumar genpd->provider = &np->fwnode; 20626a0ae73dSViresh Kumar genpd->has_provider = true; 20630159ec67SJon Hunter } 20640159ec67SJon Hunter 206540845524SThierry Reding ret = genpd_add_provider(np, data->xlate, data); 2066de0aa06dSJon Hunter if (ret < 0) 2067de0aa06dSJon Hunter goto error; 2068de0aa06dSJon Hunter 2069de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 2070de0aa06dSJon Hunter 2071de0aa06dSJon Hunter return 0; 2072de0aa06dSJon Hunter 2073de0aa06dSJon Hunter error: 2074de0aa06dSJon Hunter while (i--) { 20756a0ae73dSViresh Kumar genpd = data->domains[i]; 20766a0ae73dSViresh Kumar 20776a0ae73dSViresh Kumar if (!genpd) 2078609bed67STomeu Vizoso continue; 20796a0ae73dSViresh Kumar 20806a0ae73dSViresh Kumar genpd->provider = NULL; 20816a0ae73dSViresh Kumar genpd->has_provider = false; 20826a0ae73dSViresh Kumar 20831067ae3eSViresh Kumar if (genpd->set_performance_state) { 20841067ae3eSViresh Kumar dev_pm_opp_put_opp_table(genpd->opp_table); 20856a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&genpd->dev); 2086de0aa06dSJon Hunter } 20871067ae3eSViresh Kumar } 20880159ec67SJon Hunter 20890159ec67SJon Hunter mutex_unlock(&gpd_list_lock); 20900159ec67SJon Hunter 20910159ec67SJon Hunter return ret; 2092892ebdccSJon Hunter } 2093892ebdccSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); 2094aa42240aSTomasz Figa 2095aa42240aSTomasz Figa /** 2096aa42240aSTomasz Figa * of_genpd_del_provider() - Remove a previously registered PM domain provider 2097aa42240aSTomasz Figa * @np: Device node pointer associated with the PM domain provider 2098aa42240aSTomasz Figa */ 2099aa42240aSTomasz Figa void of_genpd_del_provider(struct device_node *np) 2100aa42240aSTomasz Figa { 2101b556b15dSKrzysztof Kozlowski struct of_genpd_provider *cp, *tmp; 2102de0aa06dSJon Hunter struct generic_pm_domain *gpd; 2103aa42240aSTomasz Figa 2104de0aa06dSJon Hunter mutex_lock(&gpd_list_lock); 2105aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 2106b556b15dSKrzysztof Kozlowski list_for_each_entry_safe(cp, tmp, &of_genpd_providers, link) { 2107aa42240aSTomasz Figa if (cp->node == np) { 2108de0aa06dSJon Hunter /* 2109de0aa06dSJon Hunter * For each PM domain associated with the 2110de0aa06dSJon Hunter * provider, set the 'has_provider' to false 2111de0aa06dSJon Hunter * so that the PM domain can be safely removed. 2112de0aa06dSJon Hunter */ 21136a0ae73dSViresh Kumar list_for_each_entry(gpd, &gpd_list, gpd_list_node) { 21146a0ae73dSViresh Kumar if (gpd->provider == &np->fwnode) { 2115de0aa06dSJon Hunter gpd->has_provider = false; 2116de0aa06dSJon Hunter 21176a0ae73dSViresh Kumar if (!gpd->set_performance_state) 21186a0ae73dSViresh Kumar continue; 21196a0ae73dSViresh Kumar 21201067ae3eSViresh Kumar dev_pm_opp_put_opp_table(gpd->opp_table); 21216a0ae73dSViresh Kumar dev_pm_opp_of_remove_table(&gpd->dev); 21226a0ae73dSViresh Kumar } 21236a0ae73dSViresh Kumar } 21246a0ae73dSViresh Kumar 2125aa42240aSTomasz Figa list_del(&cp->link); 2126aa42240aSTomasz Figa of_node_put(cp->node); 2127aa42240aSTomasz Figa kfree(cp); 2128aa42240aSTomasz Figa break; 2129aa42240aSTomasz Figa } 2130aa42240aSTomasz Figa } 2131aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 2132de0aa06dSJon Hunter mutex_unlock(&gpd_list_lock); 2133aa42240aSTomasz Figa } 2134aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(of_genpd_del_provider); 2135aa42240aSTomasz Figa 2136aa42240aSTomasz Figa /** 2137f58d4e5aSJon Hunter * genpd_get_from_provider() - Look-up PM domain 2138aa42240aSTomasz Figa * @genpdspec: OF phandle args to use for look-up 2139aa42240aSTomasz Figa * 2140aa42240aSTomasz Figa * Looks for a PM domain provider under the node specified by @genpdspec and if 2141aa42240aSTomasz Figa * found, uses xlate function of the provider to map phandle args to a PM 2142aa42240aSTomasz Figa * domain. 2143aa42240aSTomasz Figa * 2144aa42240aSTomasz Figa * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() 2145aa42240aSTomasz Figa * on failure. 2146aa42240aSTomasz Figa */ 2147f58d4e5aSJon Hunter static struct generic_pm_domain *genpd_get_from_provider( 2148aa42240aSTomasz Figa struct of_phandle_args *genpdspec) 2149aa42240aSTomasz Figa { 2150aa42240aSTomasz Figa struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); 2151aa42240aSTomasz Figa struct of_genpd_provider *provider; 2152aa42240aSTomasz Figa 215341795a8aSJon Hunter if (!genpdspec) 215441795a8aSJon Hunter return ERR_PTR(-EINVAL); 215541795a8aSJon Hunter 2156aa42240aSTomasz Figa mutex_lock(&of_genpd_mutex); 2157aa42240aSTomasz Figa 2158aa42240aSTomasz Figa /* Check if we have such a provider in our array */ 2159aa42240aSTomasz Figa list_for_each_entry(provider, &of_genpd_providers, link) { 2160aa42240aSTomasz Figa if (provider->node == genpdspec->np) 2161aa42240aSTomasz Figa genpd = provider->xlate(genpdspec, provider->data); 2162aa42240aSTomasz Figa if (!IS_ERR(genpd)) 2163aa42240aSTomasz Figa break; 2164aa42240aSTomasz Figa } 2165aa42240aSTomasz Figa 2166aa42240aSTomasz Figa mutex_unlock(&of_genpd_mutex); 2167aa42240aSTomasz Figa 2168aa42240aSTomasz Figa return genpd; 2169aa42240aSTomasz Figa } 2170aa42240aSTomasz Figa 2171aa42240aSTomasz Figa /** 2172ec69572bSJon Hunter * of_genpd_add_device() - Add a device to an I/O PM domain 2173ec69572bSJon Hunter * @genpdspec: OF phandle args to use for look-up PM domain 2174ec69572bSJon Hunter * @dev: Device to be added. 2175ec69572bSJon Hunter * 2176ec69572bSJon Hunter * Looks-up an I/O PM domain based upon phandle args provided and adds 2177ec69572bSJon Hunter * the device to the PM domain. Returns a negative error code on failure. 2178ec69572bSJon Hunter */ 2179ec69572bSJon Hunter int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) 2180ec69572bSJon Hunter { 2181ec69572bSJon Hunter struct generic_pm_domain *genpd; 218219efa5ffSJon Hunter int ret; 218319efa5ffSJon Hunter 218419efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2185ec69572bSJon Hunter 2186f58d4e5aSJon Hunter genpd = genpd_get_from_provider(genpdspec); 218719efa5ffSJon Hunter if (IS_ERR(genpd)) { 218819efa5ffSJon Hunter ret = PTR_ERR(genpd); 218919efa5ffSJon Hunter goto out; 219019efa5ffSJon Hunter } 2191ec69572bSJon Hunter 219219efa5ffSJon Hunter ret = genpd_add_device(genpd, dev, NULL); 219319efa5ffSJon Hunter 219419efa5ffSJon Hunter out: 219519efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 219619efa5ffSJon Hunter 219719efa5ffSJon Hunter return ret; 2198ec69572bSJon Hunter } 2199ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_device); 2200ec69572bSJon Hunter 2201ec69572bSJon Hunter /** 2202ec69572bSJon Hunter * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain. 2203ec69572bSJon Hunter * @parent_spec: OF phandle args to use for parent PM domain look-up 2204ec69572bSJon Hunter * @subdomain_spec: OF phandle args to use for subdomain look-up 2205ec69572bSJon Hunter * 2206ec69572bSJon Hunter * Looks-up a parent PM domain and subdomain based upon phandle args 2207ec69572bSJon Hunter * provided and adds the subdomain to the parent PM domain. Returns a 2208ec69572bSJon Hunter * negative error code on failure. 2209ec69572bSJon Hunter */ 2210ec69572bSJon Hunter int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, 2211ec69572bSJon Hunter struct of_phandle_args *subdomain_spec) 2212ec69572bSJon Hunter { 2213ec69572bSJon Hunter struct generic_pm_domain *parent, *subdomain; 221419efa5ffSJon Hunter int ret; 221519efa5ffSJon Hunter 221619efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2217ec69572bSJon Hunter 2218f58d4e5aSJon Hunter parent = genpd_get_from_provider(parent_spec); 221919efa5ffSJon Hunter if (IS_ERR(parent)) { 222019efa5ffSJon Hunter ret = PTR_ERR(parent); 222119efa5ffSJon Hunter goto out; 222219efa5ffSJon Hunter } 2223ec69572bSJon Hunter 2224f58d4e5aSJon Hunter subdomain = genpd_get_from_provider(subdomain_spec); 222519efa5ffSJon Hunter if (IS_ERR(subdomain)) { 222619efa5ffSJon Hunter ret = PTR_ERR(subdomain); 222719efa5ffSJon Hunter goto out; 222819efa5ffSJon Hunter } 2229ec69572bSJon Hunter 223019efa5ffSJon Hunter ret = genpd_add_subdomain(parent, subdomain); 223119efa5ffSJon Hunter 223219efa5ffSJon Hunter out: 223319efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 223419efa5ffSJon Hunter 223519efa5ffSJon Hunter return ret; 2236ec69572bSJon Hunter } 2237ec69572bSJon Hunter EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); 2238ec69572bSJon Hunter 2239ec69572bSJon Hunter /** 224017926551SJon Hunter * of_genpd_remove_last - Remove the last PM domain registered for a provider 224117926551SJon Hunter * @provider: Pointer to device structure associated with provider 224217926551SJon Hunter * 224317926551SJon Hunter * Find the last PM domain that was added by a particular provider and 224417926551SJon Hunter * remove this PM domain from the list of PM domains. The provider is 224517926551SJon Hunter * identified by the 'provider' device structure that is passed. The PM 224617926551SJon Hunter * domain will only be removed, if the provider associated with domain 224717926551SJon Hunter * has been removed. 224817926551SJon Hunter * 224917926551SJon Hunter * Returns a valid pointer to struct generic_pm_domain on success or 225017926551SJon Hunter * ERR_PTR() on failure. 225117926551SJon Hunter */ 225217926551SJon Hunter struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) 225317926551SJon Hunter { 2254a7e2d1bcSKrzysztof Kozlowski struct generic_pm_domain *gpd, *tmp, *genpd = ERR_PTR(-ENOENT); 225517926551SJon Hunter int ret; 225617926551SJon Hunter 225717926551SJon Hunter if (IS_ERR_OR_NULL(np)) 225817926551SJon Hunter return ERR_PTR(-EINVAL); 225917926551SJon Hunter 226017926551SJon Hunter mutex_lock(&gpd_list_lock); 2261a7e2d1bcSKrzysztof Kozlowski list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { 226217926551SJon Hunter if (gpd->provider == &np->fwnode) { 226317926551SJon Hunter ret = genpd_remove(gpd); 226417926551SJon Hunter genpd = ret ? ERR_PTR(ret) : gpd; 226517926551SJon Hunter break; 226617926551SJon Hunter } 226717926551SJon Hunter } 226817926551SJon Hunter mutex_unlock(&gpd_list_lock); 226917926551SJon Hunter 227017926551SJon Hunter return genpd; 227117926551SJon Hunter } 227217926551SJon Hunter EXPORT_SYMBOL_GPL(of_genpd_remove_last); 227317926551SJon Hunter 22743c095f32SUlf Hansson static void genpd_release_dev(struct device *dev) 22753c095f32SUlf Hansson { 22763c095f32SUlf Hansson kfree(dev); 22773c095f32SUlf Hansson } 22783c095f32SUlf Hansson 22793c095f32SUlf Hansson static struct bus_type genpd_bus_type = { 22803c095f32SUlf Hansson .name = "genpd", 22813c095f32SUlf Hansson }; 22823c095f32SUlf Hansson 228317926551SJon Hunter /** 2284aa42240aSTomasz Figa * genpd_dev_pm_detach - Detach a device from its PM domain. 22858bb6944eSJon Hunter * @dev: Device to detach. 2286aa42240aSTomasz Figa * @power_off: Currently not used 2287aa42240aSTomasz Figa * 2288aa42240aSTomasz Figa * Try to locate a corresponding generic PM domain, which the device was 2289aa42240aSTomasz Figa * attached to previously. If such is found, the device is detached from it. 2290aa42240aSTomasz Figa */ 2291aa42240aSTomasz Figa static void genpd_dev_pm_detach(struct device *dev, bool power_off) 2292aa42240aSTomasz Figa { 2293446d999cSRussell King struct generic_pm_domain *pd; 229493af5e93SGeert Uytterhoeven unsigned int i; 2295aa42240aSTomasz Figa int ret = 0; 2296aa42240aSTomasz Figa 229785168d56SUlf Hansson pd = dev_to_genpd(dev); 229885168d56SUlf Hansson if (IS_ERR(pd)) 2299aa42240aSTomasz Figa return; 2300aa42240aSTomasz Figa 2301aa42240aSTomasz Figa dev_dbg(dev, "removing from PM domain %s\n", pd->name); 2302aa42240aSTomasz Figa 230393af5e93SGeert Uytterhoeven for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { 230485168d56SUlf Hansson ret = genpd_remove_device(pd, dev); 2305aa42240aSTomasz Figa if (ret != -EAGAIN) 2306aa42240aSTomasz Figa break; 230793af5e93SGeert Uytterhoeven 230893af5e93SGeert Uytterhoeven mdelay(i); 2309aa42240aSTomasz Figa cond_resched(); 2310aa42240aSTomasz Figa } 2311aa42240aSTomasz Figa 2312aa42240aSTomasz Figa if (ret < 0) { 2313aa42240aSTomasz Figa dev_err(dev, "failed to remove from PM domain %s: %d", 2314aa42240aSTomasz Figa pd->name, ret); 2315aa42240aSTomasz Figa return; 2316aa42240aSTomasz Figa } 2317aa42240aSTomasz Figa 2318aa42240aSTomasz Figa /* Check if PM domain can be powered off after removing this device. */ 2319aa42240aSTomasz Figa genpd_queue_power_off_work(pd); 23203c095f32SUlf Hansson 23213c095f32SUlf Hansson /* Unregister the device if it was created by genpd. */ 23223c095f32SUlf Hansson if (dev->bus == &genpd_bus_type) 23233c095f32SUlf Hansson device_unregister(dev); 2324aa42240aSTomasz Figa } 2325aa42240aSTomasz Figa 2326632f7ce3SRussell King static void genpd_dev_pm_sync(struct device *dev) 2327632f7ce3SRussell King { 2328632f7ce3SRussell King struct generic_pm_domain *pd; 2329632f7ce3SRussell King 2330632f7ce3SRussell King pd = dev_to_genpd(dev); 2331632f7ce3SRussell King if (IS_ERR(pd)) 2332632f7ce3SRussell King return; 2333632f7ce3SRussell King 2334632f7ce3SRussell King genpd_queue_power_off_work(pd); 2335632f7ce3SRussell King } 2336632f7ce3SRussell King 23378cb1cbd6SUlf Hansson static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np, 2338895b6612SUlf Hansson unsigned int index, bool power_on) 2339aa42240aSTomasz Figa { 2340aa42240aSTomasz Figa struct of_phandle_args pd_args; 2341aa42240aSTomasz Figa struct generic_pm_domain *pd; 2342aa42240aSTomasz Figa int ret; 2343aa42240aSTomasz Figa 23448cb1cbd6SUlf Hansson ret = of_parse_phandle_with_args(np, "power-domains", 23458cb1cbd6SUlf Hansson "#power-domain-cells", index, &pd_args); 2346001d50c9SGeert Uytterhoeven if (ret < 0) 2347bcd931f2SUlf Hansson return ret; 2348aa42240aSTomasz Figa 234919efa5ffSJon Hunter mutex_lock(&gpd_list_lock); 2350f58d4e5aSJon Hunter pd = genpd_get_from_provider(&pd_args); 2351265e2cf6SEric Anholt of_node_put(pd_args.np); 2352aa42240aSTomasz Figa if (IS_ERR(pd)) { 235319efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2354aa42240aSTomasz Figa dev_dbg(dev, "%s() failed to find PM domain: %ld\n", 2355aa42240aSTomasz Figa __func__, PTR_ERR(pd)); 2356e01afc32SRob Herring return driver_deferred_probe_check_state(dev); 2357aa42240aSTomasz Figa } 2358aa42240aSTomasz Figa 2359aa42240aSTomasz Figa dev_dbg(dev, "adding to PM domain %s\n", pd->name); 2360aa42240aSTomasz Figa 236119efa5ffSJon Hunter ret = genpd_add_device(pd, dev, NULL); 236219efa5ffSJon Hunter mutex_unlock(&gpd_list_lock); 2363aa42240aSTomasz Figa 2364aa42240aSTomasz Figa if (ret < 0) { 236534994692SGeert Uytterhoeven if (ret != -EPROBE_DEFER) 2366aa42240aSTomasz Figa dev_err(dev, "failed to add to PM domain %s: %d", 2367aa42240aSTomasz Figa pd->name, ret); 2368919b7308SUlf Hansson return ret; 2369aa42240aSTomasz Figa } 2370aa42240aSTomasz Figa 2371aa42240aSTomasz Figa dev->pm_domain->detach = genpd_dev_pm_detach; 2372632f7ce3SRussell King dev->pm_domain->sync = genpd_dev_pm_sync; 2373aa42240aSTomasz Figa 2374895b6612SUlf Hansson if (power_on) { 237535241d12SLina Iyer genpd_lock(pd); 237686e12eacSUlf Hansson ret = genpd_power_on(pd, 0); 237735241d12SLina Iyer genpd_unlock(pd); 2378895b6612SUlf Hansson } 237972038df3SUlf Hansson 238072038df3SUlf Hansson if (ret) 238172038df3SUlf Hansson genpd_remove_device(pd, dev); 2382919b7308SUlf Hansson 2383919b7308SUlf Hansson return ret ? -EPROBE_DEFER : 1; 2384aa42240aSTomasz Figa } 23858cb1cbd6SUlf Hansson 23868cb1cbd6SUlf Hansson /** 23878cb1cbd6SUlf Hansson * genpd_dev_pm_attach - Attach a device to its PM domain using DT. 23888cb1cbd6SUlf Hansson * @dev: Device to attach. 23898cb1cbd6SUlf Hansson * 23908cb1cbd6SUlf Hansson * Parse device's OF node to find a PM domain specifier. If such is found, 23918cb1cbd6SUlf Hansson * attaches the device to retrieved pm_domain ops. 23928cb1cbd6SUlf Hansson * 23938cb1cbd6SUlf Hansson * Returns 1 on successfully attached PM domain, 0 when the device don't need a 23948cb1cbd6SUlf Hansson * PM domain or when multiple power-domains exists for it, else a negative error 23958cb1cbd6SUlf Hansson * code. Note that if a power-domain exists for the device, but it cannot be 23968cb1cbd6SUlf Hansson * found or turned on, then return -EPROBE_DEFER to ensure that the device is 23978cb1cbd6SUlf Hansson * not probed and to re-try again later. 23988cb1cbd6SUlf Hansson */ 23998cb1cbd6SUlf Hansson int genpd_dev_pm_attach(struct device *dev) 24008cb1cbd6SUlf Hansson { 24018cb1cbd6SUlf Hansson if (!dev->of_node) 24028cb1cbd6SUlf Hansson return 0; 24038cb1cbd6SUlf Hansson 24048cb1cbd6SUlf Hansson /* 24058cb1cbd6SUlf Hansson * Devices with multiple PM domains must be attached separately, as we 24068cb1cbd6SUlf Hansson * can only attach one PM domain per device. 24078cb1cbd6SUlf Hansson */ 24088cb1cbd6SUlf Hansson if (of_count_phandle_with_args(dev->of_node, "power-domains", 24098cb1cbd6SUlf Hansson "#power-domain-cells") != 1) 24108cb1cbd6SUlf Hansson return 0; 24118cb1cbd6SUlf Hansson 2412895b6612SUlf Hansson return __genpd_dev_pm_attach(dev, dev->of_node, 0, true); 24138cb1cbd6SUlf Hansson } 2414aa42240aSTomasz Figa EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 241530f60428SLina Iyer 24163c095f32SUlf Hansson /** 24173c095f32SUlf Hansson * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains. 24183c095f32SUlf Hansson * @dev: The device used to lookup the PM domain. 24193c095f32SUlf Hansson * @index: The index of the PM domain. 24203c095f32SUlf Hansson * 24213c095f32SUlf Hansson * Parse device's OF node to find a PM domain specifier at the provided @index. 24223c095f32SUlf Hansson * If such is found, creates a virtual device and attaches it to the retrieved 24233c095f32SUlf Hansson * pm_domain ops. To deal with detaching of the virtual device, the ->detach() 24243c095f32SUlf Hansson * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach(). 24253c095f32SUlf Hansson * 24263c095f32SUlf Hansson * Returns the created virtual device if successfully attached PM domain, NULL 24273c095f32SUlf Hansson * when the device don't need a PM domain, else an ERR_PTR() in case of 24283c095f32SUlf Hansson * failures. If a power-domain exists for the device, but cannot be found or 24293c095f32SUlf Hansson * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device 24303c095f32SUlf Hansson * is not probed and to re-try again later. 24313c095f32SUlf Hansson */ 24323c095f32SUlf Hansson struct device *genpd_dev_pm_attach_by_id(struct device *dev, 24333c095f32SUlf Hansson unsigned int index) 24343c095f32SUlf Hansson { 2435560928b2SViresh Kumar struct device *virt_dev; 24363c095f32SUlf Hansson int num_domains; 24373c095f32SUlf Hansson int ret; 24383c095f32SUlf Hansson 24393c095f32SUlf Hansson if (!dev->of_node) 24403c095f32SUlf Hansson return NULL; 24413c095f32SUlf Hansson 24423c095f32SUlf Hansson /* Deal only with devices using multiple PM domains. */ 24433c095f32SUlf Hansson num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", 24443c095f32SUlf Hansson "#power-domain-cells"); 24453c095f32SUlf Hansson if (num_domains < 2 || index >= num_domains) 24463c095f32SUlf Hansson return NULL; 24473c095f32SUlf Hansson 24483c095f32SUlf Hansson /* Allocate and register device on the genpd bus. */ 2449560928b2SViresh Kumar virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); 2450560928b2SViresh Kumar if (!virt_dev) 24513c095f32SUlf Hansson return ERR_PTR(-ENOMEM); 24523c095f32SUlf Hansson 2453560928b2SViresh Kumar dev_set_name(virt_dev, "genpd:%u:%s", index, dev_name(dev)); 2454560928b2SViresh Kumar virt_dev->bus = &genpd_bus_type; 2455560928b2SViresh Kumar virt_dev->release = genpd_release_dev; 24563c095f32SUlf Hansson 2457560928b2SViresh Kumar ret = device_register(virt_dev); 24583c095f32SUlf Hansson if (ret) { 2459560928b2SViresh Kumar kfree(virt_dev); 24603c095f32SUlf Hansson return ERR_PTR(ret); 24613c095f32SUlf Hansson } 24623c095f32SUlf Hansson 24633c095f32SUlf Hansson /* Try to attach the device to the PM domain at the specified index. */ 2464560928b2SViresh Kumar ret = __genpd_dev_pm_attach(virt_dev, dev->of_node, index, false); 24653c095f32SUlf Hansson if (ret < 1) { 2466560928b2SViresh Kumar device_unregister(virt_dev); 24673c095f32SUlf Hansson return ret ? ERR_PTR(ret) : NULL; 24683c095f32SUlf Hansson } 24693c095f32SUlf Hansson 2470560928b2SViresh Kumar pm_runtime_enable(virt_dev); 2471560928b2SViresh Kumar genpd_queue_power_off_work(dev_to_genpd(virt_dev)); 24723c095f32SUlf Hansson 2473560928b2SViresh Kumar return virt_dev; 24743c095f32SUlf Hansson } 24753c095f32SUlf Hansson EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id); 24763c095f32SUlf Hansson 24775d6be70aSUlf Hansson /** 24785d6be70aSUlf Hansson * genpd_dev_pm_attach_by_name - Associate a device with one of its PM domains. 24795d6be70aSUlf Hansson * @dev: The device used to lookup the PM domain. 24805d6be70aSUlf Hansson * @name: The name of the PM domain. 24815d6be70aSUlf Hansson * 24825d6be70aSUlf Hansson * Parse device's OF node to find a PM domain specifier using the 24835d6be70aSUlf Hansson * power-domain-names DT property. For further description see 24845d6be70aSUlf Hansson * genpd_dev_pm_attach_by_id(). 24855d6be70aSUlf Hansson */ 24867416f1f2SDouglas Anderson struct device *genpd_dev_pm_attach_by_name(struct device *dev, const char *name) 24875d6be70aSUlf Hansson { 24885d6be70aSUlf Hansson int index; 24895d6be70aSUlf Hansson 24905d6be70aSUlf Hansson if (!dev->of_node) 24915d6be70aSUlf Hansson return NULL; 24925d6be70aSUlf Hansson 24935d6be70aSUlf Hansson index = of_property_match_string(dev->of_node, "power-domain-names", 24945d6be70aSUlf Hansson name); 24955d6be70aSUlf Hansson if (index < 0) 24965d6be70aSUlf Hansson return NULL; 24975d6be70aSUlf Hansson 24985d6be70aSUlf Hansson return genpd_dev_pm_attach_by_id(dev, index); 24995d6be70aSUlf Hansson } 25005d6be70aSUlf Hansson 250130f60428SLina Iyer static const struct of_device_id idle_state_match[] = { 2502598da548SLina Iyer { .compatible = "domain-idle-state", }, 250330f60428SLina Iyer { } 250430f60428SLina Iyer }; 250530f60428SLina Iyer 250630f60428SLina Iyer static int genpd_parse_state(struct genpd_power_state *genpd_state, 250730f60428SLina Iyer struct device_node *state_node) 250830f60428SLina Iyer { 250930f60428SLina Iyer int err; 251030f60428SLina Iyer u32 residency; 251130f60428SLina Iyer u32 entry_latency, exit_latency; 251230f60428SLina Iyer 251330f60428SLina Iyer err = of_property_read_u32(state_node, "entry-latency-us", 251430f60428SLina Iyer &entry_latency); 251530f60428SLina Iyer if (err) { 2516ea11e94bSRob Herring pr_debug(" * %pOF missing entry-latency-us property\n", 2517ea11e94bSRob Herring state_node); 251830f60428SLina Iyer return -EINVAL; 251930f60428SLina Iyer } 252030f60428SLina Iyer 252130f60428SLina Iyer err = of_property_read_u32(state_node, "exit-latency-us", 252230f60428SLina Iyer &exit_latency); 252330f60428SLina Iyer if (err) { 2524ea11e94bSRob Herring pr_debug(" * %pOF missing exit-latency-us property\n", 2525ea11e94bSRob Herring state_node); 252630f60428SLina Iyer return -EINVAL; 252730f60428SLina Iyer } 252830f60428SLina Iyer 252930f60428SLina Iyer err = of_property_read_u32(state_node, "min-residency-us", &residency); 253030f60428SLina Iyer if (!err) 253130f60428SLina Iyer genpd_state->residency_ns = 1000 * residency; 253230f60428SLina Iyer 253330f60428SLina Iyer genpd_state->power_on_latency_ns = 1000 * exit_latency; 253430f60428SLina Iyer genpd_state->power_off_latency_ns = 1000 * entry_latency; 25350c9b694aSLina Iyer genpd_state->fwnode = &state_node->fwnode; 253630f60428SLina Iyer 253730f60428SLina Iyer return 0; 253830f60428SLina Iyer } 253930f60428SLina Iyer 2540a3381e3aSUlf Hansson static int genpd_iterate_idle_states(struct device_node *dn, 2541a3381e3aSUlf Hansson struct genpd_power_state *states) 2542a3381e3aSUlf Hansson { 2543a3381e3aSUlf Hansson int ret; 2544a3381e3aSUlf Hansson struct of_phandle_iterator it; 2545a3381e3aSUlf Hansson struct device_node *np; 2546a3381e3aSUlf Hansson int i = 0; 2547a3381e3aSUlf Hansson 2548a3381e3aSUlf Hansson ret = of_count_phandle_with_args(dn, "domain-idle-states", NULL); 2549a3381e3aSUlf Hansson if (ret <= 0) 2550a3381e3aSUlf Hansson return ret; 2551a3381e3aSUlf Hansson 2552a3381e3aSUlf Hansson /* Loop over the phandles until all the requested entry is found */ 2553a3381e3aSUlf Hansson of_for_each_phandle(&it, ret, dn, "domain-idle-states", NULL, 0) { 2554a3381e3aSUlf Hansson np = it.node; 2555a3381e3aSUlf Hansson if (!of_match_node(idle_state_match, np)) 2556a3381e3aSUlf Hansson continue; 2557a3381e3aSUlf Hansson if (states) { 2558a3381e3aSUlf Hansson ret = genpd_parse_state(&states[i], np); 2559a3381e3aSUlf Hansson if (ret) { 2560a3381e3aSUlf Hansson pr_err("Parsing idle state node %pOF failed with err %d\n", 2561a3381e3aSUlf Hansson np, ret); 2562a3381e3aSUlf Hansson of_node_put(np); 2563a3381e3aSUlf Hansson return ret; 2564a3381e3aSUlf Hansson } 2565a3381e3aSUlf Hansson } 2566a3381e3aSUlf Hansson i++; 2567a3381e3aSUlf Hansson } 2568a3381e3aSUlf Hansson 2569a3381e3aSUlf Hansson return i; 2570a3381e3aSUlf Hansson } 2571a3381e3aSUlf Hansson 257230f60428SLina Iyer /** 257330f60428SLina Iyer * of_genpd_parse_idle_states: Return array of idle states for the genpd. 257430f60428SLina Iyer * 257530f60428SLina Iyer * @dn: The genpd device node 257630f60428SLina Iyer * @states: The pointer to which the state array will be saved. 257730f60428SLina Iyer * @n: The count of elements in the array returned from this function. 257830f60428SLina Iyer * 257930f60428SLina Iyer * Returns the device states parsed from the OF node. The memory for the states 258030f60428SLina Iyer * is allocated by this function and is the responsibility of the caller to 25812c361684SUlf Hansson * free the memory after use. If any or zero compatible domain idle states is 25822c361684SUlf Hansson * found it returns 0 and in case of errors, a negative error code is returned. 258330f60428SLina Iyer */ 258430f60428SLina Iyer int of_genpd_parse_idle_states(struct device_node *dn, 258530f60428SLina Iyer struct genpd_power_state **states, int *n) 258630f60428SLina Iyer { 258730f60428SLina Iyer struct genpd_power_state *st; 2588a3381e3aSUlf Hansson int ret; 258930f60428SLina Iyer 2590a3381e3aSUlf Hansson ret = genpd_iterate_idle_states(dn, NULL); 25912c361684SUlf Hansson if (ret < 0) 25922c361684SUlf Hansson return ret; 25932c361684SUlf Hansson 25942c361684SUlf Hansson if (!ret) { 25952c361684SUlf Hansson *states = NULL; 25962c361684SUlf Hansson *n = 0; 25972c361684SUlf Hansson return 0; 25982c361684SUlf Hansson } 259930f60428SLina Iyer 2600a3381e3aSUlf Hansson st = kcalloc(ret, sizeof(*st), GFP_KERNEL); 260130f60428SLina Iyer if (!st) 260230f60428SLina Iyer return -ENOMEM; 260330f60428SLina Iyer 2604a3381e3aSUlf Hansson ret = genpd_iterate_idle_states(dn, st); 2605a3381e3aSUlf Hansson if (ret <= 0) { 260630f60428SLina Iyer kfree(st); 2607a3381e3aSUlf Hansson return ret < 0 ? ret : -EINVAL; 260830f60428SLina Iyer } 260930f60428SLina Iyer 261030f60428SLina Iyer *states = st; 2611a3381e3aSUlf Hansson *n = ret; 261230f60428SLina Iyer 261330f60428SLina Iyer return 0; 261430f60428SLina Iyer } 261530f60428SLina Iyer EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); 261630f60428SLina Iyer 26176e41766aSViresh Kumar /** 2618e38f89d3SViresh Kumar * pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node. 2619e38f89d3SViresh Kumar * 2620e38f89d3SViresh Kumar * @genpd_dev: Genpd's device for which the performance-state needs to be found. 2621e38f89d3SViresh Kumar * @opp: struct dev_pm_opp of the OPP for which we need to find performance 2622e38f89d3SViresh Kumar * state. 2623e38f89d3SViresh Kumar * 2624e38f89d3SViresh Kumar * Returns performance state encoded in the OPP of the genpd. This calls 2625e38f89d3SViresh Kumar * platform specific genpd->opp_to_performance_state() callback to translate 2626e38f89d3SViresh Kumar * power domain OPP to performance state. 2627e38f89d3SViresh Kumar * 2628e38f89d3SViresh Kumar * Returns performance state on success and 0 on failure. 2629e38f89d3SViresh Kumar */ 2630e38f89d3SViresh Kumar unsigned int pm_genpd_opp_to_performance_state(struct device *genpd_dev, 2631e38f89d3SViresh Kumar struct dev_pm_opp *opp) 2632e38f89d3SViresh Kumar { 2633e38f89d3SViresh Kumar struct generic_pm_domain *genpd = NULL; 2634e38f89d3SViresh Kumar int state; 2635e38f89d3SViresh Kumar 2636e38f89d3SViresh Kumar genpd = container_of(genpd_dev, struct generic_pm_domain, dev); 2637e38f89d3SViresh Kumar 2638e38f89d3SViresh Kumar if (unlikely(!genpd->opp_to_performance_state)) 2639e38f89d3SViresh Kumar return 0; 2640e38f89d3SViresh Kumar 2641e38f89d3SViresh Kumar genpd_lock(genpd); 2642e38f89d3SViresh Kumar state = genpd->opp_to_performance_state(genpd, opp); 2643e38f89d3SViresh Kumar genpd_unlock(genpd); 2644e38f89d3SViresh Kumar 2645e38f89d3SViresh Kumar return state; 2646e38f89d3SViresh Kumar } 2647e38f89d3SViresh Kumar EXPORT_SYMBOL_GPL(pm_genpd_opp_to_performance_state); 2648e38f89d3SViresh Kumar 26493c095f32SUlf Hansson static int __init genpd_bus_init(void) 26503c095f32SUlf Hansson { 26513c095f32SUlf Hansson return bus_register(&genpd_bus_type); 26523c095f32SUlf Hansson } 26533c095f32SUlf Hansson core_initcall(genpd_bus_init); 26543c095f32SUlf Hansson 2655d30d819dSRafael J. Wysocki #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ 26562bd5306aSMaciej Matraszek 26572bd5306aSMaciej Matraszek 26582bd5306aSMaciej Matraszek /*** debugfs support ***/ 26592bd5306aSMaciej Matraszek 26608b0510b5SJon Hunter #ifdef CONFIG_DEBUG_FS 26612bd5306aSMaciej Matraszek #include <linux/pm.h> 26622bd5306aSMaciej Matraszek #include <linux/device.h> 26632bd5306aSMaciej Matraszek #include <linux/debugfs.h> 26642bd5306aSMaciej Matraszek #include <linux/seq_file.h> 26652bd5306aSMaciej Matraszek #include <linux/init.h> 26662bd5306aSMaciej Matraszek #include <linux/kobject.h> 26679e9704eaSUlf Hansson static struct dentry *genpd_debugfs_dir; 26682bd5306aSMaciej Matraszek 26692bd5306aSMaciej Matraszek /* 26702bd5306aSMaciej Matraszek * TODO: This function is a slightly modified version of rtpm_status_show 2671d30d819dSRafael J. Wysocki * from sysfs.c, so generalize it. 26722bd5306aSMaciej Matraszek */ 26732bd5306aSMaciej Matraszek static void rtpm_status_str(struct seq_file *s, struct device *dev) 26742bd5306aSMaciej Matraszek { 26752bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 26762bd5306aSMaciej Matraszek [RPM_ACTIVE] = "active", 26772bd5306aSMaciej Matraszek [RPM_RESUMING] = "resuming", 26782bd5306aSMaciej Matraszek [RPM_SUSPENDED] = "suspended", 26792bd5306aSMaciej Matraszek [RPM_SUSPENDING] = "suspending" 26802bd5306aSMaciej Matraszek }; 26812bd5306aSMaciej Matraszek const char *p = ""; 26822bd5306aSMaciej Matraszek 26832bd5306aSMaciej Matraszek if (dev->power.runtime_error) 26842bd5306aSMaciej Matraszek p = "error"; 26852bd5306aSMaciej Matraszek else if (dev->power.disable_depth) 26862bd5306aSMaciej Matraszek p = "unsupported"; 26872bd5306aSMaciej Matraszek else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) 26882bd5306aSMaciej Matraszek p = status_lookup[dev->power.runtime_status]; 26892bd5306aSMaciej Matraszek else 26902bd5306aSMaciej Matraszek WARN_ON(1); 26912bd5306aSMaciej Matraszek 26922bd5306aSMaciej Matraszek seq_puts(s, p); 26932bd5306aSMaciej Matraszek } 26942bd5306aSMaciej Matraszek 26959e9704eaSUlf Hansson static int genpd_summary_one(struct seq_file *s, 269666a5ca4bSKevin Hilman struct generic_pm_domain *genpd) 26972bd5306aSMaciej Matraszek { 26982bd5306aSMaciej Matraszek static const char * const status_lookup[] = { 26992bd5306aSMaciej Matraszek [GPD_STATE_ACTIVE] = "on", 27002bd5306aSMaciej Matraszek [GPD_STATE_POWER_OFF] = "off" 27012bd5306aSMaciej Matraszek }; 27022bd5306aSMaciej Matraszek struct pm_domain_data *pm_data; 27032bd5306aSMaciej Matraszek const char *kobj_path; 27042bd5306aSMaciej Matraszek struct gpd_link *link; 27056954d432SGeert Uytterhoeven char state[16]; 27062bd5306aSMaciej Matraszek int ret; 27072bd5306aSMaciej Matraszek 270835241d12SLina Iyer ret = genpd_lock_interruptible(genpd); 27092bd5306aSMaciej Matraszek if (ret) 27102bd5306aSMaciej Matraszek return -ERESTARTSYS; 27112bd5306aSMaciej Matraszek 271266a5ca4bSKevin Hilman if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) 27132bd5306aSMaciej Matraszek goto exit; 271441e2c8e0SUlf Hansson if (!genpd_status_on(genpd)) 27150ba554e4SGeert Uytterhoeven snprintf(state, sizeof(state), "%s-%u", 27166954d432SGeert Uytterhoeven status_lookup[genpd->status], genpd->state_idx); 2717fc5cbf0cSAxel Haslam else 27186954d432SGeert Uytterhoeven snprintf(state, sizeof(state), "%s", 27196954d432SGeert Uytterhoeven status_lookup[genpd->status]); 27206954d432SGeert Uytterhoeven seq_printf(s, "%-30s %-15s ", genpd->name, state); 27212bd5306aSMaciej Matraszek 27222bd5306aSMaciej Matraszek /* 27232bd5306aSMaciej Matraszek * Modifications on the list require holding locks on both 27242bd5306aSMaciej Matraszek * master and slave, so we are safe. 272566a5ca4bSKevin Hilman * Also genpd->name is immutable. 27262bd5306aSMaciej Matraszek */ 272766a5ca4bSKevin Hilman list_for_each_entry(link, &genpd->master_links, master_node) { 27282bd5306aSMaciej Matraszek seq_printf(s, "%s", link->slave->name); 272966a5ca4bSKevin Hilman if (!list_is_last(&link->master_node, &genpd->master_links)) 27302bd5306aSMaciej Matraszek seq_puts(s, ", "); 27312bd5306aSMaciej Matraszek } 27322bd5306aSMaciej Matraszek 273366a5ca4bSKevin Hilman list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2734d716f479SLina Iyer kobj_path = kobject_get_path(&pm_data->dev->kobj, 2735d716f479SLina Iyer genpd_is_irq_safe(genpd) ? 2736d716f479SLina Iyer GFP_ATOMIC : GFP_KERNEL); 27372bd5306aSMaciej Matraszek if (kobj_path == NULL) 27382bd5306aSMaciej Matraszek continue; 27392bd5306aSMaciej Matraszek 27402bd5306aSMaciej Matraszek seq_printf(s, "\n %-50s ", kobj_path); 27412bd5306aSMaciej Matraszek rtpm_status_str(s, pm_data->dev); 27422bd5306aSMaciej Matraszek kfree(kobj_path); 27432bd5306aSMaciej Matraszek } 27442bd5306aSMaciej Matraszek 27452bd5306aSMaciej Matraszek seq_puts(s, "\n"); 27462bd5306aSMaciej Matraszek exit: 274735241d12SLina Iyer genpd_unlock(genpd); 27482bd5306aSMaciej Matraszek 27492bd5306aSMaciej Matraszek return 0; 27502bd5306aSMaciej Matraszek } 27512bd5306aSMaciej Matraszek 2752d32dcc6cSYangtao Li static int summary_show(struct seq_file *s, void *data) 27532bd5306aSMaciej Matraszek { 275466a5ca4bSKevin Hilman struct generic_pm_domain *genpd; 27552bd5306aSMaciej Matraszek int ret = 0; 27562bd5306aSMaciej Matraszek 27572bd5306aSMaciej Matraszek seq_puts(s, "domain status slaves\n"); 27582bd5306aSMaciej Matraszek seq_puts(s, " /device runtime status\n"); 27592bd5306aSMaciej Matraszek seq_puts(s, "----------------------------------------------------------------------\n"); 27602bd5306aSMaciej Matraszek 27612bd5306aSMaciej Matraszek ret = mutex_lock_interruptible(&gpd_list_lock); 27622bd5306aSMaciej Matraszek if (ret) 27632bd5306aSMaciej Matraszek return -ERESTARTSYS; 27642bd5306aSMaciej Matraszek 276566a5ca4bSKevin Hilman list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 27669e9704eaSUlf Hansson ret = genpd_summary_one(s, genpd); 27672bd5306aSMaciej Matraszek if (ret) 27682bd5306aSMaciej Matraszek break; 27692bd5306aSMaciej Matraszek } 27702bd5306aSMaciej Matraszek mutex_unlock(&gpd_list_lock); 27712bd5306aSMaciej Matraszek 27722bd5306aSMaciej Matraszek return ret; 27732bd5306aSMaciej Matraszek } 27742bd5306aSMaciej Matraszek 2775d32dcc6cSYangtao Li static int status_show(struct seq_file *s, void *data) 27762bd5306aSMaciej Matraszek { 2777b6a1d093SThara Gopinath static const char * const status_lookup[] = { 2778b6a1d093SThara Gopinath [GPD_STATE_ACTIVE] = "on", 2779b6a1d093SThara Gopinath [GPD_STATE_POWER_OFF] = "off" 2780b6a1d093SThara Gopinath }; 2781b6a1d093SThara Gopinath 2782b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2783b6a1d093SThara Gopinath int ret = 0; 2784b6a1d093SThara Gopinath 2785b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2786b6a1d093SThara Gopinath if (ret) 2787b6a1d093SThara Gopinath return -ERESTARTSYS; 2788b6a1d093SThara Gopinath 2789b6a1d093SThara Gopinath if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup))) 2790b6a1d093SThara Gopinath goto exit; 2791b6a1d093SThara Gopinath 2792b6a1d093SThara Gopinath if (genpd->status == GPD_STATE_POWER_OFF) 2793b6a1d093SThara Gopinath seq_printf(s, "%s-%u\n", status_lookup[genpd->status], 2794b6a1d093SThara Gopinath genpd->state_idx); 2795b6a1d093SThara Gopinath else 2796b6a1d093SThara Gopinath seq_printf(s, "%s\n", status_lookup[genpd->status]); 2797b6a1d093SThara Gopinath exit: 2798b6a1d093SThara Gopinath genpd_unlock(genpd); 2799b6a1d093SThara Gopinath return ret; 28002bd5306aSMaciej Matraszek } 28012bd5306aSMaciej Matraszek 2802d32dcc6cSYangtao Li static int sub_domains_show(struct seq_file *s, void *data) 2803b6a1d093SThara Gopinath { 2804b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2805b6a1d093SThara Gopinath struct gpd_link *link; 2806b6a1d093SThara Gopinath int ret = 0; 2807b6a1d093SThara Gopinath 2808b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2809b6a1d093SThara Gopinath if (ret) 2810b6a1d093SThara Gopinath return -ERESTARTSYS; 2811b6a1d093SThara Gopinath 2812b6a1d093SThara Gopinath list_for_each_entry(link, &genpd->master_links, master_node) 2813b6a1d093SThara Gopinath seq_printf(s, "%s\n", link->slave->name); 2814b6a1d093SThara Gopinath 2815b6a1d093SThara Gopinath genpd_unlock(genpd); 2816b6a1d093SThara Gopinath return ret; 2817b6a1d093SThara Gopinath } 2818b6a1d093SThara Gopinath 2819d32dcc6cSYangtao Li static int idle_states_show(struct seq_file *s, void *data) 2820b6a1d093SThara Gopinath { 2821b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2822b6a1d093SThara Gopinath unsigned int i; 2823b6a1d093SThara Gopinath int ret = 0; 2824b6a1d093SThara Gopinath 2825b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2826b6a1d093SThara Gopinath if (ret) 2827b6a1d093SThara Gopinath return -ERESTARTSYS; 2828b6a1d093SThara Gopinath 2829b6a1d093SThara Gopinath seq_puts(s, "State Time Spent(ms)\n"); 2830b6a1d093SThara Gopinath 2831b6a1d093SThara Gopinath for (i = 0; i < genpd->state_count; i++) { 2832b6a1d093SThara Gopinath ktime_t delta = 0; 2833b6a1d093SThara Gopinath s64 msecs; 2834b6a1d093SThara Gopinath 2835b6a1d093SThara Gopinath if ((genpd->status == GPD_STATE_POWER_OFF) && 2836b6a1d093SThara Gopinath (genpd->state_idx == i)) 2837b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 2838b6a1d093SThara Gopinath 2839b6a1d093SThara Gopinath msecs = ktime_to_ms( 2840b6a1d093SThara Gopinath ktime_add(genpd->states[i].idle_time, delta)); 2841b6a1d093SThara Gopinath seq_printf(s, "S%-13i %lld\n", i, msecs); 2842b6a1d093SThara Gopinath } 2843b6a1d093SThara Gopinath 2844b6a1d093SThara Gopinath genpd_unlock(genpd); 2845b6a1d093SThara Gopinath return ret; 2846b6a1d093SThara Gopinath } 2847b6a1d093SThara Gopinath 2848d32dcc6cSYangtao Li static int active_time_show(struct seq_file *s, void *data) 2849b6a1d093SThara Gopinath { 2850b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2851b6a1d093SThara Gopinath ktime_t delta = 0; 2852b6a1d093SThara Gopinath int ret = 0; 2853b6a1d093SThara Gopinath 2854b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2855b6a1d093SThara Gopinath if (ret) 2856b6a1d093SThara Gopinath return -ERESTARTSYS; 2857b6a1d093SThara Gopinath 2858b6a1d093SThara Gopinath if (genpd->status == GPD_STATE_ACTIVE) 2859b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 2860b6a1d093SThara Gopinath 2861b6a1d093SThara Gopinath seq_printf(s, "%lld ms\n", ktime_to_ms( 2862b6a1d093SThara Gopinath ktime_add(genpd->on_time, delta))); 2863b6a1d093SThara Gopinath 2864b6a1d093SThara Gopinath genpd_unlock(genpd); 2865b6a1d093SThara Gopinath return ret; 2866b6a1d093SThara Gopinath } 2867b6a1d093SThara Gopinath 2868d32dcc6cSYangtao Li static int total_idle_time_show(struct seq_file *s, void *data) 2869b6a1d093SThara Gopinath { 2870b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2871b6a1d093SThara Gopinath ktime_t delta = 0, total = 0; 2872b6a1d093SThara Gopinath unsigned int i; 2873b6a1d093SThara Gopinath int ret = 0; 2874b6a1d093SThara Gopinath 2875b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2876b6a1d093SThara Gopinath if (ret) 2877b6a1d093SThara Gopinath return -ERESTARTSYS; 2878b6a1d093SThara Gopinath 2879b6a1d093SThara Gopinath for (i = 0; i < genpd->state_count; i++) { 2880b6a1d093SThara Gopinath 2881b6a1d093SThara Gopinath if ((genpd->status == GPD_STATE_POWER_OFF) && 2882b6a1d093SThara Gopinath (genpd->state_idx == i)) 2883b6a1d093SThara Gopinath delta = ktime_sub(ktime_get(), genpd->accounting_time); 2884b6a1d093SThara Gopinath 2885b6a1d093SThara Gopinath total = ktime_add(total, genpd->states[i].idle_time); 2886b6a1d093SThara Gopinath } 2887b6a1d093SThara Gopinath total = ktime_add(total, delta); 2888b6a1d093SThara Gopinath 2889b6a1d093SThara Gopinath seq_printf(s, "%lld ms\n", ktime_to_ms(total)); 2890b6a1d093SThara Gopinath 2891b6a1d093SThara Gopinath genpd_unlock(genpd); 2892b6a1d093SThara Gopinath return ret; 2893b6a1d093SThara Gopinath } 2894b6a1d093SThara Gopinath 2895b6a1d093SThara Gopinath 2896d32dcc6cSYangtao Li static int devices_show(struct seq_file *s, void *data) 2897b6a1d093SThara Gopinath { 2898b6a1d093SThara Gopinath struct generic_pm_domain *genpd = s->private; 2899b6a1d093SThara Gopinath struct pm_domain_data *pm_data; 2900b6a1d093SThara Gopinath const char *kobj_path; 2901b6a1d093SThara Gopinath int ret = 0; 2902b6a1d093SThara Gopinath 2903b6a1d093SThara Gopinath ret = genpd_lock_interruptible(genpd); 2904b6a1d093SThara Gopinath if (ret) 2905b6a1d093SThara Gopinath return -ERESTARTSYS; 2906b6a1d093SThara Gopinath 2907b6a1d093SThara Gopinath list_for_each_entry(pm_data, &genpd->dev_list, list_node) { 2908b6a1d093SThara Gopinath kobj_path = kobject_get_path(&pm_data->dev->kobj, 2909b6a1d093SThara Gopinath genpd_is_irq_safe(genpd) ? 2910b6a1d093SThara Gopinath GFP_ATOMIC : GFP_KERNEL); 2911b6a1d093SThara Gopinath if (kobj_path == NULL) 2912b6a1d093SThara Gopinath continue; 2913b6a1d093SThara Gopinath 2914b6a1d093SThara Gopinath seq_printf(s, "%s\n", kobj_path); 2915b6a1d093SThara Gopinath kfree(kobj_path); 2916b6a1d093SThara Gopinath } 2917b6a1d093SThara Gopinath 2918b6a1d093SThara Gopinath genpd_unlock(genpd); 2919b6a1d093SThara Gopinath return ret; 2920b6a1d093SThara Gopinath } 2921b6a1d093SThara Gopinath 2922d32dcc6cSYangtao Li static int perf_state_show(struct seq_file *s, void *data) 2923e8912812SRajendra Nayak { 2924e8912812SRajendra Nayak struct generic_pm_domain *genpd = s->private; 2925e8912812SRajendra Nayak 2926e8912812SRajendra Nayak if (genpd_lock_interruptible(genpd)) 2927e8912812SRajendra Nayak return -ERESTARTSYS; 2928e8912812SRajendra Nayak 2929e8912812SRajendra Nayak seq_printf(s, "%u\n", genpd->performance_state); 2930e8912812SRajendra Nayak 2931e8912812SRajendra Nayak genpd_unlock(genpd); 2932e8912812SRajendra Nayak return 0; 2933e8912812SRajendra Nayak } 2934e8912812SRajendra Nayak 2935d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(summary); 2936d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(status); 2937d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(sub_domains); 2938d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(idle_states); 2939d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(active_time); 2940d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(total_idle_time); 2941d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(devices); 2942d32dcc6cSYangtao Li DEFINE_SHOW_ATTRIBUTE(perf_state); 29432bd5306aSMaciej Matraszek 29449e9704eaSUlf Hansson static int __init genpd_debug_init(void) 29452bd5306aSMaciej Matraszek { 29462bd5306aSMaciej Matraszek struct dentry *d; 2947b6a1d093SThara Gopinath struct generic_pm_domain *genpd; 29482bd5306aSMaciej Matraszek 29499e9704eaSUlf Hansson genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); 29502bd5306aSMaciej Matraszek 2951e16a42c3SGreg Kroah-Hartman debugfs_create_file("pm_genpd_summary", S_IRUGO, genpd_debugfs_dir, 2952e16a42c3SGreg Kroah-Hartman NULL, &summary_fops); 29532bd5306aSMaciej Matraszek 2954b6a1d093SThara Gopinath list_for_each_entry(genpd, &gpd_list, gpd_list_node) { 29559e9704eaSUlf Hansson d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); 2956b6a1d093SThara Gopinath 2957b6a1d093SThara Gopinath debugfs_create_file("current_state", 0444, 2958d32dcc6cSYangtao Li d, genpd, &status_fops); 2959b6a1d093SThara Gopinath debugfs_create_file("sub_domains", 0444, 2960d32dcc6cSYangtao Li d, genpd, &sub_domains_fops); 2961b6a1d093SThara Gopinath debugfs_create_file("idle_states", 0444, 2962d32dcc6cSYangtao Li d, genpd, &idle_states_fops); 2963b6a1d093SThara Gopinath debugfs_create_file("active_time", 0444, 2964d32dcc6cSYangtao Li d, genpd, &active_time_fops); 2965b6a1d093SThara Gopinath debugfs_create_file("total_idle_time", 0444, 2966d32dcc6cSYangtao Li d, genpd, &total_idle_time_fops); 2967b6a1d093SThara Gopinath debugfs_create_file("devices", 0444, 2968d32dcc6cSYangtao Li d, genpd, &devices_fops); 2969e8912812SRajendra Nayak if (genpd->set_performance_state) 2970e8912812SRajendra Nayak debugfs_create_file("perf_state", 0444, 2971d32dcc6cSYangtao Li d, genpd, &perf_state_fops); 2972b6a1d093SThara Gopinath } 2973b6a1d093SThara Gopinath 29742bd5306aSMaciej Matraszek return 0; 29752bd5306aSMaciej Matraszek } 29769e9704eaSUlf Hansson late_initcall(genpd_debug_init); 29772bd5306aSMaciej Matraszek 29789e9704eaSUlf Hansson static void __exit genpd_debug_exit(void) 29792bd5306aSMaciej Matraszek { 29809e9704eaSUlf Hansson debugfs_remove_recursive(genpd_debugfs_dir); 29812bd5306aSMaciej Matraszek } 29829e9704eaSUlf Hansson __exitcall(genpd_debug_exit); 29838b0510b5SJon Hunter #endif /* CONFIG_DEBUG_FS */ 2984