cpufreq: Add runtime initialised driver for rockchip platforms

The driver reads OTP value from SoC to provide the OPP framework
with required information. This is used to determine the voltage and
frequency value for each OPP of operating-points-v2 table when it is
parsed by the OPP framework.

Change-Id: Iec5a4ff05a4829fdbc3535f94e92759d4238623d
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
This commit is contained in:
Finley Xiao 2021-06-23 18:51:29 +08:00 committed by Tao Huang
parent c83090bafb
commit a724f211e7
5 changed files with 58 additions and 252 deletions

View File

@ -158,6 +158,16 @@ config ARM_RASPBERRYPI_CPUFREQ
If in doubt, say N.
config ARM_ROCKCHIP_CPUFREQ
tristate "Rockchip CPUfreq driver"
depends on ARCH_ROCKCHIP && CPUFREQ_DT
select PM_OPP
help
This adds the CPUFreq driver support for Rockchip SoCs,
based on cpufreq-dt.
If in doubt, say N.
config ARM_S3C_CPUFREQ
bool
help

View File

@ -69,6 +69,7 @@ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o
obj-$(CONFIG_ARM_ROCKCHIP_CPUFREQ) += rockchip-cpufreq.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o

View File

@ -66,21 +66,6 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "renesas,r8a7794", },
{ .compatible = "renesas,sh73a0", },
{ .compatible = "rockchip,rk2928", },
{ .compatible = "rockchip,rk3036", },
{ .compatible = "rockchip,rk3066a", },
{ .compatible = "rockchip,rk3066b", },
{ .compatible = "rockchip,rk3188", },
{ .compatible = "rockchip,rk3228", },
{ .compatible = "rockchip,rk3288", },
{ .compatible = "rockchip,rk3328", },
{ .compatible = "rockchip,rk3366", },
{ .compatible = "rockchip,rk3368", },
{ .compatible = "rockchip,rk3399",
.data = &(struct cpufreq_dt_platform_data)
{ .have_governor_per_policy = true, },
},
{ .compatible = "st-ericsson,u8500", },
{ .compatible = "st-ericsson,u8540", },
{ .compatible = "st-ericsson,u9500", },
@ -137,6 +122,29 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "qcom,sc7180", },
{ .compatible = "qcom,sdm845", },
{ .compatible = "rockchip,px30", },
{ .compatible = "rockchip,rk2928", },
{ .compatible = "rockchip,rk3036", },
{ .compatible = "rockchip,rk3066a", },
{ .compatible = "rockchip,rk3066b", },
{ .compatible = "rockchip,rk3126", },
{ .compatible = "rockchip,rk3128", },
{ .compatible = "rockchip,rk3188", },
{ .compatible = "rockchip,rk3228", },
{ .compatible = "rockchip,rk3229", },
{ .compatible = "rockchip,rk3288", },
{ .compatible = "rockchip,rk3288w", },
{ .compatible = "rockchip,rk3326", },
{ .compatible = "rockchip,rk3328", },
{ .compatible = "rockchip,rk3366", },
{ .compatible = "rockchip,rk3368", },
{ .compatible = "rockchip,rk3399", },
{ .compatible = "rockchip,rk3399pro", },
{ .compatible = "rockchip,rk3566", },
{ .compatible = "rockchip,rk3568", },
{ .compatible = "rockchip,rv1109", },
{ .compatible = "rockchip,rv1126", },
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },
{ .compatible = "st,stih418", },

View File

