13d0407baSopenharmony_ci/*
23d0407baSopenharmony_ci * Rockchip CPUFreq Driver
33d0407baSopenharmony_ci *
43d0407baSopenharmony_ci * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
53d0407baSopenharmony_ci *
63d0407baSopenharmony_ci * This program is free software; you can redistribute it and/or modify
73d0407baSopenharmony_ci * it under the terms of the GNU General Public License version 2 as
83d0407baSopenharmony_ci * published by the Free Software Foundation.
93d0407baSopenharmony_ci *
103d0407baSopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
113d0407baSopenharmony_ci * kind, whether express or implied; without even the implied warranty
123d0407baSopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
133d0407baSopenharmony_ci * GNU General Public License for more details.
143d0407baSopenharmony_ci */
153d0407baSopenharmony_ci
163d0407baSopenharmony_ci#include <linux/clk.h>
173d0407baSopenharmony_ci#include <linux/cpu.h>
183d0407baSopenharmony_ci#include <linux/cpufreq.h>
193d0407baSopenharmony_ci#include <linux/err.h>
203d0407baSopenharmony_ci#include <linux/init.h>
213d0407baSopenharmony_ci#include <linux/kernel.h>
223d0407baSopenharmony_ci#include <linux/mfd/syscon.h>
233d0407baSopenharmony_ci#include <linux/module.h>
243d0407baSopenharmony_ci#include <linux/nvmem-consumer.h>
253d0407baSopenharmony_ci#include <linux/of.h>
263d0407baSopenharmony_ci#include <linux/of_address.h>
273d0407baSopenharmony_ci#include <linux/platform_device.h>
283d0407baSopenharmony_ci#include <linux/pm_opp.h>
293d0407baSopenharmony_ci#include <linux/slab.h>
303d0407baSopenharmony_ci#include <linux/regmap.h>
313d0407baSopenharmony_ci#include <linux/regulator/consumer.h>
323d0407baSopenharmony_ci#include <linux/rockchip/cpu.h>
333d0407baSopenharmony_ci#include <soc/rockchip/rockchip_opp_select.h>
343d0407baSopenharmony_ci#include <soc/rockchip/rockchip_system_monitor.h>
353d0407baSopenharmony_ci
363d0407baSopenharmony_ci#include "cpufreq-dt.h"
373d0407baSopenharmony_ci#include "rockchip-cpufreq.h"
383d0407baSopenharmony_ci
393d0407baSopenharmony_cistruct cluster_info {
403d0407baSopenharmony_ci    struct list_head list_head;
413d0407baSopenharmony_ci    struct monitor_dev_info *mdev_info;
423d0407baSopenharmony_ci    struct rockchip_opp_info opp_info;
433d0407baSopenharmony_ci    cpumask_t cpus;
443d0407baSopenharmony_ci    int scale;
453d0407baSopenharmony_ci};
463d0407baSopenharmony_cistatic LIST_HEAD(cluster_info_list);
473d0407baSopenharmony_ci
483d0407baSopenharmony_cistatic int px30_get_soc_info(struct device *dev, struct device_node *np, int *bin, int *process)
493d0407baSopenharmony_ci{
503d0407baSopenharmony_ci    int ret = 0;
513d0407baSopenharmony_ci    u8 value = 0;
523d0407baSopenharmony_ci
533d0407baSopenharmony_ci    if (!bin) {
543d0407baSopenharmony_ci        return 0;
553d0407baSopenharmony_ci    }
563d0407baSopenharmony_ci
573d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", "performance") >= 0) {
583d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, "performance", &value);
593d0407baSopenharmony_ci        if (ret) {
603d0407baSopenharmony_ci            dev_err(dev, "Failed to get soc performance value\n");
613d0407baSopenharmony_ci            return ret;
623d0407baSopenharmony_ci        }
633d0407baSopenharmony_ci        *bin = value;
643d0407baSopenharmony_ci    }
653d0407baSopenharmony_ci    if (*bin >= ROCKCHIP_SOC_INFO_ZERO) {
663d0407baSopenharmony_ci        dev_info(dev, "bin=%d\n", *bin);
673d0407baSopenharmony_ci    }
683d0407baSopenharmony_ci
693d0407baSopenharmony_ci    return ret;
703d0407baSopenharmony_ci}
713d0407baSopenharmony_ci
723d0407baSopenharmony_cistatic int rk3288_get_soc_info(struct device *dev, struct device_node *np, int *bin, int *process)
733d0407baSopenharmony_ci{
743d0407baSopenharmony_ci    int ret = 0;
753d0407baSopenharmony_ci    u8 value = 0;
763d0407baSopenharmony_ci    char *name;
773d0407baSopenharmony_ci
783d0407baSopenharmony_ci    if (!bin) {
793d0407baSopenharmony_ci        goto next;
803d0407baSopenharmony_ci    }
813d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", "special") >= 0) {
823d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, "special", &value);
833d0407baSopenharmony_ci        if (ret) {
843d0407baSopenharmony_ci            dev_err(dev, "Failed to get soc special value\n");
853d0407baSopenharmony_ci            goto out;
863d0407baSopenharmony_ci        }
873d0407baSopenharmony_ci        if (value == 0xc) {
883d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ZERO;
893d0407baSopenharmony_ci        } else {
903d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ONE;
913d0407baSopenharmony_ci        }
923d0407baSopenharmony_ci    }
933d0407baSopenharmony_ci
943d0407baSopenharmony_ci    if (soc_is_rk3288w()) {
953d0407baSopenharmony_ci        name = "performance-w";
963d0407baSopenharmony_ci    } else {
973d0407baSopenharmony_ci        name = "performance";
983d0407baSopenharmony_ci    }
993d0407baSopenharmony_ci
1003d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", name) >= 0) {
1013d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, name, &value);
1023d0407baSopenharmony_ci        if (ret) {
1033d0407baSopenharmony_ci            dev_err(dev, "Failed to get soc performance value\n");
1043d0407baSopenharmony_ci            goto out;
1053d0407baSopenharmony_ci        }
1063d0407baSopenharmony_ci        if (value & 0x2) {
1073d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_THREE;
1083d0407baSopenharmony_ci        } else if (value & 0x01) {
1093d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_TWO;
1103d0407baSopenharmony_ci        }
1113d0407baSopenharmony_ci    }
1123d0407baSopenharmony_ci    if (*bin >= ROCKCHIP_SOC_INFO_ZERO) {
1133d0407baSopenharmony_ci        dev_info(dev, "bin=%d\n", *bin);
1143d0407baSopenharmony_ci    }
1153d0407baSopenharmony_ci
1163d0407baSopenharmony_cinext:
1173d0407baSopenharmony_ci    if (!process) {
1183d0407baSopenharmony_ci        goto out;
1193d0407baSopenharmony_ci    }
1203d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", "process") >= 0) {
1213d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, "process", &value);
1223d0407baSopenharmony_ci        if (ret) {
1233d0407baSopenharmony_ci            dev_err(dev, "Failed to get soc process version\n");
1243d0407baSopenharmony_ci            goto out;
1253d0407baSopenharmony_ci        }
1263d0407baSopenharmony_ci        if (soc_is_rk3288() && (value == 0 || value == 1)) {
1273d0407baSopenharmony_ci            *process = 0;
1283d0407baSopenharmony_ci        }
1293d0407baSopenharmony_ci    }
1303d0407baSopenharmony_ci    if (*process >= 0) {
1313d0407baSopenharmony_ci        dev_info(dev, "process=%d\n", *process);
1323d0407baSopenharmony_ci    }
1333d0407baSopenharmony_ci
1343d0407baSopenharmony_ciout:
1353d0407baSopenharmony_ci    return ret;
1363d0407baSopenharmony_ci}
1373d0407baSopenharmony_ci
1383d0407baSopenharmony_cistatic int rk3399_get_soc_info(struct device *dev, struct device_node *np, int *bin, int *process)
1393d0407baSopenharmony_ci{
1403d0407baSopenharmony_ci    int ret = 0;
1413d0407baSopenharmony_ci    u8 value = 0;
1423d0407baSopenharmony_ci
1433d0407baSopenharmony_ci    if (!bin) {
1443d0407baSopenharmony_ci        return 0;
1453d0407baSopenharmony_ci    }
1463d0407baSopenharmony_ci
1473d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", "specification_serial_number") >= 0) {
1483d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, "specification_serial_number", &value);
1493d0407baSopenharmony_ci        if (ret) {
1503d0407baSopenharmony_ci            dev_err(dev, "Failed to get specification_serial_number\n");
1513d0407baSopenharmony_ci            goto out;
1523d0407baSopenharmony_ci        }
1533d0407baSopenharmony_ci
1543d0407baSopenharmony_ci        if (value == 0xb) {
1553d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ZERO;
1563d0407baSopenharmony_ci        } else if (value == 0x1) {
1573d0407baSopenharmony_ci            if (of_property_match_string(np, "nvmem-cell-names", "customer_demand") >= 0) {
1583d0407baSopenharmony_ci                ret = rockchip_nvmem_cell_read_u8(np, "customer_demand", &value);
1593d0407baSopenharmony_ci                if (ret) {
1603d0407baSopenharmony_ci                    dev_err(dev, "Failed to get customer_demand\n");
1613d0407baSopenharmony_ci                    goto out;
1623d0407baSopenharmony_ci                }
1633d0407baSopenharmony_ci                if (value == 0x0) {
1643d0407baSopenharmony_ci                    *bin = ROCKCHIP_SOC_INFO_ZERO;
1653d0407baSopenharmony_ci                } else {
1663d0407baSopenharmony_ci                    *bin = ROCKCHIP_SOC_INFO_ONE;
1673d0407baSopenharmony_ci                }
1683d0407baSopenharmony_ci            }
1693d0407baSopenharmony_ci        } else if (value == 0x10) {
1703d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ONE;
1713d0407baSopenharmony_ci        }
1723d0407baSopenharmony_ci    }
1733d0407baSopenharmony_ci
1743d0407baSopenharmony_ciout:
1753d0407baSopenharmony_ci    if (*bin >= ROCKCHIP_SOC_INFO_ZERO) {
1763d0407baSopenharmony_ci        dev_info(dev, "bin=%d\n", *bin);
1773d0407baSopenharmony_ci    }
1783d0407baSopenharmony_ci
1793d0407baSopenharmony_ci    return ret;
1803d0407baSopenharmony_ci}
1813d0407baSopenharmony_ci
1823d0407baSopenharmony_cistatic int rk3588_cpu_set_read_margin(struct device *dev, struct rockchip_opp_info *opp_info, unsigned long volt)
1833d0407baSopenharmony_ci{
1843d0407baSopenharmony_ci    bool is_found = false;
1853d0407baSopenharmony_ci    u32 rm;
1863d0407baSopenharmony_ci    int i;
1873d0407baSopenharmony_ci
1883d0407baSopenharmony_ci    if (!opp_info->grf || !opp_info->volt_rm_tbl) {
1893d0407baSopenharmony_ci        return 0;
1903d0407baSopenharmony_ci    }
1913d0407baSopenharmony_ci
1923d0407baSopenharmony_ci    for (i = 0; opp_info->volt_rm_tbl[i].rm != VOLT_RM_TABLE_END; i++) {
1933d0407baSopenharmony_ci        if (volt >= opp_info->volt_rm_tbl[i].volt) {
1943d0407baSopenharmony_ci            rm = opp_info->volt_rm_tbl[i].rm;
1953d0407baSopenharmony_ci            is_found = true;
1963d0407baSopenharmony_ci            break;
1973d0407baSopenharmony_ci        }
1983d0407baSopenharmony_ci    }
1993d0407baSopenharmony_ci
2003d0407baSopenharmony_ci    if (!is_found) {
2013d0407baSopenharmony_ci        return 0;
2023d0407baSopenharmony_ci    }
2033d0407baSopenharmony_ci    if (rm == opp_info->current_rm) {
2043d0407baSopenharmony_ci        return 0;
2053d0407baSopenharmony_ci    }
2063d0407baSopenharmony_ci
2073d0407baSopenharmony_ci    dev_dbg(dev, "set rm to %d\n", rm);
2083d0407baSopenharmony_ci    regmap_write(opp_info->grf, 0x20, 0x001c0000 | (rm << 0x2));
2093d0407baSopenharmony_ci    regmap_write(opp_info->grf, 0x28, 0x003c0000 | (rm << 0x2));
2103d0407baSopenharmony_ci    regmap_write(opp_info->grf, 0x2c, 0x003c0000 | (rm << 0x2));
2113d0407baSopenharmony_ci    regmap_write(opp_info->grf, 0x30, 0x00200020);
2123d0407baSopenharmony_ci    udelay(1);
2133d0407baSopenharmony_ci    regmap_write(opp_info->grf, 0x30, 0x00200000);
2143d0407baSopenharmony_ci
2153d0407baSopenharmony_ci    opp_info->current_rm = rm;
2163d0407baSopenharmony_ci
2173d0407baSopenharmony_ci    return 0;
2183d0407baSopenharmony_ci}
2193d0407baSopenharmony_ci
2203d0407baSopenharmony_cistatic int rv1126_get_soc_info(struct device *dev, struct device_node *np, int *bin, int *process)
2213d0407baSopenharmony_ci{
2223d0407baSopenharmony_ci    int ret = 0;
2233d0407baSopenharmony_ci    u8 value = 0;
2243d0407baSopenharmony_ci
2253d0407baSopenharmony_ci    if (of_property_match_string(np, "nvmem-cell-names", "performance") >= 0) {
2263d0407baSopenharmony_ci        ret = rockchip_nvmem_cell_read_u8(np, "performance", &value);
2273d0407baSopenharmony_ci        if (ret) {
2283d0407baSopenharmony_ci            dev_err(dev, "Failed to get soc performance value\n");
2293d0407baSopenharmony_ci            return ret;
2303d0407baSopenharmony_ci        }
2313d0407baSopenharmony_ci        if (value == 0x1) {
2323d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ONE;
2333d0407baSopenharmony_ci        } else {
2343d0407baSopenharmony_ci            *bin = ROCKCHIP_SOC_INFO_ZERO;
2353d0407baSopenharmony_ci        }
2363d0407baSopenharmony_ci    }
2373d0407baSopenharmony_ci    if (*bin >= ROCKCHIP_SOC_INFO_ZERO) {
2383d0407baSopenharmony_ci        dev_info(dev, "bin=%d\n", *bin);
2393d0407baSopenharmony_ci    }
2403d0407baSopenharmony_ci
2413d0407baSopenharmony_ci    return ret;
2423d0407baSopenharmony_ci}
2433d0407baSopenharmony_ci
2443d0407baSopenharmony_cistatic const struct rockchip_opp_data px30_cpu_opp_data = {
2453d0407baSopenharmony_ci    .get_soc_info = px30_get_soc_info,
2463d0407baSopenharmony_ci};
2473d0407baSopenharmony_ci
2483d0407baSopenharmony_cistatic const struct rockchip_opp_data rk3288_cpu_opp_data = {
2493d0407baSopenharmony_ci    .get_soc_info = rk3288_get_soc_info,
2503d0407baSopenharmony_ci};
2513d0407baSopenharmony_ci
2523d0407baSopenharmony_cistatic const struct rockchip_opp_data rk3399_cpu_opp_data = {
2533d0407baSopenharmony_ci    .get_soc_info = rk3399_get_soc_info,
2543d0407baSopenharmony_ci};
2553d0407baSopenharmony_ci
2563d0407baSopenharmony_cistatic const struct rockchip_opp_data rk3588_cpu_opp_data = {
2573d0407baSopenharmony_ci    .set_read_margin = rk3588_cpu_set_read_margin,
2583d0407baSopenharmony_ci};
2593d0407baSopenharmony_ci
2603d0407baSopenharmony_cistatic const struct rockchip_opp_data rv1126_cpu_opp_data = {
2613d0407baSopenharmony_ci    .get_soc_info = rv1126_get_soc_info,
2623d0407baSopenharmony_ci};
2633d0407baSopenharmony_ci
2643d0407baSopenharmony_cistatic const struct of_device_id rockchip_cpufreq_of_match[] = {
2653d0407baSopenharmony_ci    {
2663d0407baSopenharmony_ci        .compatible = "rockchip,px30",
2673d0407baSopenharmony_ci        .data = (void *)&px30_cpu_opp_data,
2683d0407baSopenharmony_ci    },
2693d0407baSopenharmony_ci    {
2703d0407baSopenharmony_ci        .compatible = "rockchip,rk3288",
2713d0407baSopenharmony_ci        .data = (void *)&rk3288_cpu_opp_data,
2723d0407baSopenharmony_ci    },
2733d0407baSopenharmony_ci    {
2743d0407baSopenharmony_ci        .compatible = "rockchip,rk3288w",
2753d0407baSopenharmony_ci        .data = (void *)&rk3288_cpu_opp_data,
2763d0407baSopenharmony_ci    },
2773d0407baSopenharmony_ci    {
2783d0407baSopenharmony_ci        .compatible = "rockchip,rk3326",
2793d0407baSopenharmony_ci        .data = (void *)&px30_cpu_opp_data,
2803d0407baSopenharmony_ci    },
2813d0407baSopenharmony_ci    {
2823d0407baSopenharmony_ci        .compatible = "rockchip,rk3399",
2833d0407baSopenharmony_ci        .data = (void *)&rk3399_cpu_opp_data,
2843d0407baSopenharmony_ci    },
2853d0407baSopenharmony_ci    {
2863d0407baSopenharmony_ci        .compatible = "rockchip,rk3588",
2873d0407baSopenharmony_ci        .data = (void *)&rk3588_cpu_opp_data,
2883d0407baSopenharmony_ci    },
2893d0407baSopenharmony_ci    {
2903d0407baSopenharmony_ci        .compatible = "rockchip,rv1109",
2913d0407baSopenharmony_ci        .data = (void *)&rv1126_cpu_opp_data,
2923d0407baSopenharmony_ci    },
2933d0407baSopenharmony_ci    {
2943d0407baSopenharmony_ci        .compatible = "rockchip,rv1126",
2953d0407baSopenharmony_ci        .data = (void *)&rv1126_cpu_opp_data,
2963d0407baSopenharmony_ci    },
2973d0407baSopenharmony_ci    {},
2983d0407baSopenharmony_ci};
2993d0407baSopenharmony_ci
3003d0407baSopenharmony_cistatic struct cluster_info *rockchip_cluster_info_lookup(int cpu)
3013d0407baSopenharmony_ci{
3023d0407baSopenharmony_ci    struct cluster_info *cluster;
3033d0407baSopenharmony_ci
3043d0407baSopenharmony_ci    list_for_each_entry(cluster, &cluster_info_list, list_head)
3053d0407baSopenharmony_ci    {
3063d0407baSopenharmony_ci        if (cpumask_test_cpu(cpu, &cluster->cpus)) {
3073d0407baSopenharmony_ci            return cluster;
3083d0407baSopenharmony_ci        }
3093d0407baSopenharmony_ci    }
3103d0407baSopenharmony_ci
3113d0407baSopenharmony_ci    return NULL;
3123d0407baSopenharmony_ci}
3133d0407baSopenharmony_ci
3143d0407baSopenharmony_cistatic int rockchip_cpufreq_set_volt(struct device *dev, struct regulator *reg, struct dev_pm_opp_supply *supply,
3153d0407baSopenharmony_ci                                     char *reg_name)
3163d0407baSopenharmony_ci{
3173d0407baSopenharmony_ci    int ret;
3183d0407baSopenharmony_ci
3193d0407baSopenharmony_ci    dev_dbg(dev, "%s: %s voltages (uV): %lu %lu %lu\n", __func__, reg_name, supply->u_volt_min, supply->u_volt,
3203d0407baSopenharmony_ci            supply->u_volt_max);
3213d0407baSopenharmony_ci
3223d0407baSopenharmony_ci    ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, supply->u_volt, supply->u_volt_max);
3233d0407baSopenharmony_ci    if (ret) {
3243d0407baSopenharmony_ci        dev_err(dev, "%s: failed to set voltage (%lu %lu %lu uV): %d\n", __func__, supply->u_volt_min, supply->u_volt,
3253d0407baSopenharmony_ci                supply->u_volt_max, ret);
3263d0407baSopenharmony_ci    }
3273d0407baSopenharmony_ci
3283d0407baSopenharmony_ci    return ret;
3293d0407baSopenharmony_ci}
3303d0407baSopenharmony_ci
3313d0407baSopenharmony_cistatic int cpu_opp_helper(struct dev_pm_set_opp_data *data)
3323d0407baSopenharmony_ci{
3333d0407baSopenharmony_ci    struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0];
3343d0407baSopenharmony_ci    struct dev_pm_opp_supply *old_supply_mem = &data->old_opp.supplies[1];
3353d0407baSopenharmony_ci    struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0];
3363d0407baSopenharmony_ci    struct dev_pm_opp_supply *new_supply_mem = &data->new_opp.supplies[1];
3373d0407baSopenharmony_ci    struct regulator *vdd_reg = data->regulators[0];
3383d0407baSopenharmony_ci    struct regulator *mem_reg = data->regulators[1];
3393d0407baSopenharmony_ci    struct device *dev = data->dev;
3403d0407baSopenharmony_ci    struct clk *clk = data->clk;
3413d0407baSopenharmony_ci    struct cluster_info *cluster;
3423d0407baSopenharmony_ci    struct rockchip_opp_info *opp_info;
3433d0407baSopenharmony_ci    unsigned long old_freq = data->old_opp.rate;
3443d0407baSopenharmony_ci    unsigned long new_freq = data->new_opp.rate;
3453d0407baSopenharmony_ci    int ret = 0;
3463d0407baSopenharmony_ci
3473d0407baSopenharmony_ci    cluster = rockchip_cluster_info_lookup(dev->id);
3483d0407baSopenharmony_ci    if (!cluster) {
3493d0407baSopenharmony_ci        return -EINVAL;
3503d0407baSopenharmony_ci    }
3513d0407baSopenharmony_ci    opp_info = &cluster->opp_info;
3523d0407baSopenharmony_ci
3533d0407baSopenharmony_ci    /* Scaling up? Scale voltage before frequency */
3543d0407baSopenharmony_ci    if (new_freq >= old_freq) {
3553d0407baSopenharmony_ci        ret = rockchip_cpufreq_set_volt(dev, mem_reg, new_supply_mem, "mem");
3563d0407baSopenharmony_ci        if (ret) {
3573d0407baSopenharmony_ci            goto restore_voltage;
3583d0407baSopenharmony_ci        }
3593d0407baSopenharmony_ci        ret = rockchip_cpufreq_set_volt(dev, vdd_reg, new_supply_vdd, "vdd");
3603d0407baSopenharmony_ci        if (ret) {
3613d0407baSopenharmony_ci            goto restore_voltage;
3623d0407baSopenharmony_ci        }
3633d0407baSopenharmony_ci        if (opp_info->data->set_read_margin) {
3643d0407baSopenharmony_ci            opp_info->data->set_read_margin(dev, opp_info, new_supply_vdd->u_volt);
3653d0407baSopenharmony_ci        }
3663d0407baSopenharmony_ci    }
3673d0407baSopenharmony_ci
3683d0407baSopenharmony_ci    /* Change frequency */
3693d0407baSopenharmony_ci    dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, old_freq, new_freq);
3703d0407baSopenharmony_ci    ret = clk_set_rate(clk, new_freq);
3713d0407baSopenharmony_ci    if (ret) {
3723d0407baSopenharmony_ci        dev_err(dev, "%s: failed to set clk rate: %d\n", __func__, ret);
3733d0407baSopenharmony_ci        goto restore_rm;
3743d0407baSopenharmony_ci    }
3753d0407baSopenharmony_ci
3763d0407baSopenharmony_ci    /* Scaling down? Scale voltage after frequency */
3773d0407baSopenharmony_ci    if (new_freq < old_freq) {
3783d0407baSopenharmony_ci        if (opp_info->data->set_read_margin) {
3793d0407baSopenharmony_ci            opp_info->data->set_read_margin(dev, opp_info, new_supply_vdd->u_volt);
3803d0407baSopenharmony_ci        }
3813d0407baSopenharmony_ci        ret = rockchip_cpufreq_set_volt(dev, vdd_reg, new_supply_vdd, "vdd");
3823d0407baSopenharmony_ci        if (ret) {
3833d0407baSopenharmony_ci            goto restore_freq;
3843d0407baSopenharmony_ci        }
3853d0407baSopenharmony_ci        ret = rockchip_cpufreq_set_volt(dev, mem_reg, new_supply_mem, "mem");
3863d0407baSopenharmony_ci        if (ret) {
3873d0407baSopenharmony_ci            goto restore_freq;
3883d0407baSopenharmony_ci        }
3893d0407baSopenharmony_ci    }
3903d0407baSopenharmony_ci
3913d0407baSopenharmony_ci    return 0;
3923d0407baSopenharmony_ci
3933d0407baSopenharmony_cirestore_freq:
3943d0407baSopenharmony_ci    if (clk_set_rate(clk, old_freq)) {
3953d0407baSopenharmony_ci        dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", __func__, old_freq);
3963d0407baSopenharmony_ci    }
3973d0407baSopenharmony_cirestore_rm:
3983d0407baSopenharmony_ci    if (opp_info->data->set_read_margin) {
3993d0407baSopenharmony_ci        opp_info->data->set_read_margin(dev, opp_info, old_supply_vdd->u_volt);
4003d0407baSopenharmony_ci    }
4013d0407baSopenharmony_cirestore_voltage:
4023d0407baSopenharmony_ci    rockchip_cpufreq_set_volt(dev, mem_reg, old_supply_mem, "mem");
4033d0407baSopenharmony_ci    rockchip_cpufreq_set_volt(dev, vdd_reg, old_supply_vdd, "vdd");
4043d0407baSopenharmony_ci
4053d0407baSopenharmony_ci    return ret;
4063d0407baSopenharmony_ci}
4073d0407baSopenharmony_ci
4083d0407baSopenharmony_cistatic int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
4093d0407baSopenharmony_ci{
4103d0407baSopenharmony_ci    struct rockchip_opp_info *opp_info = &cluster->opp_info;
4113d0407baSopenharmony_ci    struct opp_table *pname_table = NULL;
4123d0407baSopenharmony_ci    struct opp_table *reg_table = NULL;
4133d0407baSopenharmony_ci    struct opp_table *opp_table;
4143d0407baSopenharmony_ci    struct device_node *np;
4153d0407baSopenharmony_ci    struct device *dev;
4163d0407baSopenharmony_ci    const char *const reg_names[] = {"cpu", "mem"};
4173d0407baSopenharmony_ci    char *reg_name = NULL;
4183d0407baSopenharmony_ci    int bin = -EINVAL;
4193d0407baSopenharmony_ci    int process = -EINVAL;
4203d0407baSopenharmony_ci    int volt_sel = -EINVAL;
4213d0407baSopenharmony_ci    int ret = 0;
4223d0407baSopenharmony_ci
4233d0407baSopenharmony_ci    dev = get_cpu_device(cpu);
4243d0407baSopenharmony_ci    if (!dev) {
4253d0407baSopenharmony_ci        return -ENODEV;
4263d0407baSopenharmony_ci    }
4273d0407baSopenharmony_ci
4283d0407baSopenharmony_ci    if (of_find_property(dev->of_node, "cpu-supply", NULL)) {
4293d0407baSopenharmony_ci        reg_name = "cpu";
4303d0407baSopenharmony_ci    } else if (of_find_property(dev->of_node, "cpu0-supply", NULL)) {
4313d0407baSopenharmony_ci        reg_name = "cpu0";
4323d0407baSopenharmony_ci    } else {
4333d0407baSopenharmony_ci        return -ENOENT;
4343d0407baSopenharmony_ci    }
4353d0407baSopenharmony_ci
4363d0407baSopenharmony_ci    np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
4373d0407baSopenharmony_ci    if (!np) {
4383d0407baSopenharmony_ci        dev_warn(dev, "OPP-v2 not supported\n");
4393d0407baSopenharmony_ci        return -ENOENT;
4403d0407baSopenharmony_ci    }
4413d0407baSopenharmony_ci
4423d0407baSopenharmony_ci    ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus);
4433d0407baSopenharmony_ci    if (ret) {
4443d0407baSopenharmony_ci        dev_err(dev, "Failed to get sharing cpus\n");
4453d0407baSopenharmony_ci        goto np_err;
4463d0407baSopenharmony_ci    }
4473d0407baSopenharmony_ci
4483d0407baSopenharmony_ci    rockchip_get_opp_data(rockchip_cpufreq_of_match, opp_info);
4493d0407baSopenharmony_ci    if (opp_info->data && opp_info->data->set_read_margin) {
4503d0407baSopenharmony_ci        opp_info->current_rm = UINT_MAX;
4513d0407baSopenharmony_ci        opp_info->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
4523d0407baSopenharmony_ci        if (IS_ERR(opp_info->grf)) {
4533d0407baSopenharmony_ci            opp_info->grf = NULL;
4543d0407baSopenharmony_ci        }
4553d0407baSopenharmony_ci        rockchip_get_volt_rm_table(dev, np, "volt-mem-read-margin", &opp_info->volt_rm_tbl);
4563d0407baSopenharmony_ci    }
4573d0407baSopenharmony_ci    if (opp_info->data && opp_info->data->get_soc_info) {
4583d0407baSopenharmony_ci        opp_info->data->get_soc_info(dev, np, &bin, &process);
4593d0407baSopenharmony_ci    }
4603d0407baSopenharmony_ci    rockchip_get_scale_volt_sel(dev, "cpu_leakage", reg_name, bin, process, &cluster->scale, &volt_sel);
4613d0407baSopenharmony_ci    pname_table = rockchip_set_opp_prop_name(dev, process, volt_sel);
4623d0407baSopenharmony_ci    if (IS_ERR(pname_table)) {
4633d0407baSopenharmony_ci        ret = PTR_ERR(pname_table);
4643d0407baSopenharmony_ci        goto np_err;
4653d0407baSopenharmony_ci    }
4663d0407baSopenharmony_ci
4673d0407baSopenharmony_ci    if (of_find_property(dev->of_node, "cpu-supply", NULL) && of_find_property(dev->of_node, "mem-supply", NULL)) {
4683d0407baSopenharmony_ci        reg_table = dev_pm_opp_set_regulators(dev, reg_names, ARRAY_SIZE(reg_names));
4693d0407baSopenharmony_ci        if (IS_ERR(reg_table)) {
4703d0407baSopenharmony_ci            ret = PTR_ERR(reg_table);
4713d0407baSopenharmony_ci            goto pname_opp_table;
4723d0407baSopenharmony_ci        }
4733d0407baSopenharmony_ci        opp_table = dev_pm_opp_register_set_opp_helper(dev, cpu_opp_helper);
4743d0407baSopenharmony_ci        if (IS_ERR(opp_table)) {
4753d0407baSopenharmony_ci            ret = PTR_ERR(opp_table);
4763d0407baSopenharmony_ci            goto reg_opp_table;
4773d0407baSopenharmony_ci        }
4783d0407baSopenharmony_ci    }
4793d0407baSopenharmony_ci
4803d0407baSopenharmony_ci    of_node_put(np);
4813d0407baSopenharmony_ci
4823d0407baSopenharmony_ci    return 0;
4833d0407baSopenharmony_ci
4843d0407baSopenharmony_cireg_opp_table:
4853d0407baSopenharmony_ci    if (reg_table) {
4863d0407baSopenharmony_ci        dev_pm_opp_put_regulators(reg_table);
4873d0407baSopenharmony_ci    }
4883d0407baSopenharmony_cipname_opp_table:
4893d0407baSopenharmony_ci    if (pname_table) {
4903d0407baSopenharmony_ci        dev_pm_opp_put_prop_name(pname_table);
4913d0407baSopenharmony_ci    }
4923d0407baSopenharmony_cinp_err:
4933d0407baSopenharmony_ci    of_node_put(np);
4943d0407baSopenharmony_ci
4953d0407baSopenharmony_ci    return ret;
4963d0407baSopenharmony_ci}
4973d0407baSopenharmony_ci
4983d0407baSopenharmony_ciint rockchip_cpufreq_adjust_power_scale(struct device *dev)
4993d0407baSopenharmony_ci{
5003d0407baSopenharmony_ci    struct cluster_info *cluster;
5013d0407baSopenharmony_ci
5023d0407baSopenharmony_ci    cluster = rockchip_cluster_info_lookup(dev->id);
5033d0407baSopenharmony_ci    if (!cluster) {
5043d0407baSopenharmony_ci        return -EINVAL;
5053d0407baSopenharmony_ci    }
5063d0407baSopenharmony_ci    rockchip_adjust_power_scale(dev, cluster->scale);
5073d0407baSopenharmony_ci
5083d0407baSopenharmony_ci    return 0;
5093d0407baSopenharmony_ci}
5103d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_cpufreq_adjust_power_scale);
5113d0407baSopenharmony_ci
5123d0407baSopenharmony_ciint rockchip_cpufreq_opp_set_rate(struct device *dev, unsigned long target_freq)
5133d0407baSopenharmony_ci{
5143d0407baSopenharmony_ci    struct cluster_info *cluster;
5153d0407baSopenharmony_ci    int ret = 0;
5163d0407baSopenharmony_ci
5173d0407baSopenharmony_ci    cluster = rockchip_cluster_info_lookup(dev->id);
5183d0407baSopenharmony_ci    if (!cluster) {
5193d0407baSopenharmony_ci        return -EINVAL;
5203d0407baSopenharmony_ci    }
5213d0407baSopenharmony_ci
5223d0407baSopenharmony_ci    rockchip_monitor_volt_adjust_lock(cluster->mdev_info);
5233d0407baSopenharmony_ci    ret = dev_pm_opp_set_rate(dev, target_freq);
5243d0407baSopenharmony_ci    rockchip_monitor_volt_adjust_unlock(cluster->mdev_info);
5253d0407baSopenharmony_ci
5263d0407baSopenharmony_ci    return ret;
5273d0407baSopenharmony_ci}
5283d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_cpufreq_opp_set_rate);
5293d0407baSopenharmony_ci
5303d0407baSopenharmony_cistatic int rockchip_cpufreq_suspend(struct cpufreq_policy *policy)
5313d0407baSopenharmony_ci{
5323d0407baSopenharmony_ci    int ret = 0;
5333d0407baSopenharmony_ci
5343d0407baSopenharmony_ci    ret = cpufreq_generic_suspend(policy);
5353d0407baSopenharmony_ci    if (!ret) {
5363d0407baSopenharmony_ci        rockchip_monitor_suspend_low_temp_adjust(policy->cpu);
5373d0407baSopenharmony_ci    }
5383d0407baSopenharmony_ci
5393d0407baSopenharmony_ci    return ret;
5403d0407baSopenharmony_ci}
5413d0407baSopenharmony_ci
5423d0407baSopenharmony_cistatic int rockchip_cpufreq_notifier(struct notifier_block *nb, unsigned long event, void *data)
5433d0407baSopenharmony_ci{
5443d0407baSopenharmony_ci    struct device *dev;
5453d0407baSopenharmony_ci    struct cpufreq_policy *policy = data;
5463d0407baSopenharmony_ci    struct cluster_info *cluster;
5473d0407baSopenharmony_ci    struct monitor_dev_profile *mdevp = NULL;
5483d0407baSopenharmony_ci    struct monitor_dev_info *mdev_info = NULL;
5493d0407baSopenharmony_ci
5503d0407baSopenharmony_ci    dev = get_cpu_device(policy->cpu);
5513d0407baSopenharmony_ci    if (!dev) {
5523d0407baSopenharmony_ci        return NOTIFY_BAD;
5533d0407baSopenharmony_ci    }
5543d0407baSopenharmony_ci
5553d0407baSopenharmony_ci    cluster = rockchip_cluster_info_lookup(policy->cpu);
5563d0407baSopenharmony_ci    if (!cluster) {
5573d0407baSopenharmony_ci        return NOTIFY_BAD;
5583d0407baSopenharmony_ci    }
5593d0407baSopenharmony_ci
5603d0407baSopenharmony_ci    if (event == CPUFREQ_CREATE_POLICY) {
5613d0407baSopenharmony_ci        mdevp = kzalloc(sizeof(*mdevp), GFP_KERNEL);
5623d0407baSopenharmony_ci        if (!mdevp) {
5633d0407baSopenharmony_ci            return NOTIFY_BAD;
5643d0407baSopenharmony_ci        }
5653d0407baSopenharmony_ci        mdevp->type = MONITOR_TPYE_CPU;
5663d0407baSopenharmony_ci        mdevp->low_temp_adjust = rockchip_monitor_cpu_low_temp_adjust;
5673d0407baSopenharmony_ci        mdevp->high_temp_adjust = rockchip_monitor_cpu_high_temp_adjust;
5683d0407baSopenharmony_ci        mdevp->update_volt = rockchip_monitor_check_rate_volt;
5693d0407baSopenharmony_ci        mdevp->data = (void *)policy;
5703d0407baSopenharmony_ci        mdevp->opp_info = &cluster->opp_info;
5713d0407baSopenharmony_ci        cpumask_copy(&mdevp->allowed_cpus, policy->cpus);
5723d0407baSopenharmony_ci        mdev_info = rockchip_system_monitor_register(dev, mdevp);
5733d0407baSopenharmony_ci        if (IS_ERR(mdev_info)) {
5743d0407baSopenharmony_ci            kfree(mdevp);
5753d0407baSopenharmony_ci            dev_err(dev, "failed to register system monitor\n");
5763d0407baSopenharmony_ci            return NOTIFY_BAD;
5773d0407baSopenharmony_ci        }
5783d0407baSopenharmony_ci        mdev_info->devp = mdevp;
5793d0407baSopenharmony_ci        cluster->mdev_info = mdev_info;
5803d0407baSopenharmony_ci    } else if (event == CPUFREQ_REMOVE_POLICY) {
5813d0407baSopenharmony_ci        if (cluster->mdev_info) {
5823d0407baSopenharmony_ci            kfree(cluster->mdev_info->devp);
5833d0407baSopenharmony_ci            rockchip_system_monitor_unregister(cluster->mdev_info);
5843d0407baSopenharmony_ci            cluster->mdev_info = NULL;
5853d0407baSopenharmony_ci        }
5863d0407baSopenharmony_ci    }
5873d0407baSopenharmony_ci
5883d0407baSopenharmony_ci    return NOTIFY_OK;
5893d0407baSopenharmony_ci}
5903d0407baSopenharmony_ci
5913d0407baSopenharmony_cistatic struct notifier_block rockchip_cpufreq_notifier_block = {
5923d0407baSopenharmony_ci    .notifier_call = rockchip_cpufreq_notifier,
5933d0407baSopenharmony_ci};
5943d0407baSopenharmony_ci
5953d0407baSopenharmony_cistatic int __init rockchip_cpufreq_driver_init(void)
5963d0407baSopenharmony_ci{
5973d0407baSopenharmony_ci    struct cluster_info *cluster, *pos;
5983d0407baSopenharmony_ci    struct cpufreq_dt_platform_data pdata = {0};
5993d0407baSopenharmony_ci    int cpu, ret;
6003d0407baSopenharmony_ci
6013d0407baSopenharmony_ci    for_each_possible_cpu(cpu)
6023d0407baSopenharmony_ci    {
6033d0407baSopenharmony_ci        cluster = rockchip_cluster_info_lookup(cpu);
6043d0407baSopenharmony_ci        if (cluster) {
6053d0407baSopenharmony_ci            continue;
6063d0407baSopenharmony_ci        }
6073d0407baSopenharmony_ci
6083d0407baSopenharmony_ci        cluster = kzalloc(sizeof(*cluster), GFP_KERNEL);
6093d0407baSopenharmony_ci        if (!cluster) {
6103d0407baSopenharmony_ci            ret = -ENOMEM;
6113d0407baSopenharmony_ci            goto release_cluster_info;
6123d0407baSopenharmony_ci        }
6133d0407baSopenharmony_ci
6143d0407baSopenharmony_ci        ret = rockchip_cpufreq_cluster_init(cpu, cluster);
6153d0407baSopenharmony_ci        if (ret) {
6163d0407baSopenharmony_ci            pr_err("Failed to initialize dvfs info cpu%d\n", cpu);
6173d0407baSopenharmony_ci            goto release_cluster_info;
6183d0407baSopenharmony_ci        }
6193d0407baSopenharmony_ci        list_add(&cluster->list_head, &cluster_info_list);
6203d0407baSopenharmony_ci    }
6213d0407baSopenharmony_ci
6223d0407baSopenharmony_ci    pdata.have_governor_per_policy = true;
6233d0407baSopenharmony_ci    pdata.suspend = rockchip_cpufreq_suspend;
6243d0407baSopenharmony_ci
6253d0407baSopenharmony_ci    ret = cpufreq_register_notifier(&rockchip_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER);
6263d0407baSopenharmony_ci    if (ret) {
6273d0407baSopenharmony_ci        pr_err("failed to register cpufreq notifier\n");
6283d0407baSopenharmony_ci        goto release_cluster_info;
6293d0407baSopenharmony_ci    }
6303d0407baSopenharmony_ci
6313d0407baSopenharmony_ci    return PTR_ERR_OR_ZERO(
6323d0407baSopenharmony_ci        platform_device_register_data(NULL, "cpufreq-dt", -1, (void *)&pdata, sizeof(struct cpufreq_dt_platform_data)));
6333d0407baSopenharmony_ci
6343d0407baSopenharmony_cirelease_cluster_info:
6353d0407baSopenharmony_ci    list_for_each_entry_safe(cluster, pos, &cluster_info_list, list_head)
6363d0407baSopenharmony_ci    {
6373d0407baSopenharmony_ci        list_del(&cluster->list_head);
6383d0407baSopenharmony_ci        kfree(cluster);
6393d0407baSopenharmony_ci    }
6403d0407baSopenharmony_ci    return ret;
6413d0407baSopenharmony_ci}
6423d0407baSopenharmony_cimodule_init(rockchip_cpufreq_driver_init);
6433d0407baSopenharmony_ci
6443d0407baSopenharmony_ciMODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
6453d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip cpufreq driver");
6463d0407baSopenharmony_ciMODULE_LICENSE("GPL v2");
647