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