@ -24,31 +24,19 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/soc/rockchip/pvtm.h>
#include <linux/thermal.h>
#include <linux/rockchip/cpu.h>
#include <soc/rockchip/rockchip_opp_select.h>
#include <soc/rockchip/rockchip_system_monitor.h>
#include "cpufreq-dt.h"
#include "rockchip-cpufreq.h"
#include "../clk/rockchip/clk.h"
#define LEAKAGE_INVALID 0xff
struct cluster_info {
struct opp_table *opp_table;
struct list_head list_head;
cpumask_t cpus;
unsigned int reboot_freq;
unsigned int threshold_freq;
unsigned int scale_rate;
unsigned int temp_limit_rate;
int volt_sel;
int scale;
int process;
bool offline;
bool freq_limit;
bool is_check_init;
@ -255,51 +243,27 @@ static struct cluster_info *rockchip_cluster_info_lookup(int cpu)
return NULL;
}
static struct cluster_info *rockchip_cluster_lookup_by_dev(struct device *dev)
{
struct cluster_info *cluster;
struct device *cpu_dev;
int cpu;
list_for_each_entry(cluster, &cluster_info_list, list_head) {
for_each_cpu(cpu, &cluster->cpus) {
cpu_dev = get_cpu_device(cpu);
if (!cpu_dev)
continue;
if (cpu_dev == dev)
return cluster;
}
}
return NULL;
}
static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
{
struct opp_table *opp_table;
struct device_node *np;
struct property *pp;
struct device *dev;
char *reg_name = NULL;
int ret = 0, bin = -EINVAL;
cluster->process = -EINVAL;
cluster->volt_sel = -EINVAL;
cluster->scale = 0;
int bin = -EINVAL;
int process = -EINVAL;
int volt_sel = -EINVAL;
int ret = 0;
dev = get_cpu_device(cpu);
if (!dev)
return -ENODEV;
pp = of_find_property(dev->of_node, "cpu-supply", NULL);
if (pp) {
if (of_find_property(dev->of_node, "cpu-supply", NULL))
reg_name = "cpu";
} else {
pp = of_find_property(dev->of_node, "cpu0-supply", NULL);
if (pp)
reg_name = "cpu0";
else
return -ENOENT;
}
else if (of_find_property(dev->of_node, "cpu0-supply", NULL))
reg_name = "cpu0";
else
return -ENOENT;
np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
if (!np) {
@ -313,72 +277,23 @@ static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
goto np_err;
}
of_property_read_u32(np, "rockchip,threshold-freq",
&cluster->threshold_freq);
cluster->freq_limit = of_property_read_bool(np, "rockchip,freq-limit");
rockchip_get_soc_info(dev, rockchip_cpufreq_of_match,
&bin, &cluster->process);
rockchip_get_scale_volt_sel(dev, "cpu_leakage", reg_name,
bin, cluster->process,
&cluster->scale, &cluster->volt_sel);
rockchip_get_soc_info(dev, rockchip_cpufreq_of_match, &bin, &process);
rockchip_get_scale_volt_sel(dev, "cpu_leakage", reg_name, bin, process,
&cluster->scale, &volt_sel);
opp_table = rockchip_set_opp_prop_name(dev, process, volt_sel);
if (IS_ERR(opp_table))
ret = PTR_ERR(opp_table);
np_err:
of_node_put(np);
return ret;
}
int rockchip_cpufreq_check_rate_volt(struct device *dev)
{
struct cluster_info *cluster;
cluster = rockchip_cluster_lookup_by_dev(dev);
if (!cluster)
return -EINVAL;
if (cluster->is_check_init)
return 0;
dev_pm_opp_check_rate_volt(dev, true);
cluster->is_check_init = true;
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_check_rate_volt);
int rockchip_cpufreq_set_opp_info(struct device *dev)
{
struct cluster_info *cluster;
cluster = rockchip_cluster_lookup_by_dev(dev);
if (!cluster)
return -EINVAL;
cluster->opp_table = rockchip_set_opp_prop_name(dev,
cluster->process,
cluster->volt_sel);
if (IS_ERR(cluster->opp_table)) {
dev_err(dev, "Failed to set prop name\n");
return PTR_ERR(cluster->opp_table);
}
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_set_opp_info);
void rockchip_cpufreq_put_opp_info(struct device *dev)
{
struct cluster_info *cluster;
cluster = rockchip_cluster_lookup_by_dev(dev);
if (!cluster)
return;
if (!IS_ERR_OR_NULL(cluster->opp_table))
dev_pm_opp_put_prop_name(cluster->opp_table);
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_put_opp_info);
int rockchip_cpufreq_adjust_power_scale(struct device *dev)
{
struct cluster_info *cluster;
cluster = rockchip_cluster_lookup_by_dev(dev);
cluster = rockchip_cluster_info_lookup(dev->id);
if (!cluster)
return -EINVAL;
rockchip_adjust_power_scale(dev, cluster->scale);
@ -387,101 +302,22 @@ int rockchip_cpufreq_adjust_power_scale(struct device *dev)
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_adjust_power_scale);
int rockchip_cpufreq_suspend(struct cpufreq_policy *policy)
static int rockchip_cpufreq_suspend(struct cpufreq_policy *policy)
{
int ret = 0;
ret = cpufreq_generic_suspend(policy);
if (!ret)
rockchip_monitor_suspend_low_temp_adjust(policy->cpu);
return ret;
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_suspend);
static struct cpufreq_policy *rockchip_get_policy(struct cluster_info *cluster)
{
int first_cpu;
first_cpu = cpumask_first_and(&cluster->cpus, cpu_online_mask);
if (first_cpu >= nr_cpu_ids)
return NULL;
return cpufreq_cpu_get(first_cpu);
}
/**
* rockchip_cpufreq_adjust_target() - Adjust cpu target frequency
* @cpu: CPU number
* @freq: Expected target frequency
*
* This adjusts cpu target frequency for reducing power consumption.
* Only one cluster can eanble frequency limit, and the cluster's
* maximum frequency will be limited to its threshold frequency, if the
* other cluster's frequency is geater than or equal to its threshold
* frequency.
*/
unsigned int rockchip_cpufreq_adjust_target(int cpu, unsigned int freq)
{
struct cpufreq_policy *policy;
struct cluster_info *cluster, *temp;
cluster = rockchip_cluster_info_lookup(cpu);
if (!cluster || !cluster->threshold_freq)
goto adjust_out;
if (cluster->freq_limit) {
if (freq <= cluster->threshold_freq)
goto adjust_out;
list_for_each_entry(temp, &cluster_info_list, list_head) {
if (temp->freq_limit || temp == cluster ||
temp->offline)
continue;
policy = rockchip_get_policy(temp);
if (!policy)
continue;
if (temp->threshold_freq &&
temp->threshold_freq <= policy->cur) {
cpufreq_cpu_put(policy);
return cluster->threshold_freq;
}
cpufreq_cpu_put(policy);
}
} else {
if (freq < cluster->threshold_freq)
goto adjust_out;
list_for_each_entry(temp, &cluster_info_list, list_head) {
if (!temp->freq_limit || temp == cluster ||
temp->offline)
continue;
policy = rockchip_get_policy(temp);
if (!policy)
continue;
if (temp->threshold_freq &&
temp->threshold_freq < policy->cur)
cpufreq_driver_target(policy,
temp->threshold_freq,
CPUFREQ_RELATION_H);
cpufreq_cpu_put(policy);
}
}
adjust_out:
return freq;
}
EXPORT_SYMBOL_GPL(rockchip_cpufreq_adjust_target);
static int __init rockchip_cpufreq_driver_init(void)
{
struct cluster_info *cluster, *pos;
struct cpufreq_dt_platform_data pdata = {0};
int cpu, ret, i = 0;
int cpu, ret;
for_each_possible_cpu(cpu) {
cluster = rockchip_cluster_info_lookup(cpu);
@ -496,33 +332,15 @@ static int __init rockchip_cpufreq_driver_init(void)
ret = rockchip_cpufreq_cluster_init(cpu, cluster);
if (ret) {
if (ret != -ENOENT) {
pr_err("Failed to initialize dvfs info cpu%d\n",
cpu);
goto release_cluster_info;
}
/*
* As the OPP document said, only one OPP binding
* should be used per device.
* And if there are multiple clusters on rockchip
* platforms, we should use operating-points-v2.
* So if don't support operating-points-v2, there must
* be only one cluster, the list shuold be null.
*/
list_for_each_entry(pos, &cluster_info_list, list_head)
i++;
if (i)
goto release_cluster_info;
list_add(&cluster->list_head, &cluster_info_list);
goto next;
pr_err("Failed to initialize dvfs info cpu%d\n", cpu);
goto release_cluster_info;
}
list_add(&cluster->list_head, &cluster_info_list);
}
next:
pdata.have_governor_per_policy = true;
pdata.suspend = rockchip_cpufreq_suspend;
return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "cpufreq-dt",
-1, (void *)&pdata,
sizeof(struct cpufreq_dt_platform_data)));

