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