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