View File

@ -6,43 +6,12 @@
#define __ROCKCHIP_CPUFREQ_H
#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_CPUFREQ)
unsigned int rockchip_cpufreq_adjust_target(int cpu, unsigned int freq);
int rockchip_cpufreq_check_rate_volt(struct device *dev);
int rockchip_cpufreq_set_opp_info(struct device *dev);
void rockchip_cpufreq_put_opp_info(struct device *dev);
int rockchip_cpufreq_adjust_power_scale(struct device *dev);
int rockchip_cpufreq_suspend(struct cpufreq_policy *policy);
#else
static inline unsigned int rockchip_cpufreq_adjust_target(int cpu,
unsigned int freq)
{
return freq;
}
static inline int rockchip_cpufreq_check_rate_volt(struct device *dev)
{
return -ENOTSUPP;
}
static inline int rockchip_cpufreq_set_opp_info(struct device *dev)
{
return -ENOTSUPP;
}
static inline void rockchip_cpufreq_put_opp_info(struct device *dev)
{
}
static inline int rockchip_cpufreq_adjust_power_scale(struct device *dev)
{
return -ENOTSUPP;
}
static inline int rockchip_cpufreq_suspend(struct cpufreq_policy *policy)
{
return -ENOTSUPP;
}
#endif /* CONFIG_ARM_ROCKCHIP_CPUFREQ */
#endif