18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/pm.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_clock.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 158c2ecf20Sopenharmony_ci#include <linux/of_clk.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_CLK 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cienum pce_status { 248c2ecf20Sopenharmony_ci PCE_STATUS_NONE = 0, 258c2ecf20Sopenharmony_ci PCE_STATUS_ACQUIRED, 268c2ecf20Sopenharmony_ci PCE_STATUS_ENABLED, 278c2ecf20Sopenharmony_ci PCE_STATUS_ERROR, 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct pm_clock_entry { 318c2ecf20Sopenharmony_ci struct list_head node; 328c2ecf20Sopenharmony_ci char *con_id; 338c2ecf20Sopenharmony_ci struct clk *clk; 348c2ecf20Sopenharmony_ci enum pce_status status; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/** 388c2ecf20Sopenharmony_ci * pm_clk_enable - Enable a clock, reporting any errors 398c2ecf20Sopenharmony_ci * @dev: The device for the given clock 408c2ecf20Sopenharmony_ci * @ce: PM clock entry corresponding to the clock. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int ret; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (ce->status < PCE_STATUS_ERROR) { 478c2ecf20Sopenharmony_ci ret = clk_enable(ce->clk); 488c2ecf20Sopenharmony_ci if (!ret) 498c2ecf20Sopenharmony_ci ce->status = PCE_STATUS_ENABLED; 508c2ecf20Sopenharmony_ci else 518c2ecf20Sopenharmony_ci dev_err(dev, "%s: failed to enable clk %p, error %d\n", 528c2ecf20Sopenharmony_ci __func__, ce->clk, ret); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * pm_clk_acquire - Acquire a device clock. 588c2ecf20Sopenharmony_ci * @dev: Device whose clock is to be acquired. 598c2ecf20Sopenharmony_ci * @ce: PM clock entry corresponding to the clock. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci if (!ce->clk) 648c2ecf20Sopenharmony_ci ce->clk = clk_get(dev, ce->con_id); 658c2ecf20Sopenharmony_ci if (IS_ERR(ce->clk)) { 668c2ecf20Sopenharmony_ci ce->status = PCE_STATUS_ERROR; 678c2ecf20Sopenharmony_ci } else { 688c2ecf20Sopenharmony_ci if (clk_prepare(ce->clk)) { 698c2ecf20Sopenharmony_ci ce->status = PCE_STATUS_ERROR; 708c2ecf20Sopenharmony_ci dev_err(dev, "clk_prepare() failed\n"); 718c2ecf20Sopenharmony_ci } else { 728c2ecf20Sopenharmony_ci ce->status = PCE_STATUS_ACQUIRED; 738c2ecf20Sopenharmony_ci dev_dbg(dev, 748c2ecf20Sopenharmony_ci "Clock %pC con_id %s managed by runtime PM.\n", 758c2ecf20Sopenharmony_ci ce->clk, ce->con_id); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int __pm_clk_add(struct device *dev, const char *con_id, 818c2ecf20Sopenharmony_ci struct clk *clk) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 848c2ecf20Sopenharmony_ci struct pm_clock_entry *ce; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!psd) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ce = kzalloc(sizeof(*ce), GFP_KERNEL); 908c2ecf20Sopenharmony_ci if (!ce) 918c2ecf20Sopenharmony_ci return -ENOMEM; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (con_id) { 948c2ecf20Sopenharmony_ci ce->con_id = kstrdup(con_id, GFP_KERNEL); 958c2ecf20Sopenharmony_ci if (!ce->con_id) { 968c2ecf20Sopenharmony_ci kfree(ce); 978c2ecf20Sopenharmony_ci return -ENOMEM; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1018c2ecf20Sopenharmony_ci kfree(ce); 1028c2ecf20Sopenharmony_ci return -ENOENT; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci ce->clk = clk; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pm_clk_acquire(dev, ce); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci spin_lock_irq(&psd->lock); 1108c2ecf20Sopenharmony_ci list_add_tail(&ce->node, &psd->clock_list); 1118c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * pm_clk_add - Start using a device clock for power management. 1178c2ecf20Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 1188c2ecf20Sopenharmony_ci * @con_id: Connection ID of the clock. 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Add the clock represented by @con_id to the list of clocks used for 1218c2ecf20Sopenharmony_ci * the power management of @dev. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ciint pm_clk_add(struct device *dev, const char *con_id) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return __pm_clk_add(dev, con_id, NULL); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * pm_clk_add_clk - Start using a device clock for power management. 1318c2ecf20Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 1328c2ecf20Sopenharmony_ci * @clk: Clock pointer 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Add the clock to the list of clocks used for the power management of @dev. 1358c2ecf20Sopenharmony_ci * The power-management code will take control of the clock reference, so 1368c2ecf20Sopenharmony_ci * callers should not call clk_put() on @clk after this function sucessfully 1378c2ecf20Sopenharmony_ci * returned. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ciint pm_clk_add_clk(struct device *dev, struct clk *clk) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return __pm_clk_add(dev, NULL, clk); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add_clk); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/** 1478c2ecf20Sopenharmony_ci * of_pm_clk_add_clk - Start using a device clock for power management. 1488c2ecf20Sopenharmony_ci * @dev: Device whose clock is going to be used for power management. 1498c2ecf20Sopenharmony_ci * @name: Name of clock that is going to be used for power management. 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * Add the clock described in the 'clocks' device-tree node that matches 1528c2ecf20Sopenharmony_ci * with the 'name' provided, to the list of clocks used for the power 1538c2ecf20Sopenharmony_ci * management of @dev. On success, returns 0. Returns a negative error 1548c2ecf20Sopenharmony_ci * code if the clock is not found or cannot be added. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ciint of_pm_clk_add_clk(struct device *dev, const char *name) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct clk *clk; 1598c2ecf20Sopenharmony_ci int ret; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!dev || !dev->of_node || !name) 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci clk = of_clk_get_by_name(dev->of_node, name); 1658c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 1668c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = pm_clk_add_clk(dev, clk); 1698c2ecf20Sopenharmony_ci if (ret) { 1708c2ecf20Sopenharmony_ci clk_put(clk); 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pm_clk_add_clk); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * of_pm_clk_add_clks - Start using device clock(s) for power management. 1808c2ecf20Sopenharmony_ci * @dev: Device whose clock(s) is going to be used for power management. 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Add a series of clocks described in the 'clocks' device-tree node for 1838c2ecf20Sopenharmony_ci * a device to the list of clocks used for the power management of @dev. 1848c2ecf20Sopenharmony_ci * On success, returns the number of clocks added. Returns a negative 1858c2ecf20Sopenharmony_ci * error code if there are no clocks in the device node for the device 1868c2ecf20Sopenharmony_ci * or if adding a clock fails. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ciint of_pm_clk_add_clks(struct device *dev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct clk **clks; 1918c2ecf20Sopenharmony_ci int i, count; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!dev || !dev->of_node) 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci count = of_clk_get_parent_count(dev->of_node); 1988c2ecf20Sopenharmony_ci if (count <= 0) 1998c2ecf20Sopenharmony_ci return -ENODEV; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci clks = kcalloc(count, sizeof(*clks), GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!clks) 2038c2ecf20Sopenharmony_ci return -ENOMEM; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2068c2ecf20Sopenharmony_ci clks[i] = of_clk_get(dev->of_node, i); 2078c2ecf20Sopenharmony_ci if (IS_ERR(clks[i])) { 2088c2ecf20Sopenharmony_ci ret = PTR_ERR(clks[i]); 2098c2ecf20Sopenharmony_ci goto error; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = pm_clk_add_clk(dev, clks[i]); 2138c2ecf20Sopenharmony_ci if (ret) { 2148c2ecf20Sopenharmony_ci clk_put(clks[i]); 2158c2ecf20Sopenharmony_ci goto error; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci kfree(clks); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return i; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cierror: 2248c2ecf20Sopenharmony_ci while (i--) 2258c2ecf20Sopenharmony_ci pm_clk_remove_clk(dev, clks[i]); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci kfree(clks); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pm_clk_add_clks); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * __pm_clk_remove - Destroy PM clock entry. 2358c2ecf20Sopenharmony_ci * @ce: PM clock entry to destroy. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cistatic void __pm_clk_remove(struct pm_clock_entry *ce) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci if (!ce) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (ce->status < PCE_STATUS_ERROR) { 2438c2ecf20Sopenharmony_ci if (ce->status == PCE_STATUS_ENABLED) 2448c2ecf20Sopenharmony_ci clk_disable(ce->clk); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (ce->status >= PCE_STATUS_ACQUIRED) { 2478c2ecf20Sopenharmony_ci clk_unprepare(ce->clk); 2488c2ecf20Sopenharmony_ci clk_put(ce->clk); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci kfree(ce->con_id); 2538c2ecf20Sopenharmony_ci kfree(ce); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * pm_clk_remove - Stop using a device clock for power management. 2588c2ecf20Sopenharmony_ci * @dev: Device whose clock should not be used for PM any more. 2598c2ecf20Sopenharmony_ci * @con_id: Connection ID of the clock. 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Remove the clock represented by @con_id from the list of clocks used for 2628c2ecf20Sopenharmony_ci * the power management of @dev. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_civoid pm_clk_remove(struct device *dev, const char *con_id) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 2678c2ecf20Sopenharmony_ci struct pm_clock_entry *ce; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (!psd) 2708c2ecf20Sopenharmony_ci return; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci spin_lock_irq(&psd->lock); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) { 2758c2ecf20Sopenharmony_ci if (!con_id && !ce->con_id) 2768c2ecf20Sopenharmony_ci goto remove; 2778c2ecf20Sopenharmony_ci else if (!con_id || !ce->con_id) 2788c2ecf20Sopenharmony_ci continue; 2798c2ecf20Sopenharmony_ci else if (!strcmp(con_id, ce->con_id)) 2808c2ecf20Sopenharmony_ci goto remove; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 2848c2ecf20Sopenharmony_ci return; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci remove: 2878c2ecf20Sopenharmony_ci list_del(&ce->node); 2888c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci __pm_clk_remove(ce); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_remove); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * pm_clk_remove_clk - Stop using a device clock for power management. 2968c2ecf20Sopenharmony_ci * @dev: Device whose clock should not be used for PM any more. 2978c2ecf20Sopenharmony_ci * @clk: Clock pointer 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Remove the clock pointed to by @clk from the list of clocks used for 3008c2ecf20Sopenharmony_ci * the power management of @dev. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid pm_clk_remove_clk(struct device *dev, struct clk *clk) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 3058c2ecf20Sopenharmony_ci struct pm_clock_entry *ce; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!psd || !clk) 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci spin_lock_irq(&psd->lock); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) { 3138c2ecf20Sopenharmony_ci if (clk == ce->clk) 3148c2ecf20Sopenharmony_ci goto remove; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 3188c2ecf20Sopenharmony_ci return; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci remove: 3218c2ecf20Sopenharmony_ci list_del(&ce->node); 3228c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci __pm_clk_remove(ce); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_remove_clk); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/** 3298c2ecf20Sopenharmony_ci * pm_clk_init - Initialize a device's list of power management clocks. 3308c2ecf20Sopenharmony_ci * @dev: Device to initialize the list of PM clocks for. 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * Initialize the lock and clock_list members of the device's pm_subsys_data 3338c2ecf20Sopenharmony_ci * object. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_civoid pm_clk_init(struct device *dev) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 3388c2ecf20Sopenharmony_ci if (psd) 3398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&psd->clock_list); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_init); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/** 3448c2ecf20Sopenharmony_ci * pm_clk_create - Create and initialize a device's list of PM clocks. 3458c2ecf20Sopenharmony_ci * @dev: Device to create and initialize the list of PM clocks for. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Allocate a struct pm_subsys_data object, initialize its lock and clock_list 3488c2ecf20Sopenharmony_ci * members and make the @dev's power.subsys_data field point to it. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ciint pm_clk_create(struct device *dev) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci return dev_pm_get_subsys_data(dev); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_create); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * pm_clk_destroy - Destroy a device's list of power management clocks. 3588c2ecf20Sopenharmony_ci * @dev: Device to destroy the list of PM clocks for. 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Clear the @dev's power.subsys_data field, remove the list of clock entries 3618c2ecf20Sopenharmony_ci * from the struct pm_subsys_data object pointed to by it before and free 3628c2ecf20Sopenharmony_ci * that object. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_civoid pm_clk_destroy(struct device *dev) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 3678c2ecf20Sopenharmony_ci struct pm_clock_entry *ce, *c; 3688c2ecf20Sopenharmony_ci struct list_head list; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!psd) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci spin_lock_irq(&psd->lock); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node) 3788c2ecf20Sopenharmony_ci list_move(&ce->node, &list); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci spin_unlock_irq(&psd->lock); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci dev_pm_put_subsys_data(dev); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(ce, c, &list, node) { 3858c2ecf20Sopenharmony_ci list_del(&ce->node); 3868c2ecf20Sopenharmony_ci __pm_clk_remove(ce); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_destroy); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * pm_clk_suspend - Disable clocks in a device's PM clock list. 3938c2ecf20Sopenharmony_ci * @dev: Device to disable the clocks for. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ciint pm_clk_suspend(struct device *dev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 3988c2ecf20Sopenharmony_ci struct pm_clock_entry *ce; 3998c2ecf20Sopenharmony_ci unsigned long flags; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!psd) 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci spin_lock_irqsave(&psd->lock, flags); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci list_for_each_entry_reverse(ce, &psd->clock_list, node) { 4098c2ecf20Sopenharmony_ci if (ce->status < PCE_STATUS_ERROR) { 4108c2ecf20Sopenharmony_ci if (ce->status == PCE_STATUS_ENABLED) 4118c2ecf20Sopenharmony_ci clk_disable(ce->clk); 4128c2ecf20Sopenharmony_ci ce->status = PCE_STATUS_ACQUIRED; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&psd->lock, flags); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_suspend); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/** 4238c2ecf20Sopenharmony_ci * pm_clk_resume - Enable clocks in a device's PM clock list. 4248c2ecf20Sopenharmony_ci * @dev: Device to enable the clocks for. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ciint pm_clk_resume(struct device *dev) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct pm_subsys_data *psd = dev_to_psd(dev); 4298c2ecf20Sopenharmony_ci struct pm_clock_entry *ce; 4308c2ecf20Sopenharmony_ci unsigned long flags; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci dev_dbg(dev, "%s()\n", __func__); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (!psd) 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci spin_lock_irqsave(&psd->lock, flags); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci list_for_each_entry(ce, &psd->clock_list, node) 4408c2ecf20Sopenharmony_ci __pm_clk_enable(dev, ce); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&psd->lock, flags); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_resume); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/** 4498c2ecf20Sopenharmony_ci * pm_clk_notify - Notify routine for device addition and removal. 4508c2ecf20Sopenharmony_ci * @nb: Notifier block object this function is a member of. 4518c2ecf20Sopenharmony_ci * @action: Operation being carried out by the caller. 4528c2ecf20Sopenharmony_ci * @data: Device the routine is being run for. 4538c2ecf20Sopenharmony_ci * 4548c2ecf20Sopenharmony_ci * For this function to work, @nb must be a member of an object of type 4558c2ecf20Sopenharmony_ci * struct pm_clk_notifier_block containing all of the requisite data. 4568c2ecf20Sopenharmony_ci * Specifically, the pm_domain member of that object is copied to the device's 4578c2ecf20Sopenharmony_ci * pm_domain field and its con_ids member is used to populate the device's list 4588c2ecf20Sopenharmony_ci * of PM clocks, depending on @action. 4598c2ecf20Sopenharmony_ci * 4608c2ecf20Sopenharmony_ci * If the device's pm_domain field is already populated with a value different 4618c2ecf20Sopenharmony_ci * from the one stored in the struct pm_clk_notifier_block object, the function 4628c2ecf20Sopenharmony_ci * does nothing. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_cistatic int pm_clk_notify(struct notifier_block *nb, 4658c2ecf20Sopenharmony_ci unsigned long action, void *data) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct pm_clk_notifier_block *clknb; 4688c2ecf20Sopenharmony_ci struct device *dev = data; 4698c2ecf20Sopenharmony_ci char **con_id; 4708c2ecf20Sopenharmony_ci int error; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci dev_dbg(dev, "%s() %ld\n", __func__, action); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci clknb = container_of(nb, struct pm_clk_notifier_block, nb); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci switch (action) { 4778c2ecf20Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 4788c2ecf20Sopenharmony_ci if (dev->pm_domain) 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci error = pm_clk_create(dev); 4828c2ecf20Sopenharmony_ci if (error) 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_pm_domain_set(dev, clknb->pm_domain); 4868c2ecf20Sopenharmony_ci if (clknb->con_ids[0]) { 4878c2ecf20Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 4888c2ecf20Sopenharmony_ci pm_clk_add(dev, *con_id); 4898c2ecf20Sopenharmony_ci } else { 4908c2ecf20Sopenharmony_ci pm_clk_add(dev, NULL); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case BUS_NOTIFY_DEL_DEVICE: 4958c2ecf20Sopenharmony_ci if (dev->pm_domain != clknb->pm_domain) 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci dev_pm_domain_set(dev, NULL); 4998c2ecf20Sopenharmony_ci pm_clk_destroy(dev); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ciint pm_clk_runtime_suspend(struct device *dev) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = pm_generic_runtime_suspend(dev); 5138c2ecf20Sopenharmony_ci if (ret) { 5148c2ecf20Sopenharmony_ci dev_err(dev, "failed to suspend device\n"); 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ret = pm_clk_suspend(dev); 5198c2ecf20Sopenharmony_ci if (ret) { 5208c2ecf20Sopenharmony_ci dev_err(dev, "failed to suspend clock\n"); 5218c2ecf20Sopenharmony_ci pm_generic_runtime_resume(dev); 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_runtime_suspend); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciint pm_clk_runtime_resume(struct device *dev) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci int ret; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = pm_clk_resume(dev); 5368c2ecf20Sopenharmony_ci if (ret) { 5378c2ecf20Sopenharmony_ci dev_err(dev, "failed to resume clock\n"); 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return pm_generic_runtime_resume(dev); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_runtime_resume); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci#else /* !CONFIG_PM_CLK */ 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/** 5488c2ecf20Sopenharmony_ci * enable_clock - Enable a device clock. 5498c2ecf20Sopenharmony_ci * @dev: Device whose clock is to be enabled. 5508c2ecf20Sopenharmony_ci * @con_id: Connection ID of the clock. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic void enable_clock(struct device *dev, const char *con_id) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct clk *clk; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci clk = clk_get(dev, con_id); 5578c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 5588c2ecf20Sopenharmony_ci clk_prepare_enable(clk); 5598c2ecf20Sopenharmony_ci clk_put(clk); 5608c2ecf20Sopenharmony_ci dev_info(dev, "Runtime PM disabled, clock forced on.\n"); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/** 5658c2ecf20Sopenharmony_ci * disable_clock - Disable a device clock. 5668c2ecf20Sopenharmony_ci * @dev: Device whose clock is to be disabled. 5678c2ecf20Sopenharmony_ci * @con_id: Connection ID of the clock. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_cistatic void disable_clock(struct device *dev, const char *con_id) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct clk *clk; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci clk = clk_get(dev, con_id); 5748c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 5758c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 5768c2ecf20Sopenharmony_ci clk_put(clk); 5778c2ecf20Sopenharmony_ci dev_info(dev, "Runtime PM disabled, clock forced off.\n"); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/** 5828c2ecf20Sopenharmony_ci * pm_clk_notify - Notify routine for device addition and removal. 5838c2ecf20Sopenharmony_ci * @nb: Notifier block object this function is a member of. 5848c2ecf20Sopenharmony_ci * @action: Operation being carried out by the caller. 5858c2ecf20Sopenharmony_ci * @data: Device the routine is being run for. 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * For this function to work, @nb must be a member of an object of type 5888c2ecf20Sopenharmony_ci * struct pm_clk_notifier_block containing all of the requisite data. 5898c2ecf20Sopenharmony_ci * Specifically, the con_ids member of that object is used to enable or disable 5908c2ecf20Sopenharmony_ci * the device's clocks, depending on @action. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic int pm_clk_notify(struct notifier_block *nb, 5938c2ecf20Sopenharmony_ci unsigned long action, void *data) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct pm_clk_notifier_block *clknb; 5968c2ecf20Sopenharmony_ci struct device *dev = data; 5978c2ecf20Sopenharmony_ci char **con_id; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci dev_dbg(dev, "%s() %ld\n", __func__, action); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci clknb = container_of(nb, struct pm_clk_notifier_block, nb); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci switch (action) { 6048c2ecf20Sopenharmony_ci case BUS_NOTIFY_BIND_DRIVER: 6058c2ecf20Sopenharmony_ci if (clknb->con_ids[0]) { 6068c2ecf20Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 6078c2ecf20Sopenharmony_ci enable_clock(dev, *con_id); 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci enable_clock(dev, NULL); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci case BUS_NOTIFY_DRIVER_NOT_BOUND: 6138c2ecf20Sopenharmony_ci case BUS_NOTIFY_UNBOUND_DRIVER: 6148c2ecf20Sopenharmony_ci if (clknb->con_ids[0]) { 6158c2ecf20Sopenharmony_ci for (con_id = clknb->con_ids; *con_id; con_id++) 6168c2ecf20Sopenharmony_ci disable_clock(dev, *con_id); 6178c2ecf20Sopenharmony_ci } else { 6188c2ecf20Sopenharmony_ci disable_clock(dev, NULL); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci#endif /* !CONFIG_PM_CLK */ 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * pm_clk_add_notifier - Add bus type notifier for power management clocks. 6308c2ecf20Sopenharmony_ci * @bus: Bus type to add the notifier to. 6318c2ecf20Sopenharmony_ci * @clknb: Notifier to be added to the given bus type. 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * The nb member of @clknb is not expected to be initialized and its 6348c2ecf20Sopenharmony_ci * notifier_call member will be replaced with pm_clk_notify(). However, 6358c2ecf20Sopenharmony_ci * the remaining members of @clknb should be populated prior to calling this 6368c2ecf20Sopenharmony_ci * routine. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_civoid pm_clk_add_notifier(struct bus_type *bus, 6398c2ecf20Sopenharmony_ci struct pm_clk_notifier_block *clknb) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci if (!bus || !clknb) 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci clknb->nb.notifier_call = pm_clk_notify; 6458c2ecf20Sopenharmony_ci bus_register_notifier(bus, &clknb->nb); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_clk_add_notifier); 648