162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/pm.h> 1262306a36Sopenharmony_ci#include <linux/pm_clock.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/clkdev.h> 1562306a36Sopenharmony_ci#include <linux/of_clk.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/pm_domain.h> 1962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#ifdef CONFIG_PM_CLK 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cienum pce_status { 2462306a36Sopenharmony_ci PCE_STATUS_NONE = 0, 2562306a36Sopenharmony_ci PCE_STATUS_ACQUIRED, 2662306a36Sopenharmony_ci PCE_STATUS_PREPARED, 2762306a36Sopenharmony_ci PCE_STATUS_ENABLED, 2862306a36Sopenharmony_ci PCE_STATUS_ERROR, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct pm_clock_entry { 3262306a36Sopenharmony_ci struct list_head node; 3362306a36Sopenharmony_ci char *con_id; 3462306a36Sopenharmony_ci struct clk *clk; 3562306a36Sopenharmony_ci enum pce_status status; 3662306a36Sopenharmony_ci bool enabled_when_prepared; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * pm_clk_list_lock - ensure exclusive access for modifying the PM clock 4162306a36Sopenharmony_ci * entry list. 4262306a36Sopenharmony_ci * @psd: pm_subsys_data instance corresponding to the PM clock entry list 4362306a36Sopenharmony_ci * and clk_op_might_sleep count to be modified. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Get exclusive access before modifying the PM clock entry list and the 4662306a36Sopenharmony_ci * clock_op_might_sleep count to guard against concurrent modifications. 4762306a36Sopenharmony_ci * This also protects against a concurrent clock_op_might_sleep and PM clock 4862306a36Sopenharmony_ci * entry list usage in pm_clk_suspend()/pm_clk_resume() that may or may not 4962306a36Sopenharmony_ci * happen in atomic context, hence both the mutex and the spinlock must be 5062306a36Sopenharmony_ci * taken here. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void pm_clk_list_lock(struct pm_subsys_data *psd) 5362306a36Sopenharmony_ci __acquires(&psd->lock) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci mutex_lock(&psd->clock_mutex); 5662306a36Sopenharmony_ci spin_lock_irq(&psd->lock); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * pm_clk_list_unlock - counterpart to pm_clk_list_lock(). 6162306a36Sopenharmony_ci * @psd: the same pm_subsys_data instance previously passed to 6262306a36Sopenharmony_ci * pm_clk_list_lock(). 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic void pm_clk_list_unlock(struct pm_subsys_data *psd) 6562306a36Sopenharmony_ci __releases(&psd->lock) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci spin_unlock_irq(&psd->lock); 6862306a36Sopenharmony_ci mutex_unlock(&psd->clock_mutex); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * pm_clk_op_lock - ensure exclusive access for performing clock operations. 7362306a36Sopenharmony_ci * @psd: pm_subsys_data instance corresponding to the PM clock entry list 7462306a36Sopenharmony_ci * and clk_op_might_sleep count being used. 7562306a36Sopenharmony_ci * @flags: stored irq flags. 7662306a36Sopenharmony_ci * @fn: string for the caller function's name. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * This is used by pm_clk_suspend() and pm_clk_resume() to guard 7962306a36Sopenharmony_ci * against concurrent modifications to the clock entry list and the 8062306a36Sopenharmony_ci * clock_op_might_sleep count. If clock_op_might_sleep is != 0 then 8162306a36Sopenharmony_ci * only the mutex can be locked and those functions can only be used in 8262306a36Sopenharmony_ci * non atomic context. If clock_op_might_sleep == 0 then these functions 8362306a36Sopenharmony_ci * may be used in any context and only the spinlock can be locked. 8462306a36Sopenharmony_ci * Returns -EINVAL if called in atomic context when clock ops might sleep. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic int pm_clk_op_lock(struct pm_subsys_data *psd, unsigned long *flags, 8762306a36Sopenharmony_ci const char *fn) 8862306a36Sopenharmony_ci /* sparse annotations don't work here as exit state isn't static */ 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci bool atomic_context = in_atomic() || irqs_disabled(); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_citry_again: 9362306a36Sopenharmony_ci spin_lock_irqsave(&psd->lock, *flags); 9462306a36Sopenharmony_ci if (!psd->clock_op_might_sleep) { 9562306a36Sopenharmony_ci /* the __release is there to work around sparse limitations */ 9662306a36Sopenharmony_ci __release(&psd->lock); 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* bail out if in atomic context */ 10162306a36Sopenharmony_ci if (atomic_context) { 10262306a36Sopenharmony_ci pr_err("%s: atomic context with clock_ops_might_sleep = %d", 10362306a36Sopenharmony_ci fn, psd->clock_op_might_sleep); 10462306a36Sopenharmony_ci spin_unlock_irqrestore(&psd->lock, *flags); 10562306a36Sopenharmony_ci might_sleep(); 10662306a36Sopenharmony_ci return -EPERM; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* we must switch to the mutex */ 11062306a36Sopenharmony_ci spin_unlock_irqrestore(&psd->lock, *flags); 11162306a36Sopenharmony_ci mutex_lock(&psd->clock_mutex); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * There was a possibility for psd->clock_op_might_sleep 11562306a36Sopenharmony_ci * to become 0 above. Keep the mutex only if not the case. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci if (likely(psd->clock_op_might_sleep)) 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci mutex_unlock(&psd->clock_mutex); 12162306a36Sopenharmony_ci goto try_again; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * pm_clk_op_unlock - counterpart to pm_clk_op_lock(). 12662306a36Sopenharmony_ci * @psd: the same pm_subsys_data instance previously passed to 12762306a36Sopenharmony_ci * pm_clk_op_lock(). 12862306a36Sopenharmony_ci * @flags: irq flags provided by pm_clk_op_lock(). 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic void pm_clk_op_unlock(struct pm_subsys_data *psd, unsigned long *flags) 13162306a36Sopenharmony_ci /* sparse annotations don't work here as entry state isn't static */ 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (psd->clock_op_might_sleep) { 13462306a36Sopenharmony_ci mutex_unlock(&psd->clock_mutex); 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci /* the __acquire is there to work around sparse limitations */ 13762306a36Sopenharmony_ci __acquire(&psd->lock); 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&psd->lock, *flags); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/** 14362306a36Sopenharmony_ci * __pm_clk_enable - Enable a clock, reporting any errors 14462306a36Sopenharmony_ci * @dev: The device for the given clock 14562306a36Sopenharmony_ci * @ce: PM clock entry corresponding to the clock. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_cistatic inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci switch (ce->status) { 15262306a36Sopenharmony_ci case PCE_STATUS_ACQUIRED: 15362306a36Sopenharmony_ci ret = clk_prepare_enable(ce->clk); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case PCE_STATUS_PREPARED: 15662306a36Sopenharmony_ci ret = clk_enable(ce->clk); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci if (!ret) 16262306a36Sopenharmony_ci ce->status = PCE_STATUS_ENABLED; 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci dev_err(dev, "%s: failed to enable clk %p, error %d\n", 16562306a36Sopenharmony_ci __func__, ce->clk, ret); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * pm_clk_acquire - Acquire a device clock. 17062306a36Sopenharmony_ci * @dev: Device whose clock is to be acquired. 17162306a36Sopenharmony_ci * @ce: PM clock entry corresponding to the clock. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci if (!ce->clk) 17662306a36Sopenharmony_ci ce->clk = clk_get(dev, ce->con_id); 17762306a36Sopenharmony_ci if (IS_ERR(ce->clk)) { 17862306a36Sopenharmony_ci ce->status = PCE_STATUS_ERROR; 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci } else if (clk_is_enabled_when_prepared(ce->clk)) { 18162306a36Sopenharmony_ci /* we defer preparing the clock in that case */ 18262306a36Sopenharmony_ci ce->status = PCE_STATUS_ACQUIRED; 18362306a36Sopenharmony_ci ce->enabled_when_prepared = true; 18462306a36Sopenharmony_ci } else if (clk_prepare(ce->clk)) { 18562306a36Sopenharmony_ci ce->status = PCE_STATUS_ERROR; 18662306a36Sopenharmony_ci dev_err(dev, "clk_prepare() failed\n"); 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci ce->status = PCE_STATUS_PREPARED; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", 19262306a36Sopenharmony_ci ce->clk, ce->con_id); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int __pm_clk_add(struct device *dev, const char *con_id, 19662306a36Sopenharmony_ci struct clk *clk) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 19962306a36Sopenharmony_ci struct pm_clock_entry *ce; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!psd) 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ce = kzalloc(sizeof(*ce), GFP_KERNEL); 20562306a36Sopenharmony_ci if (!ce) 20662306a36Sopenharmony_ci return -ENOMEM; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (con_id) { 20962306a36Sopenharmony_ci ce->con_id = kstrdup(con_id, GFP_KERNEL); 21062306a36Sopenharmony_ci if (!ce->con_id) { 21162306a36Sopenharmony_ci kfree(ce); 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci if (IS_ERR(clk)) { 21662306a36Sopenharmony_ci kfree(ce); 21762306a36Sopenharmony_ci return -ENOENT; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci ce->clk = clk; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pm_clk_acquire(dev, ce); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci pm_clk_list_lock(psd); 22562306a36Sopenharmony_ci list_add_tail(&ce->node, &psd->clock_list); 22662306a36Sopenharmony_ci if (ce->enabled_when_prepared) 22762306a36Sopenharmony_ci psd->clock_op_might_sleep++; 22862306a36Sopenharmony_ci pm_clk_list_unlock(psd); 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * pm_clk_add - Start using a device clock for power management. 23462306a36Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 23562306a36Sopenharmony_ci * @con_id: Connection ID of the clock. 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * Add the clock represented by @con_id to the list of clocks used for 23862306a36Sopenharmony_ci * the power management of @dev. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ciint pm_clk_add(struct device *dev, const char *con_id) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return __pm_clk_add(dev, con_id, NULL); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/** 24762306a36Sopenharmony_ci * pm_clk_add_clk - Start using a device clock for power management. 24862306a36Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 24962306a36Sopenharmony_ci * @clk: Clock pointer 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Add the clock to the list of clocks used for the power management of @dev. 25262306a36Sopenharmony_ci * The power-management code will take control of the clock reference, so 25362306a36Sopenharmony_ci * callers should not call clk_put() on @clk after this function sucessfully 25462306a36Sopenharmony_ci * returned. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ciint pm_clk_add_clk(struct device *dev, struct clk *clk) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci return __pm_clk_add(dev, NULL, clk); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add_clk); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/** 26462306a36Sopenharmony_ci * of_pm_clk_add_clk - Start using a device clock for power management. 26562306a36Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 26662306a36Sopenharmony_ci * @name: Name of clock that is going to be used for power management. 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Add the clock described in the 'clocks' device-tree node that matches 26962306a36Sopenharmony_ci * with the 'name' provided, to the list of clocks used for the power 27062306a36Sopenharmony_ci * management of @dev. On success, returns 0. Returns a negative error 27162306a36Sopenharmony_ci * code if the clock is not found or cannot be added. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ciint of_pm_clk_add_clk(struct device *dev, const char *name) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct clk *clk; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!dev || !dev->of_node || !name) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci clk = of_clk_get_by_name(dev->of_node, name); 28262306a36Sopenharmony_ci if (IS_ERR(clk)) 28362306a36Sopenharmony_ci return PTR_ERR(clk); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = pm_clk_add_clk(dev, clk); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci clk_put(clk); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pm_clk_add_clk); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/** 29662306a36Sopenharmony_ci * of_pm_clk_add_clks - Start using device clock(s) for power management. 29762306a36Sopenharmony_ci * @dev: Device whose clock(s) is going to be used for power management. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Add a series of clocks described in the 'clocks' device-tree node for 30062306a36Sopenharmony_ci * a device to the list of clocks used for the power management of @dev. 30162306a36Sopenharmony_ci * On success, returns the number of clocks added. Returns a negative 30262306a36Sopenharmony_ci * error code if there are no clocks in the device node for the device 30362306a36Sopenharmony_ci * or if adding a clock fails. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ciint of_pm_clk_add_clks(struct device *dev) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct clk **clks; 30862306a36Sopenharmony_ci int i, count; 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!dev || !dev->of_node) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci count = of_clk_get_parent_count(dev->of_node); 31562306a36Sopenharmony_ci if (count <= 0) 31662306a36Sopenharmony_ci return -ENODEV; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci clks = kcalloc(count, sizeof(*clks), GFP_KERNEL); 31962306a36Sopenharmony_ci if (!clks) 32062306a36Sopenharmony_ci return -ENOMEM; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 32362306a36Sopenharmony_ci clks[i] = of_clk_get(dev->of_node, i); 32462306a36Sopenharmony_ci if (IS_ERR(clks[i])) { 32562306a36Sopenharmony_ci ret = PTR_ERR(clks[i]); 32662306a36Sopenharmony_ci goto error; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ret = pm_clk_add_clk(dev, clks[i]); 33062306a36Sopenharmony_ci if (ret) { 33162306a36Sopenharmony_ci clk_put(clks[i]); 33262306a36Sopenharmony_ci goto error; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci kfree(clks); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cierror: 34162306a36Sopenharmony_ci while (i--) 34262306a36Sopenharmony_ci pm_clk_remove_clk(dev, clks[i]); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci kfree(clks); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pm_clk_add_clks); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/** 35162306a36Sopenharmony_ci * __pm_clk_remove - Destroy PM clock entry. 35262306a36Sopenharmony_ci * @ce: PM clock entry to destroy. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic void __pm_clk_remove(struct pm_clock_entry *ce) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci if (!ce) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci switch (ce->status) { 36062306a36Sopenharmony_ci case PCE_STATUS_ENABLED: 36162306a36Sopenharmony_ci clk_disable(ce->clk); 36262306a36Sopenharmony_ci fallthrough; 36362306a36Sopenharmony_ci case PCE_STATUS_PREPARED: 36462306a36Sopenharmony_ci clk_unprepare(ce->clk); 36562306a36Sopenharmony_ci fallthrough; 36662306a36Sopenharmony_ci case PCE_STATUS_ACQUIRED: 36762306a36Sopenharmony_ci case PCE_STATUS_ERROR: 36862306a36Sopenharmony_ci if (!IS_ERR(ce->clk)) 36962306a36Sopenharmony_ci clk_put(ce->clk); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci kfree(ce->con_id); 37662306a36Sopenharmony_ci kfree(ce); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * pm_clk_remove - Stop using a device clock for power management. 38162306a36Sopenharmony_ci * @dev: Device whose clock should not be used for PM any more. 38262306a36Sopenharmony_ci * @con_id: Connection ID of the clock. 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Remove the clock represented by @con_id from the list of clocks used for 38562306a36Sopenharmony_ci * the power management of @dev. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_civoid pm_clk_remove(struct device *dev, const char *con_id) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 39062306a36Sopenharmony_ci struct pm_clock_entry *ce; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!psd) 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pm_clk_list_lock(psd); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) { 39862306a36Sopenharmony_ci if (!con_id && !ce->con_id) 39962306a36Sopenharmony_ci goto remove; 40062306a36Sopenharmony_ci else if (!con_id || !ce->con_id) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci else if (!strcmp(con_id, ce->con_id)) 40362306a36Sopenharmony_ci goto remove; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci pm_clk_list_unlock(psd); 40762306a36Sopenharmony_ci return; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci remove: 41062306a36Sopenharmony_ci list_del(&ce->node); 41162306a36Sopenharmony_ci if (ce->enabled_when_prepared) 41262306a36Sopenharmony_ci psd->clock_op_might_sleep--; 41362306a36Sopenharmony_ci pm_clk_list_unlock(psd); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci __pm_clk_remove(ce); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_remove); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * pm_clk_remove_clk - Stop using a device clock for power management. 42162306a36Sopenharmony_ci * @dev: Device whose clock should not be used for PM any more. 42262306a36Sopenharmony_ci * @clk: Clock pointer 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * Remove the clock pointed to by @clk from the list of clocks used for 42562306a36Sopenharmony_ci * the power management of @dev. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_civoid pm_clk_remove_clk(struct device *dev, struct clk *clk) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 43062306a36Sopenharmony_ci struct pm_clock_entry *ce; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!psd || !clk) 43362306a36Sopenharmony_ci return; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci pm_clk_list_lock(psd); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) { 43862306a36Sopenharmony_ci if (clk == ce->clk) 43962306a36Sopenharmony_ci goto remove; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci pm_clk_list_unlock(psd); 44362306a36Sopenharmony_ci return; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci remove: 44662306a36Sopenharmony_ci list_del(&ce->node); 44762306a36Sopenharmony_ci if (ce->enabled_when_prepared) 44862306a36Sopenharmony_ci psd->clock_op_might_sleep--; 44962306a36Sopenharmony_ci pm_clk_list_unlock(psd); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci __pm_clk_remove(ce); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_remove_clk); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/** 45662306a36Sopenharmony_ci * pm_clk_init - Initialize a device's list of power management clocks. 45762306a36Sopenharmony_ci * @dev: Device to initialize the list of PM clocks for. 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * Initialize the lock and clock_list members of the device's pm_subsys_data 46062306a36Sopenharmony_ci * object, set the count of clocks that might sleep to 0. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_civoid pm_clk_init(struct device *dev) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 46562306a36Sopenharmony_ci if (psd) { 46662306a36Sopenharmony_ci INIT_LIST_HEAD(&psd->clock_list); 46762306a36Sopenharmony_ci mutex_init(&psd->clock_mutex); 46862306a36Sopenharmony_ci psd->clock_op_might_sleep = 0; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_init); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/** 47462306a36Sopenharmony_ci * pm_clk_create - Create and initialize a device's list of PM clocks. 47562306a36Sopenharmony_ci * @dev: Device to create and initialize the list of PM clocks for. 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * Allocate a struct pm_subsys_data object, initialize its lock and clock_list 47862306a36Sopenharmony_ci * members and make the @dev's power.subsys_data field point to it. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint pm_clk_create(struct device *dev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return dev_pm_get_subsys_data(dev); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_create); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * pm_clk_destroy - Destroy a device's list of power management clocks. 48862306a36Sopenharmony_ci * @dev: Device to destroy the list of PM clocks for. 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Clear the @dev's power.subsys_data field, remove the list of clock entries 49162306a36Sopenharmony_ci * from the struct pm_subsys_data object pointed to by it before and free 49262306a36Sopenharmony_ci * that object. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_civoid pm_clk_destroy(struct device *dev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 49762306a36Sopenharmony_ci struct pm_clock_entry *ce, *c; 49862306a36Sopenharmony_ci struct list_head list; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (!psd) 50162306a36Sopenharmony_ci return; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci pm_clk_list_lock(psd); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node) 50862306a36Sopenharmony_ci list_move(&ce->node, &list); 50962306a36Sopenharmony_ci psd->clock_op_might_sleep = 0; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci pm_clk_list_unlock(psd); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci dev_pm_put_subsys_data(dev); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci list_for_each_entry_safe_reverse(ce, c, &list, node) { 51662306a36Sopenharmony_ci list_del(&ce->node); 51762306a36Sopenharmony_ci __pm_clk_remove(ce); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_destroy); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void pm_clk_destroy_action(void *data) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci pm_clk_destroy(data); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint devm_pm_clk_create(struct device *dev) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci int ret; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = pm_clk_create(dev); 53262306a36Sopenharmony_ci if (ret) 53362306a36Sopenharmony_ci return ret; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return devm_add_action_or_reset(dev, pm_clk_destroy_action, dev); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_pm_clk_create); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/** 54062306a36Sopenharmony_ci * pm_clk_suspend - Disable clocks in a device's PM clock list. 54162306a36Sopenharmony_ci * @dev: Device to disable the clocks for. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ciint pm_clk_suspend(struct device *dev) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 54662306a36Sopenharmony_ci struct pm_clock_entry *ce; 54762306a36Sopenharmony_ci unsigned long flags; 54862306a36Sopenharmony_ci int ret; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!psd) 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ret = pm_clk_op_lock(psd, &flags, __func__); 55662306a36Sopenharmony_ci if (ret) 55762306a36Sopenharmony_ci return ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci list_for_each_entry_reverse(ce, &psd->clock_list, node) { 56062306a36Sopenharmony_ci if (ce->status == PCE_STATUS_ENABLED) { 56162306a36Sopenharmony_ci if (ce->enabled_when_prepared) { 56262306a36Sopenharmony_ci clk_disable_unprepare(ce->clk); 56362306a36Sopenharmony_ci ce->status = PCE_STATUS_ACQUIRED; 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci clk_disable(ce->clk); 56662306a36Sopenharmony_ci ce->status = PCE_STATUS_PREPARED; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci pm_clk_op_unlock(psd, &flags); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_suspend); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * pm_clk_resume - Enable clocks in a device's PM clock list. 57962306a36Sopenharmony_ci * @dev: Device to enable the clocks for. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ciint pm_clk_resume(struct device *dev) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 58462306a36Sopenharmony_ci struct pm_clock_entry *ce; 58562306a36Sopenharmony_ci unsigned long flags; 58662306a36Sopenharmony_ci int ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!psd) 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = pm_clk_op_lock(psd, &flags, __func__); 59462306a36Sopenharmony_ci if (ret) 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) 59862306a36Sopenharmony_ci __pm_clk_enable(dev, ce); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci pm_clk_op_unlock(psd, &flags); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_resume); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/** 60762306a36Sopenharmony_ci * pm_clk_notify - Notify routine for device addition and removal. 60862306a36Sopenharmony_ci * @nb: Notifier block object this function is a member of. 60962306a36Sopenharmony_ci * @action: Operation being carried out by the caller. 61062306a36Sopenharmony_ci * @data: Device the routine is being run for. 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * For this function to work, @nb must be a member of an object of type 61362306a36Sopenharmony_ci * struct pm_clk_notifier_block containing all of the requisite data. 61462306a36Sopenharmony_ci * Specifically, the pm_domain member of that object is copied to the device's 61562306a36Sopenharmony_ci * pm_domain field and its con_ids member is used to populate the device's list 61662306a36Sopenharmony_ci * of PM clocks, depending on @action. 61762306a36Sopenharmony_ci * 61862306a36Sopenharmony_ci * If the device's pm_domain field is already populated with a value different 61962306a36Sopenharmony_ci * from the one stored in the struct pm_clk_notifier_block object, the function 62062306a36Sopenharmony_ci * does nothing. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cistatic int pm_clk_notify(struct notifier_block *nb, 62362306a36Sopenharmony_ci unsigned long action, void *data) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct pm_clk_notifier_block *clknb; 62662306a36Sopenharmony_ci struct device *dev = data; 62762306a36Sopenharmony_ci char **con_id; 62862306a36Sopenharmony_ci int error; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci dev_dbg(dev, "%s() %ld\n", __func__, action); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci clknb = container_of(nb, struct pm_clk_notifier_block, nb); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci switch (action) { 63562306a36Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 63662306a36Sopenharmony_ci if (dev->pm_domain) 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci error = pm_clk_create(dev); 64062306a36Sopenharmony_ci if (error) 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci dev_pm_domain_set(dev, clknb->pm_domain); 64462306a36Sopenharmony_ci if (clknb->con_ids[0]) { 64562306a36Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 64662306a36Sopenharmony_ci pm_clk_add(dev, *con_id); 64762306a36Sopenharmony_ci } else { 64862306a36Sopenharmony_ci pm_clk_add(dev, NULL); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case BUS_NOTIFY_DEL_DEVICE: 65362306a36Sopenharmony_ci if (dev->pm_domain != clknb->pm_domain) 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci dev_pm_domain_set(dev, NULL); 65762306a36Sopenharmony_ci pm_clk_destroy(dev); 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciint pm_clk_runtime_suspend(struct device *dev) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci int ret; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = pm_generic_runtime_suspend(dev); 67162306a36Sopenharmony_ci if (ret) { 67262306a36Sopenharmony_ci dev_err(dev, "failed to suspend device\n"); 67362306a36Sopenharmony_ci return ret; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = pm_clk_suspend(dev); 67762306a36Sopenharmony_ci if (ret) { 67862306a36Sopenharmony_ci dev_err(dev, "failed to suspend clock\n"); 67962306a36Sopenharmony_ci pm_generic_runtime_resume(dev); 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_runtime_suspend); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciint pm_clk_runtime_resume(struct device *dev) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int ret; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ret = pm_clk_resume(dev); 69462306a36Sopenharmony_ci if (ret) { 69562306a36Sopenharmony_ci dev_err(dev, "failed to resume clock\n"); 69662306a36Sopenharmony_ci return ret; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return pm_generic_runtime_resume(dev); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_runtime_resume); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci#else /* !CONFIG_PM_CLK */ 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/** 70662306a36Sopenharmony_ci * enable_clock - Enable a device clock. 70762306a36Sopenharmony_ci * @dev: Device whose clock is to be enabled. 70862306a36Sopenharmony_ci * @con_id: Connection ID of the clock. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_cistatic void enable_clock(struct device *dev, const char *con_id) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct clk *clk; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci clk = clk_get(dev, con_id); 71562306a36Sopenharmony_ci if (!IS_ERR(clk)) { 71662306a36Sopenharmony_ci clk_prepare_enable(clk); 71762306a36Sopenharmony_ci clk_put(clk); 71862306a36Sopenharmony_ci dev_info(dev, "Runtime PM disabled, clock forced on.\n"); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/** 72362306a36Sopenharmony_ci * disable_clock - Disable a device clock. 72462306a36Sopenharmony_ci * @dev: Device whose clock is to be disabled. 72562306a36Sopenharmony_ci * @con_id: Connection ID of the clock. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_cistatic void disable_clock(struct device *dev, const char *con_id) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct clk *clk; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci clk = clk_get(dev, con_id); 73262306a36Sopenharmony_ci if (!IS_ERR(clk)) { 73362306a36Sopenharmony_ci clk_disable_unprepare(clk); 73462306a36Sopenharmony_ci clk_put(clk); 73562306a36Sopenharmony_ci dev_info(dev, "Runtime PM disabled, clock forced off.\n"); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/** 74062306a36Sopenharmony_ci * pm_clk_notify - Notify routine for device addition and removal. 74162306a36Sopenharmony_ci * @nb: Notifier block object this function is a member of. 74262306a36Sopenharmony_ci * @action: Operation being carried out by the caller. 74362306a36Sopenharmony_ci * @data: Device the routine is being run for. 74462306a36Sopenharmony_ci * 74562306a36Sopenharmony_ci * For this function to work, @nb must be a member of an object of type 74662306a36Sopenharmony_ci * struct pm_clk_notifier_block containing all of the requisite data. 74762306a36Sopenharmony_ci * Specifically, the con_ids member of that object is used to enable or disable 74862306a36Sopenharmony_ci * the device's clocks, depending on @action. 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_cistatic int pm_clk_notify(struct notifier_block *nb, 75162306a36Sopenharmony_ci unsigned long action, void *data) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct pm_clk_notifier_block *clknb; 75462306a36Sopenharmony_ci struct device *dev = data; 75562306a36Sopenharmony_ci char **con_id; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci dev_dbg(dev, "%s() %ld\n", __func__, action); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci clknb = container_of(nb, struct pm_clk_notifier_block, nb); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci switch (action) { 76262306a36Sopenharmony_ci case BUS_NOTIFY_BIND_DRIVER: 76362306a36Sopenharmony_ci if (clknb->con_ids[0]) { 76462306a36Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 76562306a36Sopenharmony_ci enable_clock(dev, *con_id); 76662306a36Sopenharmony_ci } else { 76762306a36Sopenharmony_ci enable_clock(dev, NULL); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case BUS_NOTIFY_DRIVER_NOT_BOUND: 77162306a36Sopenharmony_ci case BUS_NOTIFY_UNBOUND_DRIVER: 77262306a36Sopenharmony_ci if (clknb->con_ids[0]) { 77362306a36Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 77462306a36Sopenharmony_ci disable_clock(dev, *con_id); 77562306a36Sopenharmony_ci } else { 77662306a36Sopenharmony_ci disable_clock(dev, NULL); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci#endif /* !CONFIG_PM_CLK */ 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/** 78762306a36Sopenharmony_ci * pm_clk_add_notifier - Add bus type notifier for power management clocks. 78862306a36Sopenharmony_ci * @bus: Bus type to add the notifier to. 78962306a36Sopenharmony_ci * @clknb: Notifier to be added to the given bus type. 79062306a36Sopenharmony_ci * 79162306a36Sopenharmony_ci * The nb member of @clknb is not expected to be initialized and its 79262306a36Sopenharmony_ci * notifier_call member will be replaced with pm_clk_notify(). However, 79362306a36Sopenharmony_ci * the remaining members of @clknb should be populated prior to calling this 79462306a36Sopenharmony_ci * routine. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_civoid pm_clk_add_notifier(struct bus_type *bus, 79762306a36Sopenharmony_ci struct pm_clk_notifier_block *clknb) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci if (!bus || !clknb) 80062306a36Sopenharmony_ci return; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci clknb->nb.notifier_call = pm_clk_notify; 80362306a36Sopenharmony_ci bus_register_notifier(bus, &clknb->nb); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add_notifier); 806