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