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