You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
446 lines
12 KiB
446 lines
12 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2020. All rights reserved.
|
|
* Description: dvfs file
|
|
*/
|
|
#define LOG_MODULE_ID SOC_ID_DVFS
|
|
#define LOG_FUNC_TRACE 1
|
|
|
|
#include "drv_dvfs.h"
|
|
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <asm/io.h>
|
|
#include <mntn/rdr_platform.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include "soc_log.h"
|
|
#include "osal_ext.h"
|
|
#include "drv_sys_ext.h"
|
|
#include "td_type.h"
|
|
|
|
#include "drv_dvfs_common.h"
|
|
#include "drv_dvfs_volt_calc.h"
|
|
|
|
#ifdef CONFIG_SOCT_TEMP_CTRL_SUPPORT
|
|
#include "drv_dvfs_temp_ctrl.h"
|
|
#endif
|
|
|
|
#ifndef CONFIG_SOCT_COMMON_KERNEL
|
|
#include "drv_dvfs_update.h"
|
|
#endif
|
|
|
|
td_void dvfs_freq_volt_matching_check(td_void)
|
|
{
|
|
td_s32 ret;
|
|
td_s32 cur_volt_uv;
|
|
td_s32 diff;
|
|
td_u32 need_volt_mv = 0;
|
|
td_u32 cpu, freq;
|
|
struct hl_dvfs_info *info = TD_NULL;
|
|
|
|
for_each_online_cpu(cpu) {
|
|
info = dvfs_get_cluster(cpu);
|
|
if ((info != TD_NULL) && (info->cpu_reg != TD_NULL) && (info->cpu_clk != TD_NULL)) {
|
|
mutex_lock(&info->dvfs_lock);
|
|
|
|
freq = clk_get_rate(info->cpu_clk);
|
|
|
|
cur_volt_uv = regulator_get_voltage(info->cpu_reg);
|
|
if (cur_volt_uv <= 0) {
|
|
soc_err_print_call_fun_err(regulator_get_voltage, cur_volt_uv);
|
|
soc_err_print_s32(cur_volt_uv);
|
|
mutex_unlock(&info->dvfs_lock);
|
|
rdr_system_error(MODID_AP_S_DVFS_NOPANIC, 0, 0);
|
|
return;
|
|
}
|
|
|
|
ret = dvfs_get_volt_for_new_freq(&need_volt_mv, freq);
|
|
if (ret) {
|
|
soc_err_print_call_fun_err(dvfs_get_volt_for_new_freq, ret);
|
|
}
|
|
|
|
diff = cur_volt_uv / VOLT_UV_TO_MV - need_volt_mv;
|
|
if (diff < 0) {
|
|
soc_err_print_info("Cpu volt and freq do not match!");
|
|
soc_err_print_u32(cpu);
|
|
soc_err_print_u32(freq);
|
|
soc_err_print_u32(cur_volt_uv);
|
|
soc_err_print_u32(need_volt_mv);
|
|
rdr_system_error(MODID_AP_S_DVFS_PANIC, 0, 0);
|
|
} else if (diff >= VOLT_DIFF_VALUE) {
|
|
soc_err_print_info("Cpu volt and freq do not match!");
|
|
soc_err_print_u32(cpu);
|
|
soc_err_print_u32(freq);
|
|
soc_err_print_u32(cur_volt_uv);
|
|
soc_err_print_u32(need_volt_mv);
|
|
rdr_system_error(MODID_AP_S_DVFS_NOPANIC, 0, 0);
|
|
}
|
|
|
|
mutex_unlock(&info->dvfs_lock);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef CONFIG_SOCT_CPU_DVFS_SUPPORT
|
|
td_s32 dvfs_round_freq(td_u32 cpu, td_u32 *rate)
|
|
{
|
|
td_u32 freq;
|
|
struct cpufreq_policy *policy = TD_NULL;
|
|
struct cpufreq_frequency_table *freq_table = TD_NULL;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
dvfs_check_param_return_if(rate == TD_NULL, TD_FAILURE);
|
|
|
|
freq = *rate;
|
|
|
|
policy = cpufreq_cpu_get_raw(cpu);
|
|
if ((policy == TD_NULL) || (policy->freq_table == TD_NULL)) {
|
|
soc_err_print_call_fun_err(cpufreq_cpu_get_raw, TD_FAILURE);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
freq_table = policy->freq_table;
|
|
|
|
while (freq_table->frequency != CPUFREQ_TABLE_END) {
|
|
if (freq >= freq_table->frequency - MAX_DIFF_VALUE && freq <= freq_table->frequency + MAX_DIFF_VALUE) {
|
|
*rate = freq_table->frequency;
|
|
break;
|
|
}
|
|
|
|
freq_table++;
|
|
}
|
|
|
|
if (freq_table->frequency == CPUFREQ_TABLE_END) {
|
|
soc_err_print_info("freq is not in freq_table\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 dvfs_set_governor(const char *buf)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 cpu;
|
|
struct cpufreq_policy *policy = TD_NULL;
|
|
struct hl_dvfs_info *info = TD_NULL;
|
|
|
|
for_each_online_cpu(cpu) {
|
|
policy = cpufreq_cpu_get_raw(cpu);
|
|
if (policy == TD_NULL) {
|
|
soc_err_print_call_fun_err(cpufreq_cpu_get_raw, TD_FAILURE);
|
|
soc_err_print_u32(cpu);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
info = dvfs_get_cluster(cpu);
|
|
if (info != TD_NULL) {
|
|
soc_print("set cpufreq governor to %s\n", buf);
|
|
ret = set_scaling_governor(policy, buf);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(set_scaling_governor, ret);
|
|
soc_err_print_u32(cpu);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 dvfs_enter_active_standby(td_u32 model)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
ret = dvfs_set_governor("powersave");
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dvfs_set_governor, ret);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 dvfs_quit_active_standby(td_void)
|
|
{
|
|
td_s32 ret;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
ret = dvfs_set_governor("schedutil");
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dvfs_set_governor, ret);
|
|
return ret;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
#else
|
|
td_s32 dvfs_round_freq(td_u32 cpu, td_u32 *rate)
|
|
{
|
|
td_u8 i;
|
|
td_u32 freq;
|
|
|
|
soc_dbg_func_enter();
|
|
|
|
dvfs_check_param_return_if(rate == TD_NULL, TD_FAILURE);
|
|
|
|
freq = *rate;
|
|
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
if ((freq >= g_volt_cal_info[i].freq - MAX_DIFF_VALUE) && (freq <= g_volt_cal_info[i].freq + MAX_DIFF_VALUE)) {
|
|
*rate = g_volt_cal_info[i].freq;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == (td_u8)elements_in(g_volt_cal_info)) {
|
|
soc_err_print_info("freq is not in freq_table\n");
|
|
soc_err_print_u32(freq);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 dvfs_enter_active_standby(td_u32 model)
|
|
{
|
|
soc_dbg_func_enter();
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 dvfs_quit_active_standby(td_void)
|
|
{
|
|
soc_dbg_func_enter();
|
|
soc_dbg_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
td_s32 __weak otp_reg_ioremap(td_void)
|
|
{
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void __weak otp_reg_iounmap(td_void)
|
|
{
|
|
}
|
|
|
|
td_s32 dvfs_init_volt_cal_info(td_void)
|
|
{
|
|
td_s32 ret;
|
|
td_u8 i;
|
|
td_u32 volt_min, volt_max, volt_otp_added;
|
|
|
|
ret = otp_reg_ioremap();
|
|
if (ret == TD_FAILURE) {
|
|
soc_err_print_call_fun_err(otp_reg_ioremap, ret);
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
ret = dvfs_init_corner_volt(g_volt_cal_info[i].freq, &volt_min, &volt_max);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dvfs_init_corner_volt, ret);
|
|
otp_reg_iounmap();
|
|
return ret;
|
|
}
|
|
|
|
g_volt_cal_info[i].volt = volt_min;
|
|
soc_info_print_u32(g_volt_cal_info[i].volt);
|
|
|
|
g_volt_cal_info[i].volt_max = volt_max;
|
|
soc_info_print_u32(g_volt_cal_info[i].volt_max);
|
|
|
|
ret = dvfs_init_otp_compensation_volt(g_volt_cal_info[i].freq, &volt_otp_added);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dvfs_init_otp_compensation_volt, ret);
|
|
otp_reg_iounmap();
|
|
return ret;
|
|
}
|
|
|
|
g_volt_cal_info[i].volt_otp_added = volt_otp_added;
|
|
soc_info_print_u32(g_volt_cal_info[i].volt_otp_added);
|
|
}
|
|
|
|
otp_reg_iounmap();
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
#ifndef CONFIG_SOCT_COMMON_KERNEL
|
|
td_void dvfs_set_agint_test_volt(td_bool increase, td_u32 volt)
|
|
{
|
|
td_u8 i;
|
|
td_u32 data_increase;
|
|
td_u32 data_decrease;
|
|
efi_status_t status;
|
|
td_ulong data_size = sizeof(volt);
|
|
efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
|
|
|
|
if (efi.set_variable == TD_NULL) {
|
|
soc_err_print_info("efi.set_variable is NULL pointer");
|
|
return;
|
|
}
|
|
|
|
if (increase) {
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
g_volt_cal_info[i].volt_increase_test = volt;
|
|
g_volt_cal_info[i].volt_decrease_test = 0;
|
|
}
|
|
|
|
data_increase = volt;
|
|
data_decrease = 0;
|
|
} else {
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
g_volt_cal_info[i].volt_increase_test = 0;
|
|
g_volt_cal_info[i].volt_decrease_test = volt;
|
|
}
|
|
|
|
data_increase = 0;
|
|
data_decrease = volt;
|
|
}
|
|
|
|
status = efi.set_variable(L"cpu_volt_increase", &guid, EFI_VARIABLE_BUTE, data_size, &data_increase);
|
|
if (status != EFI_SUCCESS) {
|
|
soc_err_print_call_fun_err(efi.set_variable, status);
|
|
return;
|
|
}
|
|
|
|
status = efi.set_variable(L"cpu_volt_decrease", &guid, EFI_VARIABLE_BUTE, data_size, &data_decrease);
|
|
if (status != EFI_SUCCESS) {
|
|
soc_err_print_call_fun_err(efi.set_variable, status);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
td_void dvfs_get_agint_test_volt(td_void)
|
|
{
|
|
td_u8 i;
|
|
td_s32 ret;
|
|
td_s32 cpu;
|
|
efi_status_t status;
|
|
td_u32 data_increase = 0;
|
|
td_u32 data_decrease = 0;
|
|
td_ulong data_size = sizeof(data_increase);
|
|
efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
|
|
struct hl_dvfs_info *info = TD_NULL;
|
|
|
|
if (efi.get_variable == TD_NULL) {
|
|
soc_err_print_info("efi.get_variable is NULL pointer");
|
|
return;
|
|
}
|
|
status = efi.get_variable(L"cpu_volt_increase", &guid, NULL, &data_size, &data_increase);
|
|
if (status != EFI_SUCCESS) {
|
|
soc_warn_print_call_fun_err(efi.get_variable, status);
|
|
return;
|
|
}
|
|
|
|
status = efi.get_variable(L"cpu_volt_decrease", &guid, NULL, &data_size, &data_decrease);
|
|
if (status != EFI_SUCCESS) {
|
|
soc_warn_print_call_fun_err(efi.get_variable, status);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
g_volt_cal_info[i].volt_increase_test = data_increase;
|
|
g_volt_cal_info[i].volt_decrease_test = data_decrease;
|
|
}
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
info = dvfs_get_cluster((td_u32)cpu);
|
|
if (info != TD_NULL) {
|
|
ret = dvfs_update(info, 0, 0);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_err_print_call_fun_err(dvfs_update, ret);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
td_s32 dvfs_get_volt_for_new_freq(td_u32 *volt, td_u32 new_freq)
|
|
{
|
|
td_u8 i;
|
|
td_u8 target = 0;
|
|
td_u32 new_volt;
|
|
dvfs_temperature_status status = {0};
|
|
|
|
dvfs_check_param_return_if(volt == TD_NULL, TD_FAILURE);
|
|
|
|
for (i = 0; i < (td_u8)elements_in(g_volt_cal_info); i++) {
|
|
if (new_freq >= g_volt_cal_info[i].freq - MAX_DIFF_VALUE &&
|
|
new_freq <= g_volt_cal_info[i].freq + MAX_DIFF_VALUE) {
|
|
target = i;
|
|
soc_info_print_u32(g_volt_cal_info[i].freq);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == (td_u8)elements_in(g_volt_cal_info)) {
|
|
soc_err_print_info("new freq is invalid\n");
|
|
soc_err_print_u32(new_freq);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* get volt for new freq by hpm line */
|
|
new_volt = g_volt_cal_info[target].volt;
|
|
soc_info_print_u32(g_volt_cal_info[target].volt);
|
|
soc_info_print_u32(g_volt_cal_info[target].volt_max);
|
|
|
|
/* otp compensation */
|
|
new_volt += g_volt_cal_info[target].volt_otp_added;
|
|
soc_info_print_u32(g_volt_cal_info[target].volt_otp_added);
|
|
|
|
/* limit */
|
|
if ((g_volt_cal_info[target].volt_down_limit != 0) &&
|
|
(new_volt < g_volt_cal_info[target].volt_down_limit)) {
|
|
new_volt = g_volt_cal_info[target].volt_down_limit;
|
|
}
|
|
if ((g_volt_cal_info[target].volt_up_limit != 0) &&
|
|
(new_volt > g_volt_cal_info[target].volt_up_limit)) {
|
|
new_volt = g_volt_cal_info[target].volt_up_limit;
|
|
}
|
|
|
|
/* low temperature compensation */
|
|
#ifdef CONFIG_SOCT_TEMP_CTRL_SUPPORT
|
|
dvfs_get_temperature_status(&status);
|
|
if (status.low_temp_comp) {
|
|
new_volt += g_volt_cal_info[target].volt_low_temperature_added;
|
|
soc_info_print_u32(g_volt_cal_info[target].volt_low_temperature_added);
|
|
} else if (status.high_temp_comp) {
|
|
new_volt += g_volt_cal_info[target].volt_high_temperature_added;
|
|
soc_info_print_u32(g_volt_cal_info[target].volt_high_temperature_added);
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_SOCT_COMMON_KERNEL
|
|
/* increase or decrease cpu volt */
|
|
new_volt += g_volt_cal_info[target].volt_increase_test;
|
|
new_volt -= g_volt_cal_info[target].volt_decrease_test;
|
|
#endif
|
|
|
|
new_volt = (new_volt > g_volt_cal_info[target].volt_max) ? g_volt_cal_info[target].volt_max : new_volt;
|
|
|
|
*volt = new_volt;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|