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