CPUFREQ: Setting different cpufreq tables according to efuse information. [1/1]

PD#SWPL-4035

Problem:
Setting different cpufreq tables according to efuse information.

Solution:
Setting different cpufreq tables according to efuse information.

Verify:
g12a_u200, verify pass

Change-Id: I1bf571f332244f5727ef3cd8743f215f71248146
Signed-off-by: Hong Guo <hong.guo@amlogic.com>
This commit is contained in:
Hong Guo
2019-01-14 15:53:22 +08:00
committed by Jianxin Pan
parent 69ff335bad
commit d9df4b6ddc
7 changed files with 578 additions and 50 deletions

View File

@@ -14409,6 +14409,7 @@ F: sound/soc/codecs/amlogic/ad82584f.h
AMLOGIC CPUFREQS DRIVER
M: hong guo <hong.guo@amlogic.com>
F: drivers/amlogic/cpufreq/meson-cpufreq.h
F: drivers/amlogic/cpufreq/meson-cpufreq.c
F: drivers/amlogic/clk/clk-cpu-fclk-composite.c

View File

@@ -793,6 +793,122 @@
};
};
cpu_opp_table1: cpu_opp_table1 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <731000>;
};
opp01 {
opp-hz = /bits/ 64 <250000000>;
opp-microvolt = <731000>;
};
opp02 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <731000>;
};
opp03 {
opp-hz = /bits/ 64 <667000000>;
opp-microvolt = <731000>;
};
opp04 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <731000>;
};
opp05 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <731000>;
};
opp06 {
opp-hz = /bits/ 64 <1398000000>;
opp-microvolt = <761000>;
};
opp07 {
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <791000>;
};
opp08 {
opp-hz = /bits/ 64 <1608000000>;
opp-microvolt = <831000>;
};
opp09 {
opp-hz = /bits/ 64 <1704000000>;
opp-microvolt = <861000>;
};
opp10 {
opp-hz = /bits/ 64 <1800000000>;
opp-microvolt = <981000>;
};
opp11 {
opp-hz = /bits/ 64 <1908000000>;
opp-microvolt = <991000>;
};
};
cpu_opp_table2: cpu_opp_table2 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <731000>;
};
opp01 {
opp-hz = /bits/ 64 <250000000>;
opp-microvolt = <731000>;
};
opp02 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <731000>;
};
opp03 {
opp-hz = /bits/ 64 <667000000>;
opp-microvolt = <731000>;
};
opp04 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <731000>;
};
opp05 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <731000>;
};
opp06 {
opp-hz = /bits/ 64 <1398000000>;
opp-microvolt = <761000>;
};
opp07 {
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <791000>;
};
opp08 {
opp-hz = /bits/ 64 <1608000000>;
opp-microvolt = <831000>;
};
opp09 {
opp-hz = /bits/ 64 <1704000000>;
opp-microvolt = <861000>;
};
opp10 {
opp-hz = /bits/ 64 <1800000000>;
opp-microvolt = <981000>;
};
opp11 {
opp-hz = /bits/ 64 <1908000000>;
opp-microvolt = <991000>;
};
opp12 {
opp-hz = /bits/ 64 <2016000000>;
opp-microvolt = <1011000>;
};
opp13 {
opp-hz = /bits/ 64 <2100000000>;
opp-microvolt = <1011000>;
};
};
cpufreq-meson {
compatible = "amlogic, cpufreq-meson";
pinctrl-names = "default";
@@ -802,6 +918,50 @@
}; /* end of / */
&CPU0 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&CPU1 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&CPU2 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&CPU3 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&meson_fb {
status = "okay";
display_size_default = <1920 1080 1920 2160 32>;

View File

@@ -791,6 +791,122 @@
};
};
cpu_opp_table1: cpu_opp_table1 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <731000>;
};
opp01 {
opp-hz = /bits/ 64 <250000000>;
opp-microvolt = <731000>;
};
opp02 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <731000>;
};
opp03 {
opp-hz = /bits/ 64 <667000000>;
opp-microvolt = <731000>;
};
opp04 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <731000>;
};
opp05 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <731000>;
};
opp06 {
opp-hz = /bits/ 64 <1398000000>;
opp-microvolt = <761000>;
};
opp07 {
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <791000>;
};
opp08 {
opp-hz = /bits/ 64 <1608000000>;
opp-microvolt = <831000>;
};
opp09 {
opp-hz = /bits/ 64 <1704000000>;
opp-microvolt = <861000>;
};
opp10 {
opp-hz = /bits/ 64 <1800000000>;
opp-microvolt = <981000>;
};
opp11 {
opp-hz = /bits/ 64 <1908000000>;
opp-microvolt = <991000>;
};
};
cpu_opp_table2: cpu_opp_table2 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <731000>;
};
opp01 {
opp-hz = /bits/ 64 <250000000>;
opp-microvolt = <731000>;
};
opp02 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <731000>;
};
opp03 {
opp-hz = /bits/ 64 <667000000>;
opp-microvolt = <731000>;
};
opp04 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <731000>;
};
opp05 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <731000>;
};
opp06 {
opp-hz = /bits/ 64 <1398000000>;
opp-microvolt = <761000>;
};
opp07 {
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <791000>;
};
opp08 {
opp-hz = /bits/ 64 <1608000000>;
opp-microvolt = <831000>;
};
opp09 {
opp-hz = /bits/ 64 <1704000000>;
opp-microvolt = <861000>;
};
opp10 {
opp-hz = /bits/ 64 <1800000000>;
opp-microvolt = <981000>;
};
opp11 {
opp-hz = /bits/ 64 <1908000000>;
opp-microvolt = <991000>;
};
opp12 {
opp-hz = /bits/ 64 <2016000000>;
opp-microvolt = <1011000>;
};
opp13 {
opp-hz = /bits/ 64 <2100000000>;
opp-microvolt = <1011000>;
};
};
cpufreq-meson {
compatible = "amlogic, cpufreq-meson";
pinctrl-names = "default";
@@ -800,6 +916,50 @@
}; /* end of / */
&CPU0 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>;
//<&cpu_opp_table1>,
//<&cpu_opp_table2>;
};
&CPU1 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&CPU2 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&CPU3 {
/*set differents table cpufreq max*/
diff_tables_supply;
hispeed_cpufreq_max = <2100>;
medspeed_cpufreq_max = <1908>;
lospeed_cpufreq_max = <1800>;
operating-points-v2 = <&cpu_opp_table0>,
<&cpu_opp_table1>,
<&cpu_opp_table2>;
};
&meson_fb {
status = "okay";
display_size_default = <1920 1080 1920 2160 32>;

View File

@@ -30,51 +30,18 @@
#include <linux/clk.h>
#include <linux/cpumask.h>
#include <linux/clk-provider.h>
#include <linux/cpu_cooling.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/topology.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/driver.h>
#include "../../regulator/internal.h"
#include <linux/amlogic/scpi_protocol.h>
#include "../../base/power/opp/opp.h"
/* Currently we support only two clusters */
#define MAX_CLUSTERS 2
/*core power supply*/
#define CORE_SUPPLY "cpu"
/* Core Clocks */
#define CORE_CLK "core_clk"
#define LOW_FREQ_CLK_PARENT "low_freq_clk_parent"
#define HIGH_FREQ_CLK_PARENT "high_freq_clk_parent"
static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
/* Default voltage_tolerance */
#define DEF_VOLT_TOL 0
/*mid rate for set parent,Khz*/
static unsigned int mid_rate = (1000*1000);
static unsigned int gap_rate = (10*1000*1000);
struct meson_cpufreq_driver_data {
struct device *cpu_dev;
struct regulator *reg;
/* voltage tolerance in percentage */
unsigned int volt_tol;
struct clk *high_freq_clk_p;
struct clk *low_freq_clk_p;
};
static DEFINE_PER_CPU(unsigned int, physical_cluster);
static struct mutex cluster_lock[MAX_CLUSTERS];
#include "meson-cpufreq.h"
static unsigned int meson_cpufreq_get_rate(unsigned int cpu)
{
@@ -337,6 +304,70 @@ static inline u32 get_table_max(struct cpufreq_frequency_table *table)
return max_freq;
}
int get_cpufreq_tables_efuse(u32 cur_cluster)
{
int ret, efuse_info;
u32 freq, vol;
efuse_info = scpi_get_cpuinfo(cur_cluster, &freq, &vol);
if (efuse_info)
pr_err("%s,get invalid efuse_info = %d by mailbox!\n",
__func__, efuse_info);
pr_info("%s:efuse info for cpufreq = %u\n", __func__, freq);
BUG_ON(freq && freq < EFUSE_CPUFREQ_MIN);
freq = DIV_ROUND_UP(freq, CLK_DIV) * CLK_DIV;
pr_info("%s:efuse adjust cpufreq = %u\n", __func__, freq);
if (freq >= hispeed_cpufreq_max)
ret = HISPEED_INDEX;
else if (freq >= medspeed_cpufreq_max && freq < hispeed_cpufreq_max)
ret = MEDSPEED_INDEX;
else
ret = LOSPEED_INDEX;
return ret;
}
int choose_cpufreq_tables_index(const struct device_node *np, u32 cur_cluster)
{
int ret = 0;
cpufreq_tables_supply = of_property_read_bool(np, "diff_tables_supply");
if (cpufreq_tables_supply) {
/*choose appropriate cpufreq tables according efuse info*/
if (of_property_read_u32(np, "hispeed_cpufreq_max",
&hispeed_cpufreq_max)) {
pr_err("%s:don't find the node <dynamic_cpufreq_max>\n",
__func__);
hispeed_cpufreq_max = 0;
return ret;
}
if (of_property_read_u32(np, "medspeed_cpufreq_max",
&medspeed_cpufreq_max)) {
pr_err("%s:don't find the node <medspeed_cpufreq_max>\n",
__func__);
medspeed_cpufreq_max = 0;
return ret;
}
if (of_property_read_u32(np, "lospeed_cpufreq_max",
&lospeed_cpufreq_max)) {
pr_err("%s:don't find the node <lospeed_cpufreq_max>\n",
__func__);
lospeed_cpufreq_max = 0;
return ret;
}
ret = get_cpufreq_tables_efuse(cur_cluster);
pr_info("%s:hispeed_max %u,medspeed_max %u,lospeed_max %u,tables_index %u\n",
__func__, hispeed_cpufreq_max,
medspeed_cpufreq_max, lospeed_cpufreq_max, ret);
}
return ret;
}
/* CPU initialization */
static int meson_cpufreq_init(struct cpufreq_policy *policy)
{
@@ -349,8 +380,7 @@ static int meson_cpufreq_init(struct cpufreq_policy *policy)
unsigned int transition_latency = CPUFREQ_ETERNAL;
unsigned int volt_tol = 0;
unsigned long freq_hz = 0;
int cpu = 0;
int ret = 0;
int cpu = 0, ret = 0, tables_index;
if (!policy) {
pr_err("invalid cpufreq_policy\n");
@@ -415,15 +445,12 @@ static int meson_cpufreq_init(struct cpufreq_policy *policy)
volt_tol = DEF_VOLT_TOL;
pr_info("value of voltage_tolerance %u\n", volt_tol);
if (cur_cluster < MAX_CLUSTERS) {
int cpu;
if (cur_cluster < MAX_CLUSTERS)
cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
for_each_cpu(cpu, policy->cpus)
per_cpu(physical_cluster, cpu) = cur_cluster;
}
ret = dev_pm_opp_of_cpumask_add_table(policy->cpus);
tables_index = choose_cpufreq_tables_index(np, cur_cluster);
ret = dev_pm_opp_of_cpumask_add_table_indexed(policy->cpus,
tables_index);
if (ret) {
pr_err("%s: init_opp_table failed, cpu: %d, cluster: %d, err: %d\n",
__func__, cpu_dev->id, cur_cluster, ret);
@@ -503,11 +530,6 @@ static int meson_cpufreq_exit(struct cpufreq_policy *policy)
if (cpufreq_data == NULL)
return 0;
if (cur_cluster < MAX_CLUSTERS) {
cpufreq_cooling_unregister(cdev[cur_cluster]);
cdev[cur_cluster] = NULL;
}
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,

View File

@@ -0,0 +1,70 @@
/*
* drivers/amlogic/cpufreq/meson-cpufreq.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __MESON_CPUFREQ_H
#define __MESON_CPUFREQ_H
/* Currently we support only two clusters */
#define MAX_CLUSTERS 2
/*core power supply*/
#define CORE_SUPPLY "cpu"
/* Core Clocks */
#define CORE_CLK "core_clk"
#define LOW_FREQ_CLK_PARENT "low_freq_clk_parent"
#define HIGH_FREQ_CLK_PARENT "high_freq_clk_parent"
static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
/* Default voltage_tolerance */
#define DEF_VOLT_TOL 0
#define CLK_DIV 12
#define EFUSE_CPUFREQ_MIN 1500
/*mid rate for set parent,Khz*/
static unsigned int mid_rate = (1000 * 1000);
static unsigned int gap_rate = (10 * 1000 * 1000);
/*whether use different tables or not*/
bool cpufreq_tables_supply;
static unsigned int hispeed_cpufreq_max;
static unsigned int medspeed_cpufreq_max;
static unsigned int lospeed_cpufreq_max;
enum cpufreq_index {
LOSPEED_INDEX,
MEDSPEED_INDEX,
HISPEED_INDEX
};
struct meson_cpufreq_driver_data {
struct device *cpu_dev;
struct regulator *reg;
/* voltage tolerance in percentage */
unsigned int volt_tol;
struct clk *high_freq_clk_p;
struct clk *low_freq_clk_p;
};
static struct mutex cluster_lock[MAX_CLUSTERS];
static unsigned int meson_cpufreq_get_rate(unsigned int cpu);
static unsigned int meson_cpufreq_set_rate(struct cpufreq_policy *policy,
u32 cur_cluster, u32 rate);
static int meson_regulator_set_volate(struct regulator *regulator, int old_uv,
int new_uv, int tol_uv);
int get_cpufreq_tables_efuse(u32 cur_cluster);
int choose_cpufreq_tables_index(const struct device_node *np, u32 cur_cluster);
#endif /* __MESON_CPUFREQ_H */

View File

@@ -471,6 +471,59 @@ int dev_pm_opp_of_add_table(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
#ifdef CONFIG_AMLOGIC_MODIFY
/**
* dev_pm_opp_of_add_table_indexed() - Initialize indexed opp
*table from device tree
* @dev: device pointer used to lookup OPP table.
* @index: Index number.
*
* Register the initial OPP table with the OPP library for given device only
* using the "operating-points-v2" property.
*
* Return:
* 0 On success OR
* Duplicate OPPs (both freq and volt are same) and opp->available
* -EEXIST Freq are same and volt are different OR
* Duplicate OPPs (both freq and volt are same) and !opp->available
* -ENOMEM Memory allocation failure
* -ENODEV when 'operating-points' property is not found or is invalid data
* in device node.
* -ENODATA when empty 'operating-points' property is found
* -EINVAL when invalid entries are found in opp-v2 table
*/
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
struct device_node *opp_np;
int ret, count;
again:
opp_np = of_parse_phandle(dev->of_node, "operating-points-v2", index);
if (!opp_np) {
/*
* If only one phandle is present, then the same OPP table
* applies for all index requests.
*/
count = of_count_phandle_with_args(dev->of_node,
"operating-points-v2", NULL);
if (count <= index) {
pr_err("%s,count %d must smaller than index %d!\n",
__func__, count, index);
index = 0;
goto again;
}
return -ENODEV;
}
ret = _of_add_opp_table_v2(dev, opp_np);
of_node_put(opp_np);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
#endif
/* CPU device specific helpers */
/**
@@ -534,6 +587,51 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
#ifdef CONFIG_AMLOGIC_MODIFY
/**
* dev_pm_opp_of_cpumask_add_table_index() - Adds OPP table for @cpumask
* @cpumask: cpumask for which OPP table needs to be added.
*
* This adds the OPP tables for CPUs present in the @cpumask.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
int dev_pm_opp_of_cpumask_add_table_indexed(const struct cpumask *cpumask,
int index)
{
struct device *cpu_dev;
int cpu, ret = 0;
WARN_ON(cpumask_empty(cpumask));
for_each_cpu(cpu, cpumask) {
cpu_dev = get_cpu_device(cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,
cpu);
continue;
}
ret = dev_pm_opp_of_add_table_indexed(cpu_dev, index);
if (ret) {
pr_err("%s: couldn't find opp table for cpu:%d, %d\n",
__func__, cpu, ret);
/* Free all other OPPs */
dev_pm_opp_of_cpumask_remove_table(cpumask);
break;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table_indexed);
#endif
/*
* Works only for OPP v2 bindings.
*

View File

@@ -207,6 +207,11 @@ static inline void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask
int dev_pm_opp_of_add_table(struct device *dev);
void dev_pm_opp_of_remove_table(struct device *dev);
int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask);
#ifdef CONFIG_AMLOGIC_MODIFY
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index);
int dev_pm_opp_of_cpumask_add_table_indexed(const struct cpumask *cpumask,
int index);
#endif
void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask);
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
#else
@@ -224,6 +229,18 @@ static inline int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
return -ENOTSUPP;
}
#ifdef CONFIG_AMLOGIC_MODIFY
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
return -ENOTSUPP;
}
int dev_pm_opp_of_cpumask_add_table_indexed(const struct cpumask *cpumask,
int index)
{
return -ENOTSUPP;
}
#endif
static inline void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
{
}