13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
43d0407baSopenharmony_ci * Author: Tony Xie <tony.xie@rock-chips.com>
53d0407baSopenharmony_ci */
63d0407baSopenharmony_ci
73d0407baSopenharmony_ci#include <linux/arm-smccc.h>
83d0407baSopenharmony_ci#include <linux/clk.h>
93d0407baSopenharmony_ci#include <linux/cpufreq.h>
103d0407baSopenharmony_ci#include <linux/delay.h>
113d0407baSopenharmony_ci#include <linux/devfreq.h>
123d0407baSopenharmony_ci#include <linux/module.h>
133d0407baSopenharmony_ci#include <linux/of.h>
143d0407baSopenharmony_ci#include <linux/pm_opp.h>
153d0407baSopenharmony_ci#include <linux/platform_device.h>
163d0407baSopenharmony_ci#include <linux/regulator/consumer.h>
173d0407baSopenharmony_ci#include <linux/rockchip/rockchip_sip.h>
183d0407baSopenharmony_ci#include <linux/slab.h>
193d0407baSopenharmony_ci#include <linux/string.h>
203d0407baSopenharmony_ci#include <soc/rockchip/rockchip_opp_select.h>
213d0407baSopenharmony_ci
223d0407baSopenharmony_ci#define CLUSTER0	0
233d0407baSopenharmony_ci#define CLUSTER1	1
243d0407baSopenharmony_ci#define MAX_CLUSTERS	2
253d0407baSopenharmony_ci
263d0407baSopenharmony_ci#define to_rockchip_bus_clk_nb(nb) \
273d0407baSopenharmony_ci	container_of(nb, struct rockchip_bus, clk_nb)
283d0407baSopenharmony_ci#define to_rockchip_bus_cpufreq_nb(nb) \
293d0407baSopenharmony_ci	container_of(nb, struct rockchip_bus, cpufreq_nb)
303d0407baSopenharmony_ci
313d0407baSopenharmony_cistruct busfreq_table {
323d0407baSopenharmony_ci	unsigned long freq;
333d0407baSopenharmony_ci	unsigned long volt;
343d0407baSopenharmony_ci};
353d0407baSopenharmony_ci
363d0407baSopenharmony_cistruct rockchip_bus {
373d0407baSopenharmony_ci	struct device *dev;
383d0407baSopenharmony_ci	struct regulator *regulator;
393d0407baSopenharmony_ci	struct clk *clk;
403d0407baSopenharmony_ci	struct notifier_block clk_nb;
413d0407baSopenharmony_ci	struct notifier_block cpufreq_nb;
423d0407baSopenharmony_ci	struct busfreq_table *freq_table;
433d0407baSopenharmony_ci
443d0407baSopenharmony_ci	unsigned int max_state;
453d0407baSopenharmony_ci
463d0407baSopenharmony_ci	unsigned long cur_volt;
473d0407baSopenharmony_ci	unsigned long cur_rate;
483d0407baSopenharmony_ci
493d0407baSopenharmony_ci	/*
503d0407baSopenharmony_ci	 * Busfreq-policy-cpufreq:
513d0407baSopenharmony_ci	 * If the cpu frequency of two clusters are both less than or equal to
523d0407baSopenharmony_ci	 * cpu_high_freq, change bus rate to low_rate, otherwise change it to
533d0407baSopenharmony_ci	 * high_rate.
543d0407baSopenharmony_ci	 */
553d0407baSopenharmony_ci	unsigned long high_rate;
563d0407baSopenharmony_ci	unsigned long low_rate;
573d0407baSopenharmony_ci	unsigned int cpu_high_freq;
583d0407baSopenharmony_ci	unsigned int cpu_freq[MAX_CLUSTERS];
593d0407baSopenharmony_ci};
603d0407baSopenharmony_ci
613d0407baSopenharmony_cistatic int rockchip_sip_bus_smc_config(u32 bus_id, u32 cfg, u32 enable_msk)
623d0407baSopenharmony_ci{
633d0407baSopenharmony_ci	struct arm_smccc_res res;
643d0407baSopenharmony_ci
653d0407baSopenharmony_ci	res = sip_smc_bus_config(bus_id, cfg, enable_msk);
663d0407baSopenharmony_ci
673d0407baSopenharmony_ci	return res.a0;
683d0407baSopenharmony_ci}
693d0407baSopenharmony_ci
703d0407baSopenharmony_cistatic int rockchip_bus_smc_config(struct rockchip_bus *bus)
713d0407baSopenharmony_ci{
723d0407baSopenharmony_ci	struct device *dev = bus->dev;
733d0407baSopenharmony_ci	struct device_node *np = dev->of_node;
743d0407baSopenharmony_ci	struct device_node *child;
753d0407baSopenharmony_ci	unsigned int enable_msk, bus_id, cfg;
763d0407baSopenharmony_ci	int ret;
773d0407baSopenharmony_ci
783d0407baSopenharmony_ci	for_each_available_child_of_node(np, child) {
793d0407baSopenharmony_ci		ret = of_property_read_u32_index(child, "bus-id", 0,
803d0407baSopenharmony_ci						 &bus_id);
813d0407baSopenharmony_ci		if (ret)
823d0407baSopenharmony_ci			continue;
833d0407baSopenharmony_ci
843d0407baSopenharmony_ci		ret = of_property_read_u32_index(child, "cfg-val", 0,
853d0407baSopenharmony_ci						 &cfg);
863d0407baSopenharmony_ci		if (ret) {
873d0407baSopenharmony_ci			dev_info(dev, "get cfg-val error\n");
883d0407baSopenharmony_ci			continue;
893d0407baSopenharmony_ci		}
903d0407baSopenharmony_ci
913d0407baSopenharmony_ci		if (!cfg) {
923d0407baSopenharmony_ci			dev_info(dev, "cfg-val invalid\n");
933d0407baSopenharmony_ci			continue;
943d0407baSopenharmony_ci		}
953d0407baSopenharmony_ci
963d0407baSopenharmony_ci		ret = of_property_read_u32_index(child, "enable-msk", 0,
973d0407baSopenharmony_ci						 &enable_msk);
983d0407baSopenharmony_ci		if (ret) {
993d0407baSopenharmony_ci			dev_info(dev, "get enable_msk error\n");
1003d0407baSopenharmony_ci			continue;
1013d0407baSopenharmony_ci		}
1023d0407baSopenharmony_ci
1033d0407baSopenharmony_ci		ret = rockchip_sip_bus_smc_config(bus_id, cfg,
1043d0407baSopenharmony_ci						  enable_msk);
1053d0407baSopenharmony_ci		if (ret) {
1063d0407baSopenharmony_ci			dev_info(dev, "bus smc config error: %x!\n", ret);
1073d0407baSopenharmony_ci			break;
1083d0407baSopenharmony_ci		}
1093d0407baSopenharmony_ci	}
1103d0407baSopenharmony_ci
1113d0407baSopenharmony_ci	return 0;
1123d0407baSopenharmony_ci}
1133d0407baSopenharmony_ci
1143d0407baSopenharmony_cistatic int rockchip_bus_set_freq_table(struct rockchip_bus *bus)
1153d0407baSopenharmony_ci{
1163d0407baSopenharmony_ci	struct device *dev = bus->dev;
1173d0407baSopenharmony_ci	struct dev_pm_opp *opp;
1183d0407baSopenharmony_ci	unsigned long freq;
1193d0407baSopenharmony_ci	int i, count;
1203d0407baSopenharmony_ci
1213d0407baSopenharmony_ci	count = dev_pm_opp_get_opp_count(dev);
1223d0407baSopenharmony_ci	if (count <= 0)
1233d0407baSopenharmony_ci		return -EINVAL;
1243d0407baSopenharmony_ci
1253d0407baSopenharmony_ci	bus->max_state = count;
1263d0407baSopenharmony_ci	bus->freq_table = devm_kcalloc(dev,
1273d0407baSopenharmony_ci				       bus->max_state,
1283d0407baSopenharmony_ci				       sizeof(*bus->freq_table),
1293d0407baSopenharmony_ci				       GFP_KERNEL);
1303d0407baSopenharmony_ci	if (!bus->freq_table) {
1313d0407baSopenharmony_ci		bus->max_state = 0;
1323d0407baSopenharmony_ci		return -ENOMEM;
1333d0407baSopenharmony_ci	}
1343d0407baSopenharmony_ci
1353d0407baSopenharmony_ci	for (i = 0, freq = 0; i < bus->max_state; i++, freq++) {
1363d0407baSopenharmony_ci		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
1373d0407baSopenharmony_ci		if (IS_ERR(opp)) {
1383d0407baSopenharmony_ci			devm_kfree(dev, bus->freq_table);
1393d0407baSopenharmony_ci			bus->max_state = 0;
1403d0407baSopenharmony_ci			return PTR_ERR(opp);
1413d0407baSopenharmony_ci		}
1423d0407baSopenharmony_ci		bus->freq_table[i].volt = dev_pm_opp_get_voltage(opp);
1433d0407baSopenharmony_ci		bus->freq_table[i].freq = freq;
1443d0407baSopenharmony_ci		dev_pm_opp_put(opp);
1453d0407baSopenharmony_ci	}
1463d0407baSopenharmony_ci
1473d0407baSopenharmony_ci	return 0;
1483d0407baSopenharmony_ci}
1493d0407baSopenharmony_ci
1503d0407baSopenharmony_cistatic int rockchip_bus_power_control_init(struct rockchip_bus *bus)
1513d0407baSopenharmony_ci{
1523d0407baSopenharmony_ci	struct device *dev = bus->dev;
1533d0407baSopenharmony_ci	int ret = 0;
1543d0407baSopenharmony_ci
1553d0407baSopenharmony_ci	bus->clk = devm_clk_get(dev, "bus");
1563d0407baSopenharmony_ci	if (IS_ERR(bus->clk)) {
1573d0407baSopenharmony_ci		dev_err(dev, "failed to get bus clock\n");
1583d0407baSopenharmony_ci		return PTR_ERR(bus->clk);
1593d0407baSopenharmony_ci	}
1603d0407baSopenharmony_ci
1613d0407baSopenharmony_ci	bus->regulator = devm_regulator_get(dev, "bus");
1623d0407baSopenharmony_ci	if (IS_ERR(bus->regulator)) {
1633d0407baSopenharmony_ci		dev_err(dev, "failed to get bus regulator\n");
1643d0407baSopenharmony_ci		return PTR_ERR(bus->regulator);
1653d0407baSopenharmony_ci	}
1663d0407baSopenharmony_ci
1673d0407baSopenharmony_ci	ret = rockchip_init_opp_table(dev, NULL, "leakage", "pvtm");
1683d0407baSopenharmony_ci	if (ret < 0) {
1693d0407baSopenharmony_ci		dev_err(dev, "failed to get OPP table\n");
1703d0407baSopenharmony_ci		return ret;
1713d0407baSopenharmony_ci	}
1723d0407baSopenharmony_ci
1733d0407baSopenharmony_ci	ret = rockchip_bus_set_freq_table(bus);
1743d0407baSopenharmony_ci	if (ret < 0) {
1753d0407baSopenharmony_ci		dev_err(dev, "failed to set bus freq table\n");
1763d0407baSopenharmony_ci		return ret;
1773d0407baSopenharmony_ci	}
1783d0407baSopenharmony_ci
1793d0407baSopenharmony_ci	return 0;
1803d0407baSopenharmony_ci}
1813d0407baSopenharmony_ci
1823d0407baSopenharmony_cistatic int rockchip_bus_clkfreq_target(struct device *dev, unsigned long freq)
1833d0407baSopenharmony_ci{
1843d0407baSopenharmony_ci	struct rockchip_bus *bus = dev_get_drvdata(dev);
1853d0407baSopenharmony_ci	unsigned long target_volt = bus->freq_table[bus->max_state - 1].volt;
1863d0407baSopenharmony_ci	int i;
1873d0407baSopenharmony_ci
1883d0407baSopenharmony_ci	for (i = 0; i < bus->max_state; i++) {
1893d0407baSopenharmony_ci		if (freq <= bus->freq_table[i].freq) {
1903d0407baSopenharmony_ci			target_volt = bus->freq_table[i].volt;
1913d0407baSopenharmony_ci			break;
1923d0407baSopenharmony_ci		}
1933d0407baSopenharmony_ci	}
1943d0407baSopenharmony_ci
1953d0407baSopenharmony_ci	if (bus->cur_volt != target_volt) {
1963d0407baSopenharmony_ci		dev_dbg(bus->dev, "target_volt: %lu\n", target_volt);
1973d0407baSopenharmony_ci		if (regulator_set_voltage(bus->regulator, target_volt,
1983d0407baSopenharmony_ci					  INT_MAX)) {
1993d0407baSopenharmony_ci			dev_err(dev, "failed to set voltage %lu uV\n",
2003d0407baSopenharmony_ci				target_volt);
2013d0407baSopenharmony_ci			return -EINVAL;
2023d0407baSopenharmony_ci		}
2033d0407baSopenharmony_ci		bus->cur_volt = target_volt;
2043d0407baSopenharmony_ci	}
2053d0407baSopenharmony_ci
2063d0407baSopenharmony_ci	return 0;
2073d0407baSopenharmony_ci}
2083d0407baSopenharmony_ci
2093d0407baSopenharmony_cistatic int rockchip_bus_clk_notifier(struct notifier_block *nb,
2103d0407baSopenharmony_ci				     unsigned long event, void *data)
2113d0407baSopenharmony_ci{
2123d0407baSopenharmony_ci	struct clk_notifier_data *ndata = data;
2133d0407baSopenharmony_ci	struct rockchip_bus *bus = to_rockchip_bus_clk_nb(nb);
2143d0407baSopenharmony_ci	int ret = 0;
2153d0407baSopenharmony_ci
2163d0407baSopenharmony_ci	dev_dbg(bus->dev, "event %lu, old_rate %lu, new_rate: %lu\n",
2173d0407baSopenharmony_ci		event, ndata->old_rate, ndata->new_rate);
2183d0407baSopenharmony_ci
2193d0407baSopenharmony_ci	switch (event) {
2203d0407baSopenharmony_ci	case PRE_RATE_CHANGE:
2213d0407baSopenharmony_ci		if (ndata->new_rate > ndata->old_rate)
2223d0407baSopenharmony_ci			ret = rockchip_bus_clkfreq_target(bus->dev,
2233d0407baSopenharmony_ci							  ndata->new_rate);
2243d0407baSopenharmony_ci		break;
2253d0407baSopenharmony_ci	case POST_RATE_CHANGE:
2263d0407baSopenharmony_ci		if (ndata->new_rate < ndata->old_rate)
2273d0407baSopenharmony_ci			ret = rockchip_bus_clkfreq_target(bus->dev,
2283d0407baSopenharmony_ci							  ndata->new_rate);
2293d0407baSopenharmony_ci		break;
2303d0407baSopenharmony_ci	case ABORT_RATE_CHANGE:
2313d0407baSopenharmony_ci		if (ndata->new_rate > ndata->old_rate)
2323d0407baSopenharmony_ci			ret = rockchip_bus_clkfreq_target(bus->dev,
2333d0407baSopenharmony_ci							  ndata->old_rate);
2343d0407baSopenharmony_ci		break;
2353d0407baSopenharmony_ci	default:
2363d0407baSopenharmony_ci		break;
2373d0407baSopenharmony_ci	}
2383d0407baSopenharmony_ci
2393d0407baSopenharmony_ci	return notifier_from_errno(ret);
2403d0407baSopenharmony_ci}
2413d0407baSopenharmony_ci
2423d0407baSopenharmony_cistatic int rockchip_bus_clkfreq(struct rockchip_bus *bus)
2433d0407baSopenharmony_ci{
2443d0407baSopenharmony_ci	struct device *dev = bus->dev;
2453d0407baSopenharmony_ci	unsigned long init_rate;
2463d0407baSopenharmony_ci	int ret = 0;
2473d0407baSopenharmony_ci
2483d0407baSopenharmony_ci	ret = rockchip_bus_power_control_init(bus);
2493d0407baSopenharmony_ci	if (ret) {
2503d0407baSopenharmony_ci		dev_err(dev, "failed to init power control\n");
2513d0407baSopenharmony_ci		return ret;
2523d0407baSopenharmony_ci	}
2533d0407baSopenharmony_ci
2543d0407baSopenharmony_ci	init_rate = clk_get_rate(bus->clk);
2553d0407baSopenharmony_ci	ret = rockchip_bus_clkfreq_target(dev, init_rate);
2563d0407baSopenharmony_ci	if (ret)
2573d0407baSopenharmony_ci		return ret;
2583d0407baSopenharmony_ci
2593d0407baSopenharmony_ci	bus->clk_nb.notifier_call = rockchip_bus_clk_notifier;
2603d0407baSopenharmony_ci	ret = clk_notifier_register(bus->clk, &bus->clk_nb);
2613d0407baSopenharmony_ci	if (ret) {
2623d0407baSopenharmony_ci		dev_err(dev, "failed to register clock notifier\n");
2633d0407baSopenharmony_ci		return ret;
2643d0407baSopenharmony_ci	}
2653d0407baSopenharmony_ci
2663d0407baSopenharmony_ci	return 0;
2673d0407baSopenharmony_ci}
2683d0407baSopenharmony_ci
2693d0407baSopenharmony_cistatic int rockchip_bus_cpufreq_target(struct device *dev, unsigned long freq,
2703d0407baSopenharmony_ci				       u32 flags)
2713d0407baSopenharmony_ci{
2723d0407baSopenharmony_ci	struct rockchip_bus *bus = dev_get_drvdata(dev);
2733d0407baSopenharmony_ci	struct dev_pm_opp *opp;
2743d0407baSopenharmony_ci	unsigned long target_volt, target_rate = freq;
2753d0407baSopenharmony_ci	int ret = 0;
2763d0407baSopenharmony_ci
2773d0407baSopenharmony_ci	if (!bus->regulator) {
2783d0407baSopenharmony_ci		dev_dbg(dev, "%luHz -> %luHz\n", bus->cur_rate, target_rate);
2793d0407baSopenharmony_ci		ret = clk_set_rate(bus->clk, target_rate);
2803d0407baSopenharmony_ci		if (ret)
2813d0407baSopenharmony_ci			dev_err(bus->dev, "failed to set bus rate %lu\n",
2823d0407baSopenharmony_ci				target_rate);
2833d0407baSopenharmony_ci		else
2843d0407baSopenharmony_ci			bus->cur_rate = target_rate;
2853d0407baSopenharmony_ci		return ret;
2863d0407baSopenharmony_ci	}
2873d0407baSopenharmony_ci
2883d0407baSopenharmony_ci	opp = devfreq_recommended_opp(dev, &target_rate, flags);
2893d0407baSopenharmony_ci	if (IS_ERR(opp)) {
2903d0407baSopenharmony_ci		dev_err(dev, "failed to recommended opp %lu\n", target_rate);
2913d0407baSopenharmony_ci		return PTR_ERR(opp);
2923d0407baSopenharmony_ci	}
2933d0407baSopenharmony_ci	target_volt = dev_pm_opp_get_voltage(opp);
2943d0407baSopenharmony_ci	dev_pm_opp_put(opp);
2953d0407baSopenharmony_ci
2963d0407baSopenharmony_ci	if (bus->cur_rate == target_rate) {
2973d0407baSopenharmony_ci		if (bus->cur_volt == target_volt)
2983d0407baSopenharmony_ci			return 0;
2993d0407baSopenharmony_ci		ret = regulator_set_voltage(bus->regulator, target_volt,
3003d0407baSopenharmony_ci					    INT_MAX);
3013d0407baSopenharmony_ci		if (ret) {
3023d0407baSopenharmony_ci			dev_err(dev, "failed to set voltage %lu\n",
3033d0407baSopenharmony_ci				target_volt);
3043d0407baSopenharmony_ci			return ret;
3053d0407baSopenharmony_ci		}
3063d0407baSopenharmony_ci		bus->cur_volt = target_volt;
3073d0407baSopenharmony_ci		return 0;
3083d0407baSopenharmony_ci	} else if (!bus->cur_volt) {
3093d0407baSopenharmony_ci		bus->cur_volt = regulator_get_voltage(bus->regulator);
3103d0407baSopenharmony_ci	}
3113d0407baSopenharmony_ci
3123d0407baSopenharmony_ci	if (bus->cur_rate < target_rate) {
3133d0407baSopenharmony_ci		ret = regulator_set_voltage(bus->regulator, target_volt,
3143d0407baSopenharmony_ci					    INT_MAX);
3153d0407baSopenharmony_ci		if (ret) {
3163d0407baSopenharmony_ci			dev_err(dev, "failed to set voltage %lu\n",
3173d0407baSopenharmony_ci				target_volt);
3183d0407baSopenharmony_ci			return ret;
3193d0407baSopenharmony_ci		}
3203d0407baSopenharmony_ci	}
3213d0407baSopenharmony_ci
3223d0407baSopenharmony_ci	ret = clk_set_rate(bus->clk, target_rate);
3233d0407baSopenharmony_ci	if (ret) {
3243d0407baSopenharmony_ci		dev_err(dev, "failed to set bus rate %lu\n", target_rate);
3253d0407baSopenharmony_ci		return ret;
3263d0407baSopenharmony_ci	}
3273d0407baSopenharmony_ci
3283d0407baSopenharmony_ci	if (bus->cur_rate > target_rate) {
3293d0407baSopenharmony_ci		ret = regulator_set_voltage(bus->regulator, target_volt,
3303d0407baSopenharmony_ci					    INT_MAX);
3313d0407baSopenharmony_ci		if (ret) {
3323d0407baSopenharmony_ci			dev_err(dev, "failed to set voltage %lu\n",
3333d0407baSopenharmony_ci				target_volt);
3343d0407baSopenharmony_ci			return ret;
3353d0407baSopenharmony_ci		}
3363d0407baSopenharmony_ci	}
3373d0407baSopenharmony_ci
3383d0407baSopenharmony_ci	dev_dbg(dev, "%luHz %luuV -> %luHz %luuV\n", bus->cur_rate,
3393d0407baSopenharmony_ci		bus->cur_volt, target_rate, target_volt);
3403d0407baSopenharmony_ci	bus->cur_rate = target_rate;
3413d0407baSopenharmony_ci	bus->cur_volt = target_volt;
3423d0407baSopenharmony_ci
3433d0407baSopenharmony_ci	return ret;
3443d0407baSopenharmony_ci}
3453d0407baSopenharmony_ci
3463d0407baSopenharmony_cistatic int rockchip_bus_cpufreq_notifier(struct notifier_block *nb,
3473d0407baSopenharmony_ci					 unsigned long event, void *data)
3483d0407baSopenharmony_ci{
3493d0407baSopenharmony_ci	struct rockchip_bus *bus = to_rockchip_bus_cpufreq_nb(nb);
3503d0407baSopenharmony_ci	struct cpufreq_freqs *freqs = data;
3513d0407baSopenharmony_ci	int id = topology_physical_package_id(freqs->policy->cpu);
3523d0407baSopenharmony_ci
3533d0407baSopenharmony_ci	if (id < 0 || id >= MAX_CLUSTERS)
3543d0407baSopenharmony_ci		return NOTIFY_DONE;
3553d0407baSopenharmony_ci
3563d0407baSopenharmony_ci	bus->cpu_freq[id] = freqs->new;
3573d0407baSopenharmony_ci
3583d0407baSopenharmony_ci	if (!bus->cpu_freq[CLUSTER0] || !bus->cpu_freq[CLUSTER1])
3593d0407baSopenharmony_ci		return NOTIFY_DONE;
3603d0407baSopenharmony_ci
3613d0407baSopenharmony_ci	switch (event) {
3623d0407baSopenharmony_ci	case CPUFREQ_PRECHANGE:
3633d0407baSopenharmony_ci		if ((bus->cpu_freq[CLUSTER0] > bus->cpu_high_freq ||
3643d0407baSopenharmony_ci		     bus->cpu_freq[CLUSTER1] > bus->cpu_high_freq) &&
3653d0407baSopenharmony_ci		     bus->cur_rate != bus->high_rate) {
3663d0407baSopenharmony_ci			dev_dbg(bus->dev, "cpu%d freq=%d %d, up cci rate to %lu\n",
3673d0407baSopenharmony_ci				freqs->policy->cpu,
3683d0407baSopenharmony_ci				bus->cpu_freq[CLUSTER0],
3693d0407baSopenharmony_ci				bus->cpu_freq[CLUSTER1],
3703d0407baSopenharmony_ci				bus->high_rate);
3713d0407baSopenharmony_ci			rockchip_bus_cpufreq_target(bus->dev, bus->high_rate,
3723d0407baSopenharmony_ci						    0);
3733d0407baSopenharmony_ci		}
3743d0407baSopenharmony_ci		break;
3753d0407baSopenharmony_ci	case CPUFREQ_POSTCHANGE:
3763d0407baSopenharmony_ci		if (bus->cpu_freq[CLUSTER0] <= bus->cpu_high_freq &&
3773d0407baSopenharmony_ci		    bus->cpu_freq[CLUSTER1] <= bus->cpu_high_freq &&
3783d0407baSopenharmony_ci		    bus->cur_rate != bus->low_rate) {
3793d0407baSopenharmony_ci			dev_dbg(bus->dev, "cpu%d freq=%d %d, down cci rate to %lu\n",
3803d0407baSopenharmony_ci				freqs->policy->cpu,
3813d0407baSopenharmony_ci				bus->cpu_freq[CLUSTER0],
3823d0407baSopenharmony_ci				bus->cpu_freq[CLUSTER1],
3833d0407baSopenharmony_ci				bus->low_rate);
3843d0407baSopenharmony_ci			rockchip_bus_cpufreq_target(bus->dev, bus->low_rate,
3853d0407baSopenharmony_ci						    0);
3863d0407baSopenharmony_ci		}
3873d0407baSopenharmony_ci		break;
3883d0407baSopenharmony_ci	}
3893d0407baSopenharmony_ci
3903d0407baSopenharmony_ci	return NOTIFY_OK;
3913d0407baSopenharmony_ci}
3923d0407baSopenharmony_ci
3933d0407baSopenharmony_cistatic int rockchip_bus_cpufreq(struct rockchip_bus *bus)
3943d0407baSopenharmony_ci{
3953d0407baSopenharmony_ci	struct device *dev = bus->dev;
3963d0407baSopenharmony_ci	struct device_node *np = dev->of_node;
3973d0407baSopenharmony_ci	unsigned int freq;
3983d0407baSopenharmony_ci	int ret = 0;
3993d0407baSopenharmony_ci
4003d0407baSopenharmony_ci	if (of_parse_phandle(dev->of_node, "operating-points-v2", 0)) {
4013d0407baSopenharmony_ci		ret = rockchip_bus_power_control_init(bus);
4023d0407baSopenharmony_ci		if (ret) {
4033d0407baSopenharmony_ci			dev_err(dev, "failed to init power control\n");
4043d0407baSopenharmony_ci			return ret;
4053d0407baSopenharmony_ci		}
4063d0407baSopenharmony_ci	} else {
4073d0407baSopenharmony_ci		bus->clk = devm_clk_get(dev, "bus");
4083d0407baSopenharmony_ci		if (IS_ERR(bus->clk)) {
4093d0407baSopenharmony_ci			dev_err(dev, "failed to get bus clock\n");
4103d0407baSopenharmony_ci			return PTR_ERR(bus->clk);
4113d0407baSopenharmony_ci		}
4123d0407baSopenharmony_ci		bus->regulator = NULL;
4133d0407baSopenharmony_ci	}
4143d0407baSopenharmony_ci
4153d0407baSopenharmony_ci	ret = of_property_read_u32(np, "cpu-high-freq", &bus->cpu_high_freq);
4163d0407baSopenharmony_ci	if (ret) {
4173d0407baSopenharmony_ci		dev_err(dev, "failed to get cpu-high-freq\n");
4183d0407baSopenharmony_ci		return ret;
4193d0407baSopenharmony_ci	}
4203d0407baSopenharmony_ci	ret = of_property_read_u32(np, "cci-high-freq", &freq);
4213d0407baSopenharmony_ci	if (ret) {
4223d0407baSopenharmony_ci		dev_err(dev, "failed to get cci-high-freq\n");
4233d0407baSopenharmony_ci		return ret;
4243d0407baSopenharmony_ci	}
4253d0407baSopenharmony_ci	bus->high_rate = freq * 1000;
4263d0407baSopenharmony_ci	ret = of_property_read_u32(np, "cci-low-freq", &freq);
4273d0407baSopenharmony_ci	if (ret) {
4283d0407baSopenharmony_ci		dev_err(dev, "failed to get cci-low-freq\n");
4293d0407baSopenharmony_ci		return ret;
4303d0407baSopenharmony_ci	}
4313d0407baSopenharmony_ci	bus->low_rate = freq * 1000;
4323d0407baSopenharmony_ci
4333d0407baSopenharmony_ci	bus->cpufreq_nb.notifier_call = rockchip_bus_cpufreq_notifier;
4343d0407baSopenharmony_ci	ret = cpufreq_register_notifier(&bus->cpufreq_nb,
4353d0407baSopenharmony_ci					CPUFREQ_TRANSITION_NOTIFIER);
4363d0407baSopenharmony_ci	if (ret) {
4373d0407baSopenharmony_ci		dev_err(dev, "failed to register cpufreq notifier\n");
4383d0407baSopenharmony_ci		return ret;
4393d0407baSopenharmony_ci	}
4403d0407baSopenharmony_ci
4413d0407baSopenharmony_ci	return 0;
4423d0407baSopenharmony_ci}
4433d0407baSopenharmony_ci
4443d0407baSopenharmony_cistatic const struct of_device_id rockchip_busfreq_of_match[] = {
4453d0407baSopenharmony_ci	{ .compatible = "rockchip,px30-bus", },
4463d0407baSopenharmony_ci	{ .compatible = "rockchip,rk1808-bus", },
4473d0407baSopenharmony_ci	{ .compatible = "rockchip,rk3288-bus", },
4483d0407baSopenharmony_ci	{ .compatible = "rockchip,rk3368-bus", },
4493d0407baSopenharmony_ci	{ .compatible = "rockchip,rk3399-bus", },
4503d0407baSopenharmony_ci	{ .compatible = "rockchip,rk3568-bus", },
4513d0407baSopenharmony_ci	{ .compatible = "rockchip,rv1126-bus", },
4523d0407baSopenharmony_ci	{ },
4533d0407baSopenharmony_ci};
4543d0407baSopenharmony_ci
4553d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_busfreq_of_match);
4563d0407baSopenharmony_ci
4573d0407baSopenharmony_cistatic int rockchip_busfreq_probe(struct platform_device *pdev)
4583d0407baSopenharmony_ci{
4593d0407baSopenharmony_ci	struct device *dev = &pdev->dev;
4603d0407baSopenharmony_ci	struct device_node *np = dev->of_node;
4613d0407baSopenharmony_ci	struct rockchip_bus *bus;
4623d0407baSopenharmony_ci	const char *policy_name;
4633d0407baSopenharmony_ci	int ret = 0;
4643d0407baSopenharmony_ci
4653d0407baSopenharmony_ci	bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL);
4663d0407baSopenharmony_ci	if (!bus)
4673d0407baSopenharmony_ci		return -ENOMEM;
4683d0407baSopenharmony_ci	bus->dev = dev;
4693d0407baSopenharmony_ci	platform_set_drvdata(pdev, bus);
4703d0407baSopenharmony_ci
4713d0407baSopenharmony_ci	ret = of_property_read_string(np, "rockchip,busfreq-policy",
4723d0407baSopenharmony_ci				      &policy_name);
4733d0407baSopenharmony_ci	if (ret) {
4743d0407baSopenharmony_ci		dev_info(dev, "failed to get busfreq policy\n");
4753d0407baSopenharmony_ci		return ret;
4763d0407baSopenharmony_ci	}
4773d0407baSopenharmony_ci
4783d0407baSopenharmony_ci	if (!strcmp(policy_name, "smc"))
4793d0407baSopenharmony_ci		ret = rockchip_bus_smc_config(bus);
4803d0407baSopenharmony_ci	else if (!strcmp(policy_name, "clkfreq"))
4813d0407baSopenharmony_ci		ret = rockchip_bus_clkfreq(bus);
4823d0407baSopenharmony_ci	else if (!strcmp(policy_name, "cpufreq"))
4833d0407baSopenharmony_ci		ret = rockchip_bus_cpufreq(bus);
4843d0407baSopenharmony_ci
4853d0407baSopenharmony_ci	return ret;
4863d0407baSopenharmony_ci}
4873d0407baSopenharmony_ci
4883d0407baSopenharmony_cistatic struct platform_driver rockchip_busfreq_driver = {
4893d0407baSopenharmony_ci	.probe	= rockchip_busfreq_probe,
4903d0407baSopenharmony_ci	.driver = {
4913d0407baSopenharmony_ci		.name	= "rockchip,bus",
4923d0407baSopenharmony_ci		.of_match_table = rockchip_busfreq_of_match,
4933d0407baSopenharmony_ci	},
4943d0407baSopenharmony_ci};
4953d0407baSopenharmony_ci
4963d0407baSopenharmony_cimodule_platform_driver(rockchip_busfreq_driver);
4973d0407baSopenharmony_ci
4983d0407baSopenharmony_ciMODULE_LICENSE("GPL v2");
4993d0407baSopenharmony_ciMODULE_AUTHOR("Tony Xie <tony.xie@rock-chips.com>");
5003d0407baSopenharmony_ciMODULE_DESCRIPTION("rockchip busfreq driver with devfreq framework");
501