1223 lines
33 KiB
C
1223 lines
33 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef BUILD_POLICY_TEST
|
||
|
|
#include <linux/types.h>
|
||
|
|
#include <linux/delay.h>
|
||
|
|
#include <linux/platform_device.h>
|
||
|
|
#include <linux/seq_file.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include <linux/mutex.h>
|
||
|
|
#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<<user);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_power_debug_lock == true)
|
||
|
|
fix_dvfs_debug();
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int apusys_power_off(enum DVFS_USER user)
|
||
|
|
{
|
||
|
|
int id = 0; // dvfs user id
|
||
|
|
int ret = 0;
|
||
|
|
struct hal_param_pwr_mask pwr_mask;
|
||
|
|
enum DVFS_VOLTAGE_DOMAIN buck_domain;
|
||
|
|
|
||
|
|
mutex_lock(&power_dvfs_mtx);
|
||
|
|
apusys_opps.is_power_on[user] = false;
|
||
|
|
apusys_opps.power_bit_mask &= (~(1<<user));
|
||
|
|
|
||
|
|
ret = hal_config_power(PWR_CMD_SET_SHUT_DOWN, user, (void *)&pwr_mask);
|
||
|
|
|
||
|
|
if (ret == 0) {
|
||
|
|
/* PWR_CMD_SET_SHUT_DOWN pass */
|
||
|
|
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.user_opp_index[user] =
|
||
|
|
APUSYS_DEFAULT_OPP;
|
||
|
|
apusys_opps.driver_opp_index[user] =
|
||
|
|
APUSYS_DEFAULT_OPP;
|
||
|
|
}
|
||
|
|
if (apusys_opps.power_bit_mask == 0) {
|
||
|
|
PWR_LOG_INF("%s all power off\n", __func__);
|
||
|
|
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 SUPPORT_VCORE_TO_IPUIF
|
||
|
|
apusys_opps.driver_apu_vcore =
|
||
|
|
VCORE_SHUTDOWN_VOLT;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&power_dvfs_mtx);
|
||
|
|
if (is_power_debug_lock == false) {
|
||
|
|
for (id = 0 ; id < APUSYS_DVFS_USER_NUM ; id++) {
|
||
|
|
if (dvfs_user_support(id)
|
||
|
|
&& apusys_opps.is_power_on[id]) {
|
||
|
|
PWR_LOG_INF("%s trigger DVFS\n", __func__);
|
||
|
|
event_trigger_dvfs_policy();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else /* PWR_CMD_SET_SHUT_DOWN fail */
|
||
|
|
mutex_unlock(&power_dvfs_mtx);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int apusys_power_init(enum DVFS_USER user, void *init_power_data)
|
||
|
|
{
|
||
|
|
int i = 0, j = 0;
|
||
|
|
struct hal_param_seg_support seg_data = {0};
|
||
|
|
enum DVFS_VOLTAGE_DOMAIN domain;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
mutex_init(&power_dvfs_mtx);
|
||
|
|
spin_lock_init(&ipuif_lock);
|
||
|
|
ret = hal_config_power(PWR_CMD_SEGMENT_CHECK, VPU0, (void *)&seg_data);
|
||
|
|
if (ret)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
#if defined(CONFIG_MACH_MT6893)
|
||
|
|
if (seg_data.seg == SEGMENT_0)
|
||
|
|
apusys_opps.opps = dvfs_table_b0;
|
||
|
|
else
|
||
|
|
apusys_opps.opps = dvfs_table_b1;
|
||
|
|
#else
|
||
|
|
if (seg_data.seg == SEGMENT_0)
|
||
|
|
apusys_opps.opps = dvfs_table_0;
|
||
|
|
else if (seg_data.seg == SEGMENT_2)
|
||
|
|
apusys_opps.opps = dvfs_table_2;
|
||
|
|
else
|
||
|
|
apusys_opps.opps = dvfs_table_1;
|
||
|
|
#endif
|
||
|
|
ret = hal_config_power(PWR_CMD_BINNING_CHECK, VPU0, NULL);
|
||
|
|
if (ret)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
for (i = 0; i < APUSYS_DVFS_USER_NUM; i++) {
|
||
|
|
seg_data.user = i;
|
||
|
|
hal_config_power(PWR_CMD_SEGMENT_CHECK,
|
||
|
|
VPU0, (void *)&seg_data);
|
||
|
|
domain = apusys_user_to_buck_domain[i];
|
||
|
|
apusys_dvfs_user_support[i] = seg_data.support;
|
||
|
|
apusys_dvfs_buck_domain_support[domain] = seg_data.support;
|
||
|
|
if (dvfs_user_support(user) == false)
|
||
|
|
continue;
|
||
|
|
apusys_opps.thermal_opp[i] = 0;
|
||
|
|
apusys_opps.user_opp_index[i] = APUSYS_DEFAULT_OPP;
|
||
|
|
apusys_opps.driver_opp_index[i] = APUSYS_DEFAULT_OPP;
|
||
|
|
apusys_opps.power_lock_max_opp[i] = APUSYS_MAX_NUM_OPPS-1;
|
||
|
|
apusys_opps.power_lock_min_opp[i] = 0;
|
||
|
|
apusys_opps.is_power_on[i] = false;
|
||
|
|
for (j = 0; j < APUSYS_PATH_USER_NUM; j++)
|
||
|
|
apusys_opps.user_path_volt[i][j] =
|
||
|
|
DVFS_VOLT_00_575000_V;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < APUSYS_BUCK_DOMAIN_NUM; i++) {
|
||
|
|
apusys_opps.next_opp_index[i] = APUSYS_DEFAULT_OPP;
|
||
|
|
apusys_opps.cur_opp_index[i] = APUSYS_DEFAULT_OPP;
|
||
|
|
}
|
||
|
|
|
||
|
|
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.next_buck_volt[VPU_BUCK] = DVFS_VOLT_00_575000_V;
|
||
|
|
apusys_opps.next_buck_volt[MDLA_BUCK] = DVFS_VOLT_00_575000_V;
|
||
|
|
apusys_opps.next_buck_volt[VCORE_BUCK] = VCORE_DEFAULT_VOLT;
|
||
|
|
apusys_opps.vsram_volatge = VSRAM_DEFAULT_VOLT;
|
||
|
|
|
||
|
|
apusys_opps.power_bit_mask = 0;
|
||
|
|
|
||
|
|
ret = hal_config_power(PWR_CMD_INIT_POWER, user, init_power_data);
|
||
|
|
out:
|
||
|
|
PWR_LOG_INF("%s done ret %d\n", __func__, ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void apusys_power_uninit(enum DVFS_USER user)
|
||
|
|
{
|
||
|
|
hal_config_power(PWR_CMD_UNINIT_POWER, user, NULL);
|
||
|
|
PWR_LOG_INF("%s done,\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
enum DVFS_FREQ apusys_get_dvfs_freq(enum DVFS_VOLTAGE_DOMAIN domain)
|
||
|
|
{
|
||
|
|
return apusys_opps.opps[apusys_opps.cur_opp_index[domain]][domain].freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool apusys_get_power_on_status(enum DVFS_USER user)
|
||
|
|
{
|
||
|
|
return apusys_opps.is_power_on[user];
|
||
|
|
}
|