From 2bee4db6a5a55635203cadc6b2dd672da5d2924d Mon Sep 17 00:00:00 2001 From: cl Date: Wed, 5 Nov 2014 10:00:00 +0800 Subject: [PATCH] dvfs: add pvtm support Signed-off-by: cl --- arch/arm/boot/dts/rk3288-tb_8846.dts | 16 ++ arch/arm/boot/dts/rk3288.dtsi | 4 +- arch/arm/mach-rockchip/dvfs.c | 392 ++++++++++++++++++++++----- include/linux/rockchip/common.h | 1 + include/linux/rockchip/dvfs.h | 23 ++ 5 files changed, 364 insertions(+), 72 deletions(-) diff --git a/arch/arm/boot/dts/rk3288-tb_8846.dts b/arch/arm/boot/dts/rk3288-tb_8846.dts index 9ec042d07fa0..3c15425a45f8 100644 --- a/arch/arm/boot/dts/rk3288-tb_8846.dts +++ b/arch/arm/boot/dts/rk3288-tb_8846.dts @@ -728,6 +728,22 @@ // 1704000 1350000 // 1800000 1400000 >; + support-pvtm = <1>; + pvtm-operating-points = < + /* KHz uV margin(uV)*/ + 126000 900000 25000 + 216000 900000 25000 + 312000 900000 25000 + 408000 900000 25000 + 600000 900000 25000 + 696000 950000 25000 + 816000 1000000 25000 + 1008000 1050000 25000 + 1200000 1100000 25000 + 1416000 1200000 25000 + 1512000 1300000 25000 + 1608000 1350000 25000 + >; status="okay"; }; diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 2c3ed665c3d6..481063dc96bc 100755 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -908,9 +908,9 @@ 816000 1100000 1008000 1100000 >; + channel = <0>; temp-limit-enable = <1>; target-temp = <80>; - temp-channel = <1>; normal-temp-limit = < /*delta-temp delta-freq*/ 3 96000 @@ -944,6 +944,7 @@ 300000 1200000 400000 1200000 >; + channel = <2>; status = "disabled"; }; }; @@ -971,6 +972,7 @@ 300000 1200000 400000 1200000 >; + channel = <1>; status = "okay"; regu-mode-table = < /*freq mode*/ diff --git a/arch/arm/mach-rockchip/dvfs.c b/arch/arm/mach-rockchip/dvfs.c index ea62aa4d6cd9..fe12ce36f41c 100644 --- a/arch/arm/mach-rockchip/dvfs.c +++ b/arch/arm/mach-rockchip/dvfs.c @@ -27,6 +27,7 @@ #include #include #include "../../../drivers/clk/rockchip/clk-pd.h" +#include "efuse.h" extern int rockchip_tsadc_get_temp(int chn); @@ -858,6 +859,161 @@ static void dvfs_temp_limit_work_func(struct work_struct *work) } #endif +static struct cpufreq_frequency_table rk3288v0_arm_pvtm_table[] = { + {.frequency = 216000, .index = 4006}, + {.frequency = 408000, .index = 6518}, + {.frequency = 600000, .index = 8345}, + {.frequency = 816000, .index = 11026}, + {.frequency = 1008000, .index = 12906}, + {.frequency = 1200000, .index = 15532}, + {.frequency = 1416000, .index = 18076}, + {.frequency = 1608000, .index = 21282}, + {.frequency = CPUFREQ_TABLE_END, .index = 1}, +}; + +static struct pvtm_info rk3288v0_arm_pvtm_info = { + .compatible = "rockchip,rk3288", + .pvtm_table = rk3288v0_arm_pvtm_table, + .channel = ARM_DVFS_CH, + .process_version = RK3288_PROCESS_V0, + .scan_rate_hz = 216000000, + .sample_time_us = 1000, + .volt_step_uv = 12500, + .delta_pvtm_by_volt = 400, + .delta_pvtm_by_temp = 14, + .volt_margin_uv = 25000, + .min_volt_uv = 850000, + .max_volt_uv = 1400000, +}; + +static struct cpufreq_frequency_table rk3288v1_arm_pvtm_table[] = { + {.frequency = 216000, .index = 4710}, + {.frequency = 408000, .index = 7200}, + {.frequency = 600000, .index = 9192}, + {.frequency = 816000, .index = 12560}, + {.frequency = 1008000, .index = 14741}, + {.frequency = 1200000, .index = 16886}, + {.frequency = 1416000, .index = 20081}, + {.frequency = 1608000, .index = 24061}, + {.frequency = CPUFREQ_TABLE_END, .index = 1}, +}; + +static struct pvtm_info rk3288v1_arm_pvtm_info = { + .compatible = "rockchip,rk3288", + .pvtm_table = rk3288v1_arm_pvtm_table, + .channel = ARM_DVFS_CH, + .process_version = RK3288_PROCESS_V1, + .scan_rate_hz = 216000000, + .sample_time_us = 1000, + .volt_step_uv = 12500, + .delta_pvtm_by_volt = 450, + .delta_pvtm_by_temp = 7, + .volt_margin_uv = 25000, + .min_volt_uv = 850000, + .max_volt_uv = 1400000, +}; + +static struct pvtm_info *pvtm_info_table[] = { + &rk3288v0_arm_pvtm_info, + &rk3288v1_arm_pvtm_info, + NULL +}; + +static int pvtm_set_single_dvfs(struct dvfs_node *dvfs_node, u32 idx, + struct pvtm_info *info, int *pvtm_list, + u32 min_pvtm) +{ + struct cpufreq_frequency_table *dvfs_table = dvfs_node->dvfs_table; + struct cpufreq_frequency_table *pvtm_table = dvfs_node->pvtm_table; + int target_pvtm, pvtm_margin, volt_margin; + unsigned int n_voltages = dvfs_node->vd->n_voltages; + int *volt_list = dvfs_node->vd->volt_list; + int n, temp; + + volt_margin = info->volt_margin_uv + pvtm_table[idx].index; + n = volt_margin/info->volt_step_uv; + if (volt_margin%info->volt_step_uv) + n++; + + pvtm_margin = n*info->delta_pvtm_by_volt; + temp = rockchip_tsadc_get_temp(1); + target_pvtm = min_pvtm+temp * info->delta_pvtm_by_temp + pvtm_margin; + + DVFS_DBG("=====%s: temp:%d, freq:%d, target pvtm:%d=====\n", + __func__, temp, dvfs_table[idx].frequency, target_pvtm); + + for (n = 0; n < n_voltages; n++) { + if (pvtm_list[n] >= target_pvtm) { + dvfs_table[idx].index = volt_list[n]; + DVFS_DBG("freq[%d]=%d, volt=%d\n", + idx, dvfs_table[idx].frequency, volt_list[n]); + + return 0; + } + } + + return -EINVAL; + + return 0; +} + +static void pvtm_set_dvfs_table(struct dvfs_node *dvfs_node) +{ + struct cpufreq_frequency_table *dvfs_table = dvfs_node->dvfs_table; + struct pvtm_info *info = dvfs_node->pvtm_info; + struct regulator *regulator = dvfs_node->vd->regulator; + int i, j; + int ret = 0; + int pvtm_list[VD_VOL_LIST_CNT] = {0}; + unsigned int n_voltages = dvfs_node->vd->n_voltages; + int *volt_list = dvfs_node->vd->volt_list; + + if (!info) + return; + + clk_set_rate(dvfs_node->clk, info->scan_rate_hz); + DVFS_DBG("%s:%lu\n", __func__, clk_get_rate(dvfs_node->clk)); + + for (i = 0; i < n_voltages; i++) { + if ((volt_list[i] >= info->min_volt_uv) && + (volt_list[i] <= info->max_volt_uv)) { + regulator_set_voltage(regulator, volt_list[i], + volt_list[i]); + pvtm_list[i] = pvtm_get_value(info->channel, + info->sample_time_us); + } + } + + for (i = 0; dvfs_table[i].frequency != CPUFREQ_TABLE_END; i++) { + for (j = 0; info->pvtm_table[j].frequency != + CPUFREQ_TABLE_END; j++) + if (info->pvtm_table[j].frequency >= + dvfs_table[i].frequency) { + int min_pvtm = info->pvtm_table[j].index; + + ret = pvtm_set_single_dvfs(dvfs_node, + i, + info, + pvtm_list, + min_pvtm); + break; + } + + if (ret) { + DVFS_WARNING("freq: %d can not reach target pvtm\n", + dvfs_table[i].frequency); + break; + } + + if (info->pvtm_table[j].frequency == CPUFREQ_TABLE_END) { + DVFS_WARNING("not support freq :%d, max freq is %d\n", + dvfs_table[i].frequency, + info->pvtm_table[j-1].frequency); + break; + } + } +} + static void dvfs_virt_temp_limit_work_func(void) { const struct cpufreq_frequency_table *limits_table = NULL; @@ -995,7 +1151,6 @@ static void dvfs_temp_limit_work_func(struct work_struct *work) } static DECLARE_DELAYED_WORK(dvfs_temp_limit_work, dvfs_temp_limit_work_func); - int dvfs_clk_enable_limit(struct dvfs_node *clk_dvfs_node, unsigned int min_rate, unsigned int max_rate) { u32 rate = 0, ret = 0; @@ -1144,7 +1299,6 @@ int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node) unsigned int mode; int ret; - if (!clk_dvfs_node) return -EINVAL; @@ -1184,6 +1338,8 @@ int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node) dvfs_table_round_clk_rate(clk_dvfs_node); dvfs_get_rate_range(clk_dvfs_node); clk_dvfs_node->freq_limit_en = 1; + if (clk_dvfs_node->support_pvtm) + pvtm_set_dvfs_table(clk_dvfs_node); dvfs_table_round_volt(clk_dvfs_node); clk_dvfs_node->set_freq = clk_dvfs_node_get_rate_kz(clk_dvfs_node->clk); clk_dvfs_node->last_set_rate = clk_dvfs_node->set_freq*1000; @@ -1587,25 +1743,6 @@ int rk_regist_clk(struct dvfs_node *clk_dvfs_node) } EXPORT_SYMBOL_GPL(rk_regist_clk); -static int rk_convert_cpufreq_table(struct dvfs_node *dvfs_node) -{ - struct opp *opp; - struct device *dev; - struct cpufreq_frequency_table *table; - int i; - - table = dvfs_node->dvfs_table; - dev = &dvfs_node->dev; - - for (i = 0; table[i].frequency!= CPUFREQ_TABLE_END; i++){ - opp = opp_find_freq_exact(dev, table[i].frequency * 1000, true); - if (IS_ERR(opp)) - return PTR_ERR(opp); - table[i].index = opp_get_voltage(opp); - } - return 0; -} - static struct cpufreq_frequency_table *of_get_temp_limit_table(struct device_node *dev_node, const char *propname) { struct cpufreq_frequency_table *temp_limt_table = NULL; @@ -1642,6 +1779,167 @@ static struct cpufreq_frequency_table *of_get_temp_limit_table(struct device_nod } +static int of_get_dvfs_table(struct device_node *dev_node, + struct cpufreq_frequency_table **dvfs_table) +{ + struct cpufreq_frequency_table *tmp_dvfs_table = NULL; + const struct property *prop; + const __be32 *val; + int nr, i; + + prop = of_find_property(dev_node, "operating-points", NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -EINVAL; + + nr = prop->length / sizeof(u32); + if (nr % 2) { + pr_err("%s: Invalid freq list\n", __func__); + return -EINVAL; + } + + tmp_dvfs_table = kzalloc(sizeof(*tmp_dvfs_table) * + (nr/2 + 1), GFP_KERNEL); + val = prop->value; + + for (i = 0; i < nr/2; i++) { + tmp_dvfs_table[i].frequency = be32_to_cpup(val++); + tmp_dvfs_table[i].index = be32_to_cpup(val++); + } + + tmp_dvfs_table[i].index = 0; + tmp_dvfs_table[i].frequency = CPUFREQ_TABLE_END; + + *dvfs_table = tmp_dvfs_table; + + return 0; +} + + +static int of_get_dvfs_pvtm_table(struct device_node *dev_node, + struct cpufreq_frequency_table **dvfs_table, + struct cpufreq_frequency_table **pvtm_table) +{ + struct cpufreq_frequency_table *tmp_dvfs_table = NULL; + struct cpufreq_frequency_table *tmp_pvtm_table = NULL; + const struct property *prop; + const __be32 *val; + int nr, i; + + prop = of_find_property(dev_node, "pvtm-operating-points", NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -EINVAL; + + nr = prop->length / sizeof(u32); + if (nr % 3) { + pr_err("%s: Invalid freq list\n", __func__); + return -EINVAL; + } + + tmp_dvfs_table = kzalloc(sizeof(*tmp_dvfs_table) * + (nr/3 + 1), GFP_KERNEL); + + tmp_pvtm_table = kzalloc(sizeof(*tmp_pvtm_table) * + (nr/3 + 1), GFP_KERNEL); + + val = prop->value; + + for (i = 0; i < nr/3; i++) { + tmp_dvfs_table[i].frequency = be32_to_cpup(val++); + tmp_dvfs_table[i].index = be32_to_cpup(val++); + + tmp_pvtm_table[i].frequency = tmp_dvfs_table[i].frequency; + tmp_pvtm_table[i].index = be32_to_cpup(val++); + } + + tmp_dvfs_table[i].index = 0; + tmp_dvfs_table[i].frequency = CPUFREQ_TABLE_END; + + tmp_pvtm_table[i].index = 0; + tmp_pvtm_table[i].frequency = CPUFREQ_TABLE_END; + + *dvfs_table = tmp_dvfs_table; + *pvtm_table = tmp_pvtm_table; + + return 0; +} + +static int dvfs_node_parse_dt(struct device_node *np, + struct dvfs_node *dvfs_node) +{ + int process_version = rockchip_process_version(); + int i = 0; + int ret; + + of_property_read_u32_index(np, "channel", 0, &dvfs_node->channel); + + pr_info("channel:%d, lkg:%d\n", + dvfs_node->channel, rockchip_get_leakage(dvfs_node->channel)); + + of_property_read_u32_index(np, "regu-mode-en", 0, + &dvfs_node->regu_mode_en); + if (dvfs_node->regu_mode_en) + dvfs_node->regu_mode_table = of_get_regu_mode_table(np); + else + dvfs_node->regu_mode_table = NULL; + + of_property_read_u32_index(np, "temp-limit-enable", 0, + &temp_limit_enable); + if (temp_limit_enable) { + of_property_read_u32_index(np, "target-temp", 0, &target_temp); + pr_info("target-temp:%d\n", target_temp); + dvfs_node->nor_temp_limit_table = + of_get_temp_limit_table(np, + "normal-temp-limit"); + dvfs_node->per_temp_limit_table = + of_get_temp_limit_table(np, + "performance-temp-limit"); + dvfs_node->virt_temp_limit_table[0] = + of_get_temp_limit_table(np, + "virt-temp-limit-1-cpu-busy"); + dvfs_node->virt_temp_limit_table[1] = + of_get_temp_limit_table(np, + "virt-temp-limit-2-cpu-busy"); + dvfs_node->virt_temp_limit_table[2] = + of_get_temp_limit_table(np, + "virt-temp-limit-3-cpu-busy"); + dvfs_node->virt_temp_limit_table[3] = + of_get_temp_limit_table(np, + "virt-temp-limit-4-cpu-busy"); + } + dvfs_node->temp_limit_rate = -1; + + ret = of_property_read_u32_index(np, "support-pvtm", 0, + &dvfs_node->support_pvtm); + if (!ret) { + if (of_get_dvfs_pvtm_table(np, &dvfs_node->dvfs_table, + &dvfs_node->pvtm_table)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pvtm_info_table); i++) { + struct pvtm_info *pvtm_info = pvtm_info_table[i]; + + if ((pvtm_info->channel == dvfs_node->channel) && + (pvtm_info->process_version == process_version) && + of_machine_is_compatible(pvtm_info->compatible)) { + dvfs_node->pvtm_info = pvtm_info; + break; + } + } + + if (!dvfs_node->pvtm_info) + dvfs_node->support_pvtm = 0; + } else { + if (of_get_dvfs_table(np, &dvfs_node->dvfs_table)) + return -EINVAL; + } + + return 0; +} + int of_dvfs_init(void) { struct vd_node *vd; @@ -1649,10 +1947,10 @@ int of_dvfs_init(void) struct device_node *dvfs_dev_node, *clk_dev_node, *vd_dev_node, *pd_dev_node; struct dvfs_node *dvfs_node; struct clk *clk; - const __be32 *val; int ret; DVFS_DBG("%s\n", __func__); + pr_info("process version: %d\n", rockchip_process_version()); dvfs_dev_node = of_find_node_by_name(NULL, "dvfs"); if (IS_ERR_OR_NULL(dvfs_dev_node)) { @@ -1717,56 +2015,8 @@ int of_dvfs_init(void) dvfs_node->pd = pd; dvfs_node->vd = vd; - val = of_get_property(clk_dev_node, "regu-mode-en", NULL); - if (val) - dvfs_node->regu_mode_en = be32_to_cpup(val); - if (dvfs_node->regu_mode_en) - dvfs_node->regu_mode_table = of_get_regu_mode_table(clk_dev_node); - else - dvfs_node->regu_mode_table = NULL; - - val = of_get_property(clk_dev_node, "temp-limit-enable", NULL); - if (val) - temp_limit_enable = be32_to_cpup(val); - if (temp_limit_enable) { - val = of_get_property(clk_dev_node, "target-temp", NULL); - if (val) - target_temp = be32_to_cpup(val); - val = of_get_property(clk_dev_node, "temp-channel", NULL); - if (val) - dvfs_node->temp_channel = be32_to_cpup(val); - - dvfs_node->nor_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "normal-temp-limit"); - dvfs_node->per_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "performance-temp-limit"); - dvfs_node->virt_temp_limit_table[0] = - of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-1-cpu-busy"); - dvfs_node->virt_temp_limit_table[1] = - of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-2-cpu-busy"); - dvfs_node->virt_temp_limit_table[2] = - of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-3-cpu-busy"); - dvfs_node->virt_temp_limit_table[3] = - of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-4-cpu-busy"); - } - dvfs_node->temp_limit_rate = -1; - dvfs_node->dev.of_node = clk_dev_node; - ret = of_init_opp_table(&dvfs_node->dev); - if (ret) { - DVFS_ERR("%s:clk(%s) get opp table err:%d\n", __func__, dvfs_node->name, ret); - kfree(dvfs_node); - continue; - } - - ret = opp_init_cpufreq_table(&dvfs_node->dev, &dvfs_node->dvfs_table); - if (ret) { - DVFS_ERR("%s:clk(%s) get cpufreq table err:%d\n", __func__, dvfs_node->name, ret); - kfree(dvfs_node); + if (dvfs_node_parse_dt(clk_dev_node, dvfs_node)) continue; - } - ret = rk_convert_cpufreq_table(dvfs_node); - if (ret) { - kfree(dvfs_node); - continue; - } clk = clk_get(NULL, clk_dev_node->name); if (IS_ERR(clk)){ diff --git a/include/linux/rockchip/common.h b/include/linux/rockchip/common.h index 986943afa2f1..cf51a1ce95c1 100644 --- a/include/linux/rockchip/common.h +++ b/include/linux/rockchip/common.h @@ -76,6 +76,7 @@ int rockchip_unregister_system_status_notifier(struct notifier_block *nb); int rockchip_set_system_status(unsigned long status); int rockchip_clear_system_status(unsigned long status); unsigned long rockchip_get_system_status(void); +u32 pvtm_get_value(u32 ch, u32 time_us); #define INVALID_TEMP INT_MAX #if IS_ENABLED(CONFIG_SENSORS_ROCKCHIP_TSADC) diff --git a/include/linux/rockchip/dvfs.h b/include/linux/rockchip/dvfs.h index 209374a21182..47c3d084fd9d 100644 --- a/include/linux/rockchip/dvfs.h +++ b/include/linux/rockchip/dvfs.h @@ -18,6 +18,10 @@ #include #include +#define ARM_DVFS_CH 0 +#define GPU_DVFS_CH 1 +#define LOG_DVFS_CH 2 + struct dvfs_node; typedef int (*dvfs_set_rate_callback)(struct dvfs_node *clk_dvfs_node, unsigned long rate); typedef int (*clk_set_rate_callback)(struct clk *clk, unsigned long rate); @@ -76,6 +80,21 @@ struct pd_node { unsigned int regu_mode; }; +struct pvtm_info { + const char *compatible; + struct cpufreq_frequency_table *pvtm_table; + int channel; + int process_version; + int scan_rate_hz; + int sample_time_us; + int volt_step_uv; + int delta_pvtm_by_volt; + int delta_pvtm_by_temp; + int volt_margin_uv; + int min_volt_uv; + int max_volt_uv; +}; + /** * struct dvfs_node: To Store All dvfs clocks' info * @name: Dvfs clock's Name @@ -96,9 +115,11 @@ struct dvfs_node { int set_volt; //MV int enable_count; int freq_limit_en; //sign if use limit frequency + int support_pvtm; unsigned int min_rate; //limit min frequency unsigned int max_rate; //limit max frequency unsigned long last_set_rate; + unsigned int channel; unsigned int temp_channel; unsigned long temp_limit_rate; struct clk *clk; @@ -107,6 +128,7 @@ struct dvfs_node { struct list_head node; struct notifier_block *dvfs_nb; struct cpufreq_frequency_table *dvfs_table; + struct cpufreq_frequency_table *pvtm_table; struct cpufreq_frequency_table *per_temp_limit_table; struct cpufreq_frequency_table *nor_temp_limit_table; struct cpufreq_frequency_table *virt_temp_limit_table[4]; @@ -114,6 +136,7 @@ struct dvfs_node { struct cpufreq_frequency_table *regu_mode_table; int regu_mode_en; unsigned int regu_mode; + struct pvtm_info *pvtm_info; }; -- 2.34.1