// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #ifndef BUILD_POLICY_TEST #include #include #include #include #include #include #endif #include "apu_power_api.h" #include "apusys_power_debug.h" #include "apusys_power_ctl.h" #include "apusys_power_cust.h" #include "apusys_power.h" #include "apu_power_table.h" #include "hal_config_power.h" #include "apu_log.h" #ifdef APUPWR_TAG_TP #include "apupwr_events.h" #endif static struct mutex power_dvfs_mtx; struct apusys_dvfs_opps apusys_opps; static spinlock_t ipuif_lock; bool dvfs_user_support(enum DVFS_USER user) { return apusys_dvfs_user_support[user]; } bool dvfs_power_domain_support(enum DVFS_VOLTAGE_DOMAIN domain) { return apusys_dvfs_buck_domain_support[domain]; } int32_t apusys_thermal_en_throttle_cb(enum DVFS_USER user, enum APU_OPP_INDEX opp) { // need to check constraint voltage, fixed me switch (user) { #ifdef CONFIG_MTK_APUSYS_VPU case VPU0: for (user = VPU0; user < VPU0 + APUSYS_VPU_NUM; user++) apusys_opps.thermal_opp[user] = opp; break; #endif #ifdef CONFIG_MTK_APUSYS_MDLA_SUPPORT case MDLA0: for (user = MDLA0; user < MDLA0 + APUSYS_MDLA_NUM; user++) apusys_opps.thermal_opp[user] = opp; break; #endif default: apusys_opps.thermal_opp[user] = opp; break; } PWR_LOG_INF("%s, user=%d, opp=%d\n", __func__, user, opp); if (apusys_opps.is_power_on[user]) event_trigger_dvfs_policy(); return 0; } int32_t apusys_thermal_dis_throttle_cb(enum DVFS_USER user) { switch (user) { #ifdef CONFIG_MTK_APUSYS_VPU case VPU0: for (user = VPU0; user < VPU0 + APUSYS_VPU_NUM; user++) apusys_opps.thermal_opp[user] = 0; break; #endif #ifdef CONFIG_MTK_APUSYS_MDLA_SUPPORT case MDLA0: for (user = MDLA0; user < MDLA0 + APUSYS_MDLA_NUM; user++) apusys_opps.thermal_opp[user] = 0; break; #endif default: apusys_opps.thermal_opp[user] = 0; break; } PWR_LOG_INF("%s, user=%d, opp=0\n", __func__, user); if (apusys_opps.is_power_on[user]) event_trigger_dvfs_policy(); return 0; } int apusys_opp_to_boost_value(enum DVFS_USER user, uint8_t opp) { int boost_value; uint32_t max_freq = 0, freq = 0; enum DVFS_VOLTAGE_DOMAIN buck_domain = apusys_user_to_buck_domain[user]; if (opp >= APUSYS_MAX_NUM_OPPS) { PWR_LOG_ERR("%s invalid opp : %d\n", __func__, opp); return -1; } max_freq = apusys_opps.opps[0][buck_domain].freq; freq = apusys_opps.opps[opp][buck_domain].freq; boost_value = (freq * 100) / max_freq; if (boost_value > 100 || boost_value < 0) { PWR_LOG_ERR("%s invalid boost : %d\n", __func__, boost_value); return -1; } return boost_value; } uint8_t apusys_boost_value_to_opp(enum DVFS_USER user, uint8_t boost_value) { uint8_t i = 0; uint8_t opp = APUSYS_MAX_NUM_OPPS-1; uint32_t max_freq = 0, freq = 0; enum DVFS_VOLTAGE_DOMAIN buck_domain = apusys_user_to_buck_domain[user]; if (boost_value >= 100) { opp = 0; } else { max_freq = apusys_opps.opps[0][buck_domain].freq; freq = boost_value * max_freq / 100; for (i = 1; i < APUSYS_MAX_NUM_OPPS; i++) { if (freq > apusys_opps.opps[i][buck_domain].freq) { opp = i-1; break; } } } PWR_LOG_INF( "%s, user=%d, boost_value=%d,max_freq=%d, freq=%d, opp=%d\n", __func__, user, boost_value, max_freq, freq, opp); return opp; } EXPORT_SYMBOL(apusys_boost_value_to_opp); enum DVFS_FREQ apusys_opp_to_freq(enum DVFS_USER user, uint8_t opp) { enum DVFS_FREQ freq = DVFS_FREQ_NOT_SUPPORT; enum DVFS_VOLTAGE_DOMAIN buck_domain; if (user >= APUSYS_DVFS_USER_NUM) return freq; buck_domain = apusys_user_to_buck_domain[user]; if (opp < APUSYS_MAX_NUM_OPPS) freq = apusys_opps.opps[opp][buck_domain].freq; return freq; } EXPORT_SYMBOL(apusys_opp_to_freq); uint8_t apusys_freq_to_opp(enum DVFS_VOLTAGE_DOMAIN buck_domain, uint32_t freq) { uint8_t opp = 0; uint32_t next_freq = 0; for (opp = 0 ; opp < APUSYS_MAX_NUM_OPPS - 1 ; opp++) { next_freq = apusys_opps.opps[opp+1][buck_domain].freq + 1; if (freq >= next_freq && freq <= (apusys_opps.opps[opp][buck_domain].freq + 1)) break; } return opp; } EXPORT_SYMBOL(apusys_freq_to_opp); int8_t apusys_get_opp(enum DVFS_USER user) { enum DVFS_VOLTAGE_DOMAIN buck_domain; if (user >= APUSYS_DVFS_USER_NUM) return -1; buck_domain = apusys_user_to_buck_domain[user]; return apusys_opps.cur_opp_index[buck_domain]; } EXPORT_SYMBOL(apusys_get_opp); int8_t apusys_get_ceiling_opp(enum DVFS_USER user) { uint8_t used_opp; enum DVFS_VOLTAGE_DOMAIN buck_domain; if (user >= APUSYS_DVFS_USER_NUM) return -1; buck_domain = apusys_user_to_buck_domain[user]; used_opp = apusys_opps.cur_opp_index[buck_domain]; // upper bound for power hal used_opp = MAX(used_opp, apusys_opps.power_lock_min_opp[user]); // lower bound for power hal used_opp = MIN(used_opp, apusys_opps.power_lock_max_opp[user]); // upper bound for thermal used_opp = MAX(used_opp, apusys_opps.thermal_opp[user]); return used_opp; } EXPORT_SYMBOL(apusys_get_ceiling_opp); void apusys_set_pwr_lock(enum DVFS_USER user, uint8_t min_opp, uint8_t max_opp) { apusys_opps.power_lock_min_opp[user] = min_opp; apusys_opps.power_lock_max_opp[user] = max_opp; PWR_LOG_INF("%s, user=%d, min_opp=%d, max_opp=%d,\n", __func__, user, min_opp, max_opp); } uint8_t apusys_pwr_max_min_check(enum DVFS_USER user, uint8_t opp) { uint8_t used_opp = opp; // upper bound for power hal used_opp = MAX(used_opp, apusys_opps.power_lock_min_opp[user]); // lower bound for power hal used_opp = MIN(used_opp, apusys_opps.power_lock_max_opp[user]); // upper bound for thermal used_opp = MAX(used_opp, apusys_opps.thermal_opp[user]); PWR_LOG_INF( "%s, %s, used_opp=%d,thermal_opp=%d,pwr_lock_min=%d,pwr_lock_max=%d\n", __func__, user_str[user], used_opp, apusys_opps.thermal_opp[user], apusys_opps.power_lock_min_opp[user], apusys_opps.power_lock_max_opp[user]); return used_opp; } void apusys_clk_path_update_pwr(enum DVFS_USER user, enum DVFS_VOLTAGE voltage) { uint8_t path_volt_index = 0; if (apusys_opps.is_power_on[user] == false) return; for (path_volt_index = 0; path_volt_index < APUSYS_PATH_USER_NUM; path_volt_index++){ apusys_opps.user_path_volt[user][path_volt_index] = (voltage > dvfs_clk_path_max_vol[user][path_volt_index] ? dvfs_clk_path_max_vol[user][path_volt_index] : voltage); PWR_LOG_INF("%s, volt=%d, user_path_volt[%s][%d]=%d\n", __func__, voltage, user_str[user], path_volt_index, apusys_opps.user_path_volt[user][path_volt_index]); } } void apusys_final_volt_check(void) { uint8_t user_index = 0, path_index = 0, buck_index = 0; // for every buck domain, check clk path matrix for buck shared relation for (buck_index = 0; buck_index < APUSYS_BUCK_NUM; buck_index++) { #if !VCORE_DVFS_SUPPORT if (buck_index == VCORE_BUCK) continue; #endif for (user_index = 0; user_index < APUSYS_DVFS_USER_NUM; user_index++) { if (dvfs_user_support(user_index) == false) continue; if (apusys_opps.is_power_on[user_index] == false) continue; for (path_index = 0; path_index < APUSYS_PATH_USER_NUM; path_index++) { if (buck_shared[buck_index][user_index][path_index] == true) { apusys_opps.next_buck_volt[buck_index] = MAX(apusys_opps.next_buck_volt[buck_index], apusys_opps.user_path_volt[user_index][path_index]); PWR_LOG_INF("%s, %s = %d,(%s,%d)=%d\n", __func__, buck_str[buck_index], apusys_opps.next_buck_volt[buck_index], user_str[user_index], path_index, apusys_opps.user_path_volt[user_index][path_index]); } } } } } void apusys_pwr_constraint_check(void) { uint8_t i = 0; int8_t opp_index = APUSYS_MAX_NUM_OPPS-1; enum DVFS_BUCK buck0; enum DVFS_BUCK buck1; enum DVFS_VOLTAGE voltage0; enum DVFS_VOLTAGE voltage1; enum DVFS_VOLTAGE_DOMAIN buck_domain; bool vcore_constraint = false; for (i = 0; i < APUSYS_DVFS_CONSTRAINT_NUM; i++) { buck0 = dvfs_constraint_table[i].buck0; buck1 = dvfs_constraint_table[i].buck1; voltage0 = dvfs_constraint_table[i].voltage0; voltage1 = dvfs_constraint_table[i].voltage1; if (apusys_opps.next_buck_volt[buck0] == voltage0 && apusys_opps.next_buck_volt[buck1] == voltage1) { if (buck0 == VCORE_BUCK) vcore_constraint = true; for (opp_index = APUSYS_MAX_NUM_OPPS-1; opp_index >= 0; opp_index--) { if (voltage0 < voltage1) { buck_domain = apusys_buck_to_buck_domain[buck0]; if (apusys_opps.opps[opp_index][buck_domain].voltage > voltage0) { apusys_opps.next_buck_volt[buck0] = apusys_opps.opps[opp_index][buck_domain].voltage; PWR_LOG_INF("%s, %s from %d --> %d\n", __func__, buck_str[buck0], voltage0, apusys_opps.next_buck_volt[buck0]); break; } } else if (voltage0 > voltage1) { buck_domain = apusys_buck_to_buck_domain[buck1]; if (apusys_opps.opps[opp_index][buck_domain].voltage > voltage1) { apusys_opps.next_buck_volt[buck1] = apusys_opps.opps[opp_index][buck_domain].voltage; PWR_LOG_INF("%s, %s from %d --> %d\n", __func__, buck_str[buck1], voltage1, apusys_opps.next_buck_volt[buck1]); break; } } } } } if (vcore_constraint == false) apusys_opps.next_buck_volt[VCORE_BUCK] = VCORE_DEFAULT_VOLT; } void apusys_pwr_efficiency_check(void) { uint8_t buck_domain_index = 0; uint8_t buck_index = 0; uint8_t opp_index = 0; enum DVFS_USER user; for (buck_domain_index = 0; buck_domain_index < APUSYS_BUCK_DOMAIN_NUM; buck_domain_index++) { if (dvfs_power_domain_support(buck_domain_index) == false) continue; buck_index = apusys_buck_domain_to_buck[buck_domain_index]; for (opp_index = 0; opp_index < APUSYS_MAX_NUM_OPPS; opp_index++) { #ifndef CONFIG_MACH_MT6877 if (apusys_opps.opps[opp_index][buck_domain_index].voltage <= apusys_opps.next_buck_volt[buck_index]){ #else if ((apusys_opps.opps[opp_index][buck_domain_index].voltage <= apusys_opps.next_buck_volt[buck_index]) || (opp_index == (APUSYS_MAX_NUM_OPPS - 1))) { #endif user = apusys_buck_domain_to_user[buck_domain_index]; if (user < APUSYS_DVFS_USER_NUM) { if (apusys_opps.is_power_on[user] == false) continue; apusys_opps.next_opp_index[buck_domain_index] = apusys_pwr_max_min_check(user, opp_index); } else { apusys_opps.next_opp_index[buck_domain_index] = opp_index; } PWR_LOG_INF("%s, %s, opp=%d\n", __func__, buck_domain_str[buck_domain_index], apusys_opps.next_opp_index[buck_domain_index]); break; } } } } void apusys_buck_up_check(void) { uint8_t user = 0; // don't care uint8_t buck_small_index = 0, buck_large_index = 0; struct hal_param_volt volt_data; if (apusys_opps.cur_buck_volt[VCORE_BUCK] < apusys_opps.next_buck_volt[VCORE_BUCK]) { volt_data.target_buck = VCORE_BUCK; volt_data.target_volt = apusys_opps.next_buck_volt[VCORE_BUCK]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if (apusys_opps.cur_buck_volt[VPU_BUCK] < apusys_opps.cur_buck_volt[MDLA_BUCK]) { buck_small_index = VPU_BUCK; buck_large_index = MDLA_BUCK; } else if (apusys_opps.cur_buck_volt[VPU_BUCK] > apusys_opps.cur_buck_volt[MDLA_BUCK]){ buck_small_index = MDLA_BUCK; buck_large_index = VPU_BUCK; } else { if (apusys_opps.next_buck_volt[VPU_BUCK] < apusys_opps.next_buck_volt[MDLA_BUCK]){ buck_small_index = VPU_BUCK; buck_large_index = MDLA_BUCK; } else { buck_small_index = MDLA_BUCK; buck_large_index = VPU_BUCK; } } PWR_LOG_INF("%s,cur vpu=%d, cur mdla=%d, vpu=%d, mdla=%d, vsrm=%d\n", __func__, apusys_opps.cur_buck_volt[VPU_BUCK], apusys_opps.cur_buck_volt[MDLA_BUCK], apusys_opps.next_buck_volt[VPU_BUCK], apusys_opps.next_buck_volt[MDLA_BUCK], apusys_opps.vsram_volatge); if (apusys_opps.vsram_volatge == VSRAM_LOW_VOLT && (apusys_opps.next_buck_volt[VPU_BUCK] > VSRAM_TRANS_VOLT || apusys_opps.next_buck_volt[MDLA_BUCK] > VSRAM_TRANS_VOLT)) { if ((apusys_opps.cur_buck_volt[buck_small_index] < apusys_opps.next_buck_volt[buck_small_index])) { if (apusys_opps.next_buck_volt[buck_small_index] > VSRAM_TRANS_VOLT) volt_data.target_volt = VSRAM_TRANS_VOLT; else volt_data.target_volt = apusys_opps.next_buck_volt[buck_small_index]; volt_data.target_buck = buck_small_index; if (apusys_opps.cur_buck_volt[buck_small_index] != volt_data.target_volt) hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if ((apusys_opps.cur_buck_volt[buck_large_index] < apusys_opps.next_buck_volt[buck_large_index])) { if (apusys_opps.next_buck_volt[buck_large_index] > VSRAM_TRANS_VOLT) volt_data.target_volt = VSRAM_TRANS_VOLT; else volt_data.target_volt = apusys_opps.next_buck_volt[buck_large_index]; volt_data.target_buck = buck_large_index; if (apusys_opps.cur_buck_volt[buck_large_index] != volt_data.target_volt) hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } volt_data.target_buck = SRAM_BUCK; volt_data.target_volt = VSRAM_HIGH_VOLT; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); apusys_opps.vsram_volatge = VSRAM_HIGH_VOLT; if ((apusys_opps.next_buck_volt[buck_small_index] > VSRAM_TRANS_VOLT) && (apusys_opps.next_buck_volt[buck_small_index] > apusys_opps.cur_buck_volt[buck_small_index])) { volt_data.target_buck = buck_small_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_small_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if ((apusys_opps.next_buck_volt[buck_large_index] > VSRAM_TRANS_VOLT) && (apusys_opps.next_buck_volt[buck_large_index] > apusys_opps.cur_buck_volt[buck_large_index])) { volt_data.target_buck = buck_large_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_large_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } } else { if (apusys_opps.next_buck_volt[buck_small_index] > apusys_opps.cur_buck_volt[buck_small_index]) { volt_data.target_buck = buck_small_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_small_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if (apusys_opps.next_buck_volt[buck_large_index] > apusys_opps.cur_buck_volt[buck_large_index]) { volt_data.target_buck = buck_large_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_large_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } } } void apusys_frequency_check(void) { uint8_t buck_domain_index = 0; uint8_t next_opp_index = 0, cur_opp_index = 0; struct hal_param_freq freq_data; enum DVFS_USER user; bool apusys_power_on = false; for (buck_domain_index = 0; buck_domain_index < APUSYS_BUCK_DOMAIN_NUM; buck_domain_index++) { if (dvfs_power_domain_support(buck_domain_index) == false) continue; user = apusys_buck_domain_to_user[buck_domain_index]; if (user < APUSYS_DVFS_USER_NUM) { if (apusys_opps.is_power_on[user] == false) continue; else apusys_power_on = true; } else { if (apusys_power_on == false && #ifndef CONFIG_MACH_MT6853 apusys_opps.is_power_on[EDMA] == false && #if !defined(CONFIG_MACH_MT6873) && !defined(CONFIG_MACH_MT6877) apusys_opps.is_power_on[EDMA2] == false && #endif #endif apusys_opps.is_power_on[REVISER] == false) continue; } if (apusys_opps.cur_opp_index[buck_domain_index] == apusys_opps.next_opp_index[buck_domain_index]) continue; next_opp_index = apusys_opps.next_opp_index[buck_domain_index]; cur_opp_index = apusys_opps.cur_opp_index[buck_domain_index]; if (apusys_opps.opps[next_opp_index][buck_domain_index].freq != apusys_opps.opps[cur_opp_index][buck_domain_index].freq){ freq_data.target_volt_domain = buck_domain_index; freq_data.target_freq = apusys_opps.opps[next_opp_index] [buck_domain_index].freq; hal_config_power(PWR_CMD_SET_FREQ, VPU0, (void *)&freq_data); PWR_LOG_INF("%s, %s, freq from %d --> %d\n", __func__, buck_domain_str[buck_domain_index], apusys_opps.opps[cur_opp_index][buck_domain_index].freq, apusys_opps.opps[next_opp_index][buck_domain_index].freq); } } } void apusys_buck_down_check(void) { uint8_t user = 0; //don't care uint8_t buck_small_index = 0, buck_large_index = 0; struct hal_param_volt volt_data; if (apusys_opps.cur_buck_volt[VPU_BUCK] < apusys_opps.cur_buck_volt[MDLA_BUCK]) { buck_small_index = VPU_BUCK; buck_large_index = MDLA_BUCK; } else if (apusys_opps.cur_buck_volt[VPU_BUCK] > apusys_opps.cur_buck_volt[MDLA_BUCK]){ buck_small_index = MDLA_BUCK; buck_large_index = VPU_BUCK; } else { if (apusys_opps.next_buck_volt[VPU_BUCK] < apusys_opps.next_buck_volt[MDLA_BUCK]){ buck_small_index = VPU_BUCK; buck_large_index = MDLA_BUCK; } else { buck_small_index = MDLA_BUCK; buck_large_index = VPU_BUCK; } } PWR_LOG_INF("%s,cur vpu=%d, cur mdla=%d, vpu=%d, mdla=%d, vsrm=%d\n", __func__, apusys_opps.cur_buck_volt[VPU_BUCK], apusys_opps.cur_buck_volt[MDLA_BUCK], apusys_opps.next_buck_volt[VPU_BUCK], apusys_opps.next_buck_volt[MDLA_BUCK], apusys_opps.vsram_volatge); if (apusys_opps.vsram_volatge == VSRAM_HIGH_VOLT && (apusys_opps.next_buck_volt[VPU_BUCK] <= VSRAM_TRANS_VOLT && apusys_opps.next_buck_volt[MDLA_BUCK] <= VSRAM_TRANS_VOLT)) { if (apusys_opps.cur_buck_volt[buck_large_index] > apusys_opps.next_buck_volt[buck_large_index]){ if (apusys_opps.next_buck_volt[buck_large_index] <= VSRAM_TRANS_VOLT) volt_data.target_volt = VSRAM_TRANS_VOLT; else volt_data.target_volt = apusys_opps.next_buck_volt[buck_large_index]; volt_data.target_buck = buck_large_index; if (apusys_opps.cur_buck_volt[buck_large_index] != volt_data.target_volt) hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if (apusys_opps.cur_buck_volt[buck_small_index] > apusys_opps.next_buck_volt[buck_small_index]){ if (apusys_opps.next_buck_volt[buck_small_index] <= VSRAM_TRANS_VOLT) volt_data.target_volt = VSRAM_TRANS_VOLT; else volt_data.target_volt = apusys_opps.next_buck_volt[buck_small_index]; volt_data.target_buck = buck_small_index; if (apusys_opps.cur_buck_volt[buck_small_index] != volt_data.target_volt) hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } volt_data.target_buck = SRAM_BUCK; volt_data.target_volt = VSRAM_LOW_VOLT; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); apusys_opps.vsram_volatge = VSRAM_LOW_VOLT; } if (apusys_opps.cur_buck_volt[buck_large_index] > apusys_opps.next_buck_volt[buck_large_index]) { volt_data.target_buck = buck_large_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_large_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if (apusys_opps.cur_buck_volt[buck_small_index] > apusys_opps.next_buck_volt[buck_small_index]) { volt_data.target_buck = buck_small_index; volt_data.target_volt = apusys_opps.next_buck_volt[buck_small_index]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } if (apusys_opps.cur_buck_volt[VCORE_BUCK] > apusys_opps.next_buck_volt[VCORE_BUCK]) { volt_data.target_buck = VCORE_BUCK; volt_data.target_volt = apusys_opps.next_buck_volt[VCORE_BUCK]; hal_config_power(PWR_CMD_SET_VOLT, user, (void *)&volt_data); } } void apusys_dvfs_info(void) { char logv_str[128], logf_str[128], log_str[128]; uint8_t domain; uint8_t user; int div = 1000; uint8_t cur_opp_index[APUSYS_BUCK_DOMAIN_NUM]; uint8_t next_opp_index[APUSYS_BUCK_DOMAIN_NUM]; uint8_t c_opp_index; uint8_t n_opp_index; unsigned long rem_nsec; int ret, ret_v, ret_f = 0; ret = sprintf(log_str, "(u_op,T,min,max)"); if (ret < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret); ret_v = sprintf(logv_str, "v["); if (ret_v < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_v); ret_f = sprintf(logf_str, "f["); if (ret_f < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_f); for (user = 0; user < APUSYS_DVFS_USER_NUM; user++) { if (dvfs_user_support(user) == false) continue; ret = sprintf(log_str + strlen(log_str), ",(%d,%d,%d,%d)", apusys_opps.driver_opp_index[user], apusys_opps.thermal_opp[user], apusys_opps.power_lock_min_opp[user], apusys_opps.power_lock_max_opp[user]); if (ret < 0) LOG_ERR("%s sprintf fail\n", __func__); } for (domain = 0; domain < APUSYS_BUCK_DOMAIN_NUM; domain++) { if (dvfs_power_domain_support(domain) == false) continue; cur_opp_index[domain] = apusys_opps.cur_opp_index[domain]; c_opp_index = cur_opp_index[domain]; next_opp_index[domain] = apusys_opps.next_opp_index[domain]; n_opp_index = next_opp_index[domain]; ret_v = sprintf(logv_str + strlen(logv_str), ",(%d,%d)", apusys_opps.opps[c_opp_index][domain].voltage / div, apusys_opps.opps[n_opp_index][domain].voltage / div); if (ret_v < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_v); ret_f = sprintf(logf_str + strlen(logf_str), ",(%d,%d)", apusys_opps.opps[c_opp_index][domain].freq / div, apusys_opps.opps[n_opp_index][domain].freq / div); if (ret_f < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_f); } rem_nsec = do_div(apusys_opps.id, 1000000000); ret = sprintf(log_str + strlen(log_str), "] [%5lu.%06lu]", (unsigned long)apusys_opps.id, rem_nsec / 1000); if (ret < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret); ret_v = sprintf(logv_str + strlen(logv_str), "] [%5lu.%06lu]", (unsigned long)apusys_opps.id, rem_nsec / 1000); if (ret_v < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_v); ret_f = sprintf(logf_str + strlen(logf_str), "] [%5lu.%06lu]", (unsigned long)apusys_opps.id, rem_nsec / 1000); if (ret_f < 0) LOG_ERR("%s sprintf fail (%d)\n", __func__, ret_f); PWR_LOG_PM("APUPWR DVFS %s\n", log_str); PWR_LOG_PM("APUPWR DVFS %s\n", logv_str); PWR_LOG_PM("APUPWR DVFS %s\n", logf_str); #if APUSYS_SETTLE_TIME_TEST /* * upper bound (APUSYS_BUCK_NUM + 1) * is due to index of Vsram as -1. * And borrow div to be index of buck. */ for (div = 0; div < (APUSYS_BUCK_NUM + 1); div++) { if (apusys_opps.st[div].end) { snprintf(log_str, sizeof(log_str), "APUSYS_SETTLE_TIME_TEST buck_id:%d, total settle_time", div - 1); apu_profiling(&apusys_opps.st[div], log_str); } /* clear voltage timestamp for next round */ memset(&apusys_opps.st[div].end, 0, sizeof(u64)); /* * set freq timestamp as right now, * since there will be case that * F --> no change * V --> change * such as Vsram. */ apusys_opps.st[div].begin = sched_clock(); } #endif #ifdef APUPWR_TAG_TP trace_apupwr_dvfs(log_str); trace_apupwr_dvfs(logv_str); trace_apupwr_dvfs(logf_str); #endif // debug ring buffer LOG_DUMP("APUPWR DVFS %s\n", log_str); LOG_DUMP("APUPWR DVFS %s\n", logv_str); LOG_DUMP("APUPWR DVFS %s\n", logf_str); } void apusys_dvfs_state_machine(void) { /* check whether V needs to change before F changes*/ apusys_buck_up_check(); /* check whether F needs to change*/ apusys_frequency_check(); /* check whether V needs to change after F changes*/ apusys_buck_down_check(); } void apusys_dvfs_policy(uint64_t round_id) { uint8_t user; uint8_t opp; uint8_t use_opp; enum DVFS_VOLTAGE_DOMAIN buck_domain; enum DVFS_VOLTAGE voltage; uint8_t buck_domain_num = 0; uint8_t buck_index; uint8_t i = 0, j = 0; mutex_lock(&power_dvfs_mtx); apusys_opps.id = round_id; for (user = 0; user < APUSYS_DVFS_USER_NUM; user++) { apusys_opps.driver_opp_index[user] = apusys_opps.user_opp_index[user]; if (is_power_debug_lock == false) { if (dvfs_user_support(user) == false) continue; opp = apusys_opps.driver_opp_index[user]; buck_domain = apusys_user_to_buck_domain[user]; use_opp = apusys_pwr_max_min_check(user, opp); voltage = apusys_opps.opps[use_opp][buck_domain].voltage; apusys_clk_path_update_pwr(user, voltage); } } if (is_power_debug_lock == false) { apusys_final_volt_check(); apusys_pwr_constraint_check(); apusys_pwr_efficiency_check(); } apusys_dvfs_state_machine(); apusys_dvfs_info(); for (buck_domain_num = 0; buck_domain_num < APUSYS_BUCK_DOMAIN_NUM; buck_domain_num++) { if (dvfs_power_domain_support(buck_domain_num) == false) continue; apusys_opps.cur_opp_index[buck_domain_num] = apusys_opps.next_opp_index[buck_domain_num]; } for (buck_index = 0; buck_index < APUSYS_BUCK_NUM; buck_index++) { apusys_opps.cur_buck_volt[buck_index] = apusys_opps.next_buck_volt[buck_index]; apusys_opps.next_buck_volt[buck_index] = DVFS_VOLT_00_575000_V; } for (i = 0; i < APUSYS_DVFS_USER_NUM; i++) { for (j = 0; j < APUSYS_PATH_USER_NUM; j++) apusys_opps.user_path_volt[i][j] = DVFS_VOLT_00_575000_V; } mutex_unlock(&power_dvfs_mtx); } void apusys_set_opp(enum DVFS_USER user, uint8_t opp) { if (is_power_debug_lock == false) { if (apusys_opps.is_power_on[user] == true) { apusys_opps.user_opp_index[user] = opp; PWR_LOG_INF("%s, %s, user_opp=%d\n", __func__, user_str[user], opp); } } } #if SUPPORT_VCORE_TO_IPUIF void apusys_ipuif_opp_change(void) { int prev_ipuif = 0, next_ipuif = 0; //enum DVFS_USER user = MDLA0; // separate from VPU0 for vcore pm_qos mutex_lock(&power_dvfs_mtx); spin_lock(&ipuif_lock); if (apusys_opps.qos_apu_vcore != apusys_opps.driver_apu_vcore) { prev_ipuif = apusys_opps.driver_apu_vcore; next_ipuif = apusys_opps.qos_apu_vcore; apusys_opps.driver_apu_vcore = apusys_opps.qos_apu_vcore; } spin_unlock(&ipuif_lock); /* no change and leave */ if (!next_ipuif && !prev_ipuif) goto out; else PWR_LOG_INF("%s, qos_apu_vcore=%d, driver_apu_vcore=%d\n", __func__, next_ipuif, prev_ipuif); if (conn_mtcmos_on == 1) { /* raise freq */ if (next_ipuif > prev_ipuif) { config_vcore(MDLA0, (int)volt_to_vcore_opp(next_ipuif)); set_apu_clock_source( volt_to_ipuif_freq(next_ipuif), V_VCORE); } else { set_apu_clock_source( volt_to_ipuif_freq(next_ipuif), V_VCORE); config_vcore(MDLA0, (int)volt_to_vcore_opp( next_ipuif)); } } else { //26M setting in conn_mtcmos off //set_apu_clock_source(VCORE_OFF_FREQ, V_VCORE); //buck_control //config_vcore(user, volt_to_vcore_opp(VCORE_DEFAULT_VOLT)); } out: mutex_unlock(&power_dvfs_mtx); } void apusys_set_apu_vcore(int target_volt) { if (is_power_debug_lock == false) { if (conn_mtcmos_on == 1) { spin_lock(&ipuif_lock); apusys_opps.qos_apu_vcore = target_volt; spin_unlock(&ipuif_lock); PWR_LOG_INF("%s, qos_apu_vcore, target_volt=%d\n", __func__, target_volt); } } } #endif // this function will be called in DVFS thread and be protected by mutex lock bool apusys_check_opp_change(void) { static uint8_t prev_thermal_opp[APUSYS_DVFS_USER_NUM]; static bool prev_is_power_on[APUSYS_POWER_USER_NUM]; uint8_t user; if (is_power_debug_lock == true) return false; for (user = 0; user < APUSYS_DVFS_USER_NUM; user++) { if (dvfs_user_support(user) == false) continue; if (apusys_opps.user_opp_index[user] != apusys_opps.driver_opp_index[user]) { PWR_LOG_INF("%s DVFS since opp change\n", __func__); return true; } if (apusys_opps.is_power_on[user] != prev_is_power_on[user]) { prev_is_power_on[user] = apusys_opps.is_power_on[user]; PWR_LOG_INF("%s DVFS since power change\n", __func__); return true; } if (apusys_opps.thermal_opp[user] != prev_thermal_opp[user]) { prev_thermal_opp[user] = apusys_opps.thermal_opp[user]; PWR_LOG_INF("%s DVFS since thermal event\n", __func__); return true; } #if SUPPORT_VCORE_TO_IPUIF if (apusys_opps.driver_apu_vcore != apusys_opps.qos_apu_vcore) { PWR_LOG_INF("%s DVFS since qos change\n", __func__); return true; } #endif } return false; } int apusys_power_on(enum DVFS_USER user) { int ret = 0; struct hal_param_pwr_mask pwr_mask; enum DVFS_VOLTAGE_DOMAIN buck_domain; ret = hal_config_power(PWR_CMD_SET_BOOT_UP, user, (void *)&pwr_mask); if (ret == 0) { if (apusys_opps.power_bit_mask == 0) { // first power on PWR_LOG_INF("%s first power on\n", __func__); apusys_opps.cur_buck_volt[VPU_BUCK] = VVPU_DEFAULT_VOLT; apusys_opps.cur_buck_volt[MDLA_BUCK] = VMDLA_DEFAULT_VOLT; apusys_opps.cur_buck_volt[VCORE_BUCK] = VCORE_DEFAULT_VOLT; apusys_opps.vsram_volatge = VSRAM_DEFAULT_VOLT; apusys_opps.cur_opp_index[V_APU_CONN] = APUSYS_DEFAULT_OPP; apusys_opps.next_opp_index[V_APU_CONN] = APUSYS_DEFAULT_OPP; #if !defined(CONFIG_MACH_MT6873) && !defined(CONFIG_MACH_MT6853) apusys_opps.cur_opp_index[V_TOP_IOMMU] = APUSYS_DEFAULT_OPP; apusys_opps.next_opp_index[V_TOP_IOMMU] = APUSYS_DEFAULT_OPP; #endif } if (user < APUSYS_DVFS_USER_NUM) { buck_domain = apusys_user_to_buck_domain[user]; apusys_opps.cur_opp_index[buck_domain] = APUSYS_DEFAULT_OPP; apusys_opps.next_opp_index[buck_domain] = APUSYS_DEFAULT_OPP; } apusys_opps.is_power_on[user] = true; apusys_opps.power_bit_mask |= (1<