blob: 5aba40c2cf0affeaa7a1000de3aab5066675cde4 [file] [log] [blame]
/*
* Copyright (C) 2016 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/jiffies.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/input.h>
#include <linux/sched.h>
#include <linux/sched/rt.h>
#include <linux/kthread.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_address.h>
#endif
#include <linux/uaccess.h>
#include "mtk_gpufreq.h"
/*
#include "mtk_static_power.h"
*/
#if 0 /* pedro */
#include <mt-plat/upmu_common.h>
#include "mt-plat/sync_write.h"
#include "mt-plat/mtk_pmic_wrap.h"
#include "mt-plat/mtk_chip.h"
#include "mach/mtk_fhreg.h"
#include "mach/mtk_freqhopping.h"
/* TODO: check this! */
/* #include "mach/mt_static_power.h" */
#include "mach/mtk_thermal.h"
#include "mt8167/include/mach/upmu_sw.h"
#endif /* pedro */
#include <linux/regulator/consumer.h>
#define DRIVER_NOT_READY -1
/*
* CONFIG
*/
/**************************************************
* GPU DVFS input boost feature
***************************************************/
#define MT_GPUFREQ_INPUT_BOOST
/***************************
* Define for dynamic power table update
****************************/
#define MT_GPUFREQ_DYNAMIC_POWER_TABLE_UPDATE
/* there is no PBM feature in mt8167 */
#define DISABLE_PBM_FEATURE
#define VGPU_SET_BY_PMIC
/**************************************************
* Define register write function
***************************************************/
#define mt_gpufreq_reg_write(val, addr) mt_reg_sync_writel((val), ((void *)addr))
/***************************
* Operate Point Definition
****************************/
#define GPUOP(khz, volt, idx) \
{ \
.gpufreq_khz = khz, \
.gpufreq_volt = volt, \
.gpufreq_idx = idx, \
}
/**************************
* GPU DVFS OPP table setting
***************************/
#if 0 /* for E2 */
#define GPU_DVFS_FREQ0 (598000) /* KHz */
#define GPU_DVFS_FREQ1 (494000) /* KHz */
#define GPU_DVFS_FREQ2 (445000) /* KHz */
#define GPU_DVFS_FREQ3 (396500) /* KHz */
#define GPU_DVFS_FREQ4 (299000) /* KHz */
#define GPU_DVFS_FREQ5 (253500) /* KHz */
#else /* for E1 */
#define GPU_DVFS_FREQ0_e1 (574000) /* KHz */
#define GPU_DVFS_FREQ0 (598000) /* KHz */
#define GPU_DVFS_FREQ1_e1 (468000) /* KHz */
#define GPU_DVFS_FREQ1 (500500) /* KHz */
#define GPU_DVFS_FREQ2 (430000) /* KHz */
#define GPU_DVFS_FREQ3_e1 (390000) /* KHz */
#define GPU_DVFS_FREQ3 (390000) /* KHz */
#define GPU_DVFS_FREQ4 (299000) /* KHz */
#define GPU_DVFS_FREQ5 (253500) /* KHz */
#endif
#define GPUFREQ_LAST_FREQ_LEVEL (GPU_DVFS_FREQ5)
#define GPU_DVFS_VOLT0 (130000) /* mV x 100 */
#define GPU_DVFS_VOLT1 (125000) /* mV x 100 */
#define GPU_DVFS_VOLT2 (115000) /* mV x 100 */
#define GPU_DVFS_PTPOD_DISABLE_VOLT GPU_DVFS_VOLT2
#define UNIVPLL_FREQ GPU_DVFS_FREQ3 /* KHz */
/*****************************************
* PMIC settle time (us), should not be changed
******************************************/
#ifdef VGPU_SET_BY_PMIC
#define PMIC_MIN_VGPU GPU_DVFS_VOLT2
#define PMIC_MAX_VGPU GPU_DVFS_VOLT0
/* mt6799 us * 100 BEGIN */
#define DELAY_FACTOR 1250
#define HALF_DELAY_FACTOR 625
#define BUCK_INIT_US 750
/* mt6799 END */
#define PMIC_CMD_DELAY_TIME 5
#define MIN_PMIC_SETTLE_TIME 25
#define PMIC_VOLT_UP_SETTLE_TIME(old_volt, new_volt) \
(((((new_volt) - (old_volt)) + 1250 - 1) / 1250) + PMIC_CMD_DELAY_TIME)
#define PMIC_VOLT_DOWN_SETTLE_TIME(old_volt, new_volt) \
(((((old_volt) - (new_volt)) * 2) / 625) + PMIC_CMD_DELAY_TIME)
#define PMIC_VOLT_ON_OFF_DELAY_US (200)
#define INVALID_SLEW_RATE (0)
/* #define GPU_DVFS_PMIC_SETTLE_TIME (40) // us */
/* register val -> mV */
#define GPU_VOLT_TO_MV(volt) ((((volt)*625)/100+700)*100)
#define PMIC_BUCK_VGPU_VOSEL_ON MT6351_PMIC_BUCK_VGPU_VOSEL_ON
#define PMIC_ADDR_VGPU_VOSEL_ON MT6351_PMIC_BUCK_VGPU_VOSEL_ON_ADDR
#define PMIC_ADDR_VGPU_VOSEL_ON_MASK MT6351_PMIC_BUCK_VGPU_VOSEL_ON_MASK
#define PMIC_ADDR_VGPU_VOSEL_ON_SHIFT MT6351_PMIC_BUCK_VGPU_VOSEL_ON_SHIFT
#define PMIC_ADDR_VGPU_VOSEL_CTRL MT6351_PMIC_BUCK_VGPU_VOSEL_CTRL_ADDR
#define PMIC_ADDR_VGPU_VOSEL_CTRL_MASK MT6351_PMIC_BUCK_VGPU_VOSEL_CTRL_MASK
#define PMIC_ADDR_VGPU_VOSEL_CTRL_SHIFT MT6351_PMIC_BUCK_VGPU_VOSEL_CTRL_SHIFT
#define PMIC_ADDR_VGPU_EN MT6351_PMIC_BUCK_VGPU_EN_ADDR
#define PMIC_ADDR_VGPU_EN_MASK MT6351_PMIC_BUCK_VGPU_EN_MASK
#define PMIC_ADDR_VGPU_EN_SHIFT MT6351_PMIC_BUCK_VGPU_EN_SHIFT
#define PMIC_ADDR_VGPU_EN_CTRL MT6351_PMIC_BUCK_VGPU_EN_CTRL_ADDR
#define PMIC_ADDR_VGPU_EN_CTRL_MASK MT6351_PMIC_BUCK_VGPU_EN_CTRL_MASK
#define PMIC_ADDR_VGPU_EN_CTRL_SHIFT MT6351_PMIC_BUCK_VGPU_EN_CTRL_SHIFT
#elif defined(VGPU_SET_BY_EXTIC)
#define GPU_LDO_BASE 0x10001000
#define EXTIC_VSEL0 0x0 /* [0] */
#define EXTIC_BUCK_EN0_MASK 0x1
#define EXTIC_BUCK_EN0_SHIFT 0x7
#define EXTIC_VSEL1 0x1 /* [0] */
#define EXTIC_BUCK_EN1_MASK 0x1
#define EXTIC_BUCK_EN1_SHIFT 0x7
#define EXTIC_VGPU_CTRL 0x2
#define EXTIC_VGPU_SLEW_MASK 0x7
#define EXTIC_VGPU_SLEW_SHIFT 0x4
#define EXTIC_VOLT_ON_OFF_DELAY_US 350
#define EXTIC_VOLT_STEP 12826 /* 12.826mV per step */
#define EXTIC_SLEW_STEP 100 /* 10.000mV per step */
#define EXTIC_VOLT_UP_SETTLE_TIME(old_volt, new_volt, slew_rate) \
(((((new_volt) - (old_volt)) * EXTIC_SLEW_STEP) / EXTIC_VOLT_STEP) / (slew_rate)) /* us */
#define EXTIC_VOLT_DOWN_SETTLE_TIME(old_volt, new_volt, slew_rate) \
(((((old_volt) - (new_volt)) * EXTIC_SLEW_STEP) / EXTIC_VOLT_STEP) / (slew_rate)) /* us */
#endif
/* efuse */
#define GPUFREQ_EFUSE_INDEX (4)
#define EFUSE_MFG_SPD_BOND_SHIFT (22)
#define EFUSE_MFG_SPD_BOND_MASK (0x3)
/*
* LOG
*/
#if 1
#define TAG "[Power/gpufreq] "
#define gpufreq_err(fmt, args...) \
pr_err(fmt, ##args)
#define gpufreq_warn(fmt, args...) \
pr_warn(fmt, ##args)
#define gpufreq_info(fmt, args...) \
pr_debug(fmt, ##args)
#define gpufreq_dbg(fmt, args...) \
pr_debug(fmt, ##args)
#define gpufreq_ver(fmt, args...) \
pr_debug(fmt, ##args)
#else
#define gpufreq_err(fmt, args...)
#define gpufreq_warn(fmt, args...)
#define gpufreq_info(fmt, args...)
#define gpufreq_dbg(fmt, args...)
#define gpufreq_ver(fmt, args...)
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct early_suspend mt_gpufreq_early_suspend_handler = {
.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 200,
.suspend = NULL,
.resume = NULL,
};
#endif
static sampler_func g_pFreqSampler;
static sampler_func g_pVoltSampler;
static gpufreq_power_limit_notify g_pGpufreq_power_limit_notify;
#ifdef MT_GPUFREQ_INPUT_BOOST
static gpufreq_input_boost_notify g_pGpufreq_input_boost_notify;
#endif
static gpufreq_ptpod_update_notify g_pGpufreq_ptpod_update_notify;
/***************************
* GPU DVFS OPP Table
****************************/
/* Full Yield */
static struct mt_gpufreq_table_info mt_gpufreq_opp_tbl_e1_0[] = {
/* GPUOP(GPU_DVFS_FREQ0, GPU_DVFS_VOLT0, 0), */
/* GPUOP(GPU_DVFS_FREQ1, GPU_DVFS_VOLT1, 1), */
/* GPUOP(GPU_DVFS_FREQ2, GPU_DVFS_VOLT1, 2), */
GPUOP(GPU_DVFS_FREQ3, GPU_DVFS_VOLT2, 3),
GPUOP(GPU_DVFS_FREQ4, GPU_DVFS_VOLT2, 4),
GPUOP(GPU_DVFS_FREQ5, GPU_DVFS_VOLT2, 5),
};
static struct mt_gpufreq_table_info mt_gpufreq_opp_tbl_e1_1[] = {
/* GPUOP(GPU_DVFS_FREQ0, GPU_DVFS_VOLT0, 0), */
GPUOP(GPU_DVFS_FREQ1, GPU_DVFS_VOLT1, 1),
/* GPUOP(GPU_DVFS_FREQ2, GPU_DVFS_VOLT1, 2), */
GPUOP(GPU_DVFS_FREQ3, GPU_DVFS_VOLT2, 3),
GPUOP(GPU_DVFS_FREQ4, GPU_DVFS_VOLT2, 4),
GPUOP(GPU_DVFS_FREQ5, GPU_DVFS_VOLT2, 5),
};
static struct mt_gpufreq_table_info mt_gpufreq_opp_tbl_e1_2[] = {
GPUOP(GPU_DVFS_FREQ0, GPU_DVFS_VOLT0, 0),
GPUOP(GPU_DVFS_FREQ1, GPU_DVFS_VOLT1, 1),
/* GPUOP(GPU_DVFS_FREQ2, GPU_DVFS_VOLT1, 2), */
GPUOP(GPU_DVFS_FREQ3, GPU_DVFS_VOLT2, 3),
/* GPUOP(GPU_DVFS_FREQ4, GPU_DVFS_VOLT2, 4), */
GPUOP(GPU_DVFS_FREQ5, GPU_DVFS_VOLT2, 5),
};
/**************************
* PTPOD enable/disable GPU power doamin
***************************/
static gpufreq_mfgclock_notify g_pGpufreq_mfgclock_enable_notify;
static gpufreq_mfgclock_notify g_pGpufreq_mfgclock_disable_notify;
/*
* AEE (SRAM debug)
*/
#ifdef MT_GPUFREQ_AEE_RR_REC
enum gpu_dvfs_state {
GPU_DVFS_IS_DOING_DVFS = 0,
GPU_DVFS_IS_VGPU_ENABLED,
};
static void _mt_gpufreq_aee_init(void)
{
aee_rr_rec_gpu_dvfs_vgpu(0xFF);
aee_rr_rec_gpu_dvfs_oppidx(0xFF);
aee_rr_rec_gpu_dvfs_status(0xFC);
}
#endif
/**************************
* enable GPU DVFS count
***************************/
static int g_gpufreq_dvfs_disable_count;
static unsigned int g_cur_gpu_freq = GPU_DVFS_FREQ5; /* initial value, 396.5MHz */
static unsigned int g_cur_gpu_volt = GPU_DVFS_VOLT2; /* initial value, 1.15v */
static unsigned int g_cur_gpu_idx = 0xFF;
static unsigned int g_cur_gpu_OPPidx = 0xFF;
static unsigned int g_cur_freq_init_keep;
static bool mt_gpufreq_ready;
/* In default settiing, freq_table[0] is max frequency, freq_table[num-1] is min frequency,*/
static unsigned int g_gpufreq_max_id;
/* If not limited, it should be set to freq_table[0] (MAX frequency) */
static unsigned int g_limited_max_id;
static unsigned int g_limited_min_id;
static bool mt_gpufreq_debug;
static bool mt_gpufreq_pause = true;
static bool mt_gpufreq_keep_max_frequency_state;
static bool mt_gpufreq_keep_opp_frequency_state;
#if 1
static unsigned int mt_gpufreq_keep_opp_frequency;
#endif
static unsigned int mt_gpufreq_keep_opp_index;
static bool mt_gpufreq_fixed_freq_volt_state;
static unsigned int mt_gpufreq_fixed_frequency;
static unsigned int mt_gpufreq_fixed_voltage;
#if 1
static unsigned int mt_gpufreq_volt_enable;
#endif
static unsigned int mt_gpufreq_volt_enable_state;
#ifdef MT_GPUFREQ_INPUT_BOOST
static unsigned int mt_gpufreq_input_boost_state = 1;
#endif
/* static bool g_limited_power_ignore_state = false; */
static bool g_limited_thermal_ignore_state;
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
static bool g_limited_low_batt_volt_ignore_state;
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
static bool g_limited_low_batt_volume_ignore_state;
#endif
#ifdef MT_GPUFREQ_OC_PROTECT
static bool g_limited_oc_ignore_state;
#endif
static bool mt_gpufreq_opp_max_frequency_state;
static unsigned int mt_gpufreq_opp_max_frequency;
static unsigned int mt_gpufreq_opp_max_index;
static unsigned int mt_gpufreq_dvfs_table_type;
/* static DEFINE_SPINLOCK(mt_gpufreq_lock); */
static DEFINE_MUTEX(mt_gpufreq_lock);
static DEFINE_MUTEX(mt_gpufreq_power_lock);
static unsigned int mt_gpufreqs_num;
static struct mt_gpufreq_table_info *mt_gpufreqs;
static struct mt_gpufreq_table_info *mt_gpufreqs_default;
static struct mt_gpufreq_power_table_info *mt_gpufreqs_power;
static struct mtk_gpu_power_info *mt_gpufreqs_power_info;
static struct mt_gpufreq_pmic_t *mt_gpufreq_pmic;
/* static struct mt_gpufreq_power_table_info *mt_gpufreqs_default_power; */
static bool mt_gpufreq_ptpod_disable;
static int mt_gpufreq_ptpod_disable_idx;
static void mt_gpufreq_clock_switch(unsigned int freq_new);
static void mt_gpufreq_volt_switch(unsigned int volt_old, unsigned int volt_new);
static void mt_gpufreq_set(unsigned int freq_old, unsigned int freq_new,
unsigned int volt_old, unsigned int volt_new);
static unsigned int _mt_gpufreq_get_cur_volt(void);
static unsigned int _mt_gpufreq_get_cur_freq(void);
static void _mt_gpufreq_kick_pbm(int enable);
#ifndef DISABLE_PBM_FEATURE
static bool g_limited_pbm_ignore_state;
static unsigned int mt_gpufreq_pbm_limited_gpu_power; /* PBM limit power */
static unsigned int mt_gpufreq_pbm_limited_index; /* Limited frequency index for PBM */
#define GPU_OFF_SETTLE_TIME_MS (100)
struct delayed_work notify_pbm_gpuoff_work;
#endif
#if 0 // pedro
int __attribute__ ((weak))
get_immediate_gpu_wrap(void)
{
pr_err("get_immediate_gpu_wrap doesn't exist");
return 0;
}
#endif // pedro
/*************************************************************************************
* Check GPU DVFS Efuse
**************************************************************************************/
static unsigned int mt_gpufreq_get_dvfs_table_type(void)
{
unsigned int gpu_speed_bounding = 0;
unsigned int type = 0;
// if (mt_get_chip_sw_ver() == 0) { /* E1 */
//
{ // pedro, assume E1
mt_gpufreq_opp_tbl_e1_0[0].gpufreq_khz = GPU_DVFS_FREQ3_e1;
mt_gpufreq_opp_tbl_e1_1[0].gpufreq_khz = GPU_DVFS_FREQ1_e1;
mt_gpufreq_opp_tbl_e1_1[1].gpufreq_khz = GPU_DVFS_FREQ3_e1;
mt_gpufreq_opp_tbl_e1_2[0].gpufreq_khz = GPU_DVFS_FREQ0_e1;
mt_gpufreq_opp_tbl_e1_2[1].gpufreq_khz = GPU_DVFS_FREQ1_e1;
mt_gpufreq_opp_tbl_e1_2[2].gpufreq_khz = GPU_DVFS_FREQ3_e1;
}
/* Read the gpu efuse data*/
gpu_speed_bounding = get_devinfo_with_index(4);
gpufreq_info("GPU other bounding from efuse = %x\n", gpu_speed_bounding);
gpu_speed_bounding = (get_devinfo_with_index(GPUFREQ_EFUSE_INDEX) >>
EFUSE_MFG_SPD_BOND_SHIFT) & EFUSE_MFG_SPD_BOND_MASK;
gpufreq_info("GPU frequency bounding from efuse = %x\n", gpu_speed_bounding);
/* No efuse or free run? use clock-frequency from device tree to determine GPU table type! */
if (gpu_speed_bounding == 0) {
#ifdef CONFIG_OF
static const struct of_device_id gpu_ids[] = {
{.compatible = "mediatek,mt8167-clark"},
{ /* sentinel */ }
};
struct device_node *node;
unsigned int gpu_speed = 0;
node = of_find_matching_node(NULL, gpu_ids);
if (!node) {
gpufreq_err("@%s: find GPU node failed\n", __func__);
gpu_speed = 500000; /* default speed */
} else {
if (!of_property_read_u32(node, "clock-frequency", &gpu_speed)) {
gpu_speed = gpu_speed / 1000; /* KHz */
} else {
gpufreq_err
("@%s: missing clock-frequency property, use default GPU level\n",
__func__);
gpu_speed = 500000; /* default speed */
}
}
gpufreq_info("GPU clock-frequency from DT = %d MHz\n", gpu_speed);
if (gpu_speed >= GPU_DVFS_FREQ0)
type = 2; /* 600M */
else if (gpu_speed >= GPU_DVFS_FREQ1)
type = 1; /* 500M */
#else
gpufreq_err("@%s: Cannot get GPU speed from DT!\n", __func__);
type = 0;
#endif
return type;
}
switch (gpu_speed_bounding) {
case 0:
type = 0; /* free run */
break;
case 1:
type = 2; /* 600M */
break;
case 2:
type = 1; /* 500M */
break;
case 3:
default:
type = 0; /* 400M */
}
return type;
}
#ifdef MT_GPUFREQ_INPUT_BOOST
static struct task_struct *mt_gpufreq_up_task;
static void mt_gpufreq_clock_switch(unsigned int freq_new);
static int mt_gpufreq_input_boost_task(void *data)
{
while (1) {
gpufreq_dbg("@%s: begin\n", __func__);
if (g_pGpufreq_input_boost_notify != NULL) {
gpufreq_dbg("@%s: g_pGpufreq_input_boost_notify\n", __func__);
g_pGpufreq_input_boost_notify(g_gpufreq_max_id);
}
gpufreq_dbg("@%s: end\n", __func__);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
if (kthread_should_stop())
break;
}
return 0;
}
/*************************************************************************************
* Input boost
**************************************************************************************/
static void mt_gpufreq_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
if ((type == EV_KEY) && (code == BTN_TOUCH) && (value == 1)
&& (mt_gpufreq_input_boost_state == 1)) {
gpufreq_dbg("@%s: accept.\n", __func__);
wake_up_process(mt_gpufreq_up_task);
}
}
static int mt_gpufreq_input_connect(struct input_handler *handler,
struct input_dev *dev, const struct input_device_id *id)
{
struct input_handle *handle;
int error;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = "gpufreq_ib";
error = input_register_handle(handle);
if (error)
goto err2;
error = input_open_device(handle);
if (error)
goto err1;
return 0;
err1:
input_unregister_handle(handle);
err2:
kfree(handle);
return error;
}
static void mt_gpufreq_input_disconnect(struct input_handle *handle)
{
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
static const struct input_device_id mt_gpufreq_ids[] = {
{.driver_info = 1},
{},
};
static struct input_handler mt_gpufreq_input_handler = {
.event = mt_gpufreq_input_event,
.connect = mt_gpufreq_input_connect,
.disconnect = mt_gpufreq_input_disconnect,
.name = "gpufreq_ib",
.id_table = mt_gpufreq_ids,
};
#endif
/*
* Power table calculation
*/
static void mt_gpufreq_power_calculation(unsigned int idx, unsigned int freq,
unsigned int volt, unsigned int temp)
{
#define GPU_ACT_REF_POWER 845 /* mW */
#define GPU_ACT_REF_FREQ 850000 /* KHz */
#define GPU_ACT_REF_VOLT 90000 /* mV x 100 */
unsigned int p_total = 0, p_dynamic = 0, ref_freq = 0, ref_volt = 0;
int p_leakage = 0;
p_dynamic = GPU_ACT_REF_POWER;
ref_freq = GPU_ACT_REF_FREQ;
ref_volt = GPU_ACT_REF_VOLT;
p_dynamic = p_dynamic *
((freq * 100) / ref_freq) *
((volt * 100) / ref_volt) * ((volt * 100) / ref_volt) / (100 * 100 * 100);
#ifdef STATIC_PWR_READY2USE
p_leakage =
mt_spower_get_leakage(MT_SPOWER_GPU, (volt / 100), temp);
if (!mt_gpufreq_volt_enable_state || p_leakage < 0)
p_leakage = 0;
#else
p_leakage = 71;
#endif
p_total = p_dynamic + p_leakage;
gpufreq_ver("%d: p_dynamic = %d, p_leakage = %d, p_total = %d, temp = %d\n",
idx, p_dynamic, p_leakage, p_total, temp);
mt_gpufreqs_power[idx].gpufreq_power = p_total;
}
/**************************************
* Random seed generated for test
***************************************/
#ifdef MT_GPU_DVFS_RANDOM_TEST
static int mt_gpufreq_idx_get(int num)
{
int random = 0, mult = 0, idx;
random = jiffies & 0xF;
while (1) {
if ((mult * num) >= random) {
idx = (mult * num) - random;
break;
}
mult++;
}
return idx;
}
#endif
/* Set frequency and voltage at driver probe function */
static void mt_gpufreq_set_initial(void)
{
unsigned int cur_volt = 0, cur_freq = 0;
int i = 0;
mutex_lock(&mt_gpufreq_lock);
#ifdef MT_GPUFREQ_AEE_RR_REC
aee_rr_rec_gpu_dvfs_status(aee_rr_curr_gpu_dvfs_status() | (1 << GPU_DVFS_IS_DOING_DVFS));
#endif
cur_volt = _mt_gpufreq_get_cur_volt();
cur_freq = _mt_gpufreq_get_cur_freq();
// for (i = 0; i < mt_gpufreqs_num; i++) {
// if (cur_volt >= mt_gpufreqs[i].gpufreq_volt) {
mt_gpufreq_set(cur_freq, mt_gpufreqs[i].gpufreq_khz,
cur_volt, mt_gpufreqs[i].gpufreq_volt);
g_cur_gpu_OPPidx = i;
gpufreq_dbg("init_idx = %d\n", g_cur_gpu_OPPidx);
_mt_gpufreq_kick_pbm(1);
// break;
// }
// }
/* Not found, set to LPM */
if (i == mt_gpufreqs_num) {
gpufreq_err
("Set to LPM since GPU idx not found according to current Vcore = %d mV\n",
cur_volt / 100);
g_cur_gpu_OPPidx = mt_gpufreqs_num - 1;
gpufreq_err
("mt_gpufreq_set_initial freq index = %d mV\n", g_cur_gpu_OPPidx);
mt_gpufreq_set(cur_freq, mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_khz,
cur_volt, mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt);
}
g_cur_gpu_freq = mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_khz;
g_cur_gpu_volt = mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt;
g_cur_gpu_idx = mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_idx;
#ifdef MT_GPUFREQ_AEE_RR_REC
aee_rr_rec_gpu_dvfs_oppidx(g_cur_gpu_OPPidx);
aee_rr_rec_gpu_dvfs_status(aee_rr_curr_gpu_dvfs_status() & ~(1 << GPU_DVFS_IS_DOING_DVFS));
#endif
mutex_unlock(&mt_gpufreq_lock);
}
#ifndef DISABLE_PBM_FEATURE
static void mt_gpufreq_notify_pbm_gpuoff(struct work_struct *work)
{
mutex_lock(&mt_gpufreq_lock);
if (!mt_gpufreq_volt_enable_state)
_mt_gpufreq_kick_pbm(0);
mutex_unlock(&mt_gpufreq_lock);
}
#endif
/* Set VGPU enable/disable when GPU clock be switched on/off */
unsigned int mt_gpufreq_voltage_enable_set(unsigned int enable)
{
/* mt8167, gpu uses vcore, the enable/disable is always on*/
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return DRIVER_NOT_READY;
}
if (mt_gpufreq_ptpod_disable == true) {
if (enable == 0) {
gpufreq_info("mt_gpufreq_ptpod_disable == true\n");
return DRIVER_NOT_READY;
}
}
mt_gpufreq_volt_enable_state = enable;
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_voltage_enable_set);
/************************************************
* DVFS enable API for PTPOD
*************************************************/
void mt_gpufreq_enable_by_ptpod(void)
{
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
mt_gpufreq_voltage_enable_set(0);
#ifdef MTK_GPU_SPM
if (mt_gpufreq_ptpod_disable)
mtk_gpu_spm_resume();
#endif
mt_gpufreq_ptpod_disable = false;
gpufreq_info("mt_gpufreq enabled by ptpod\n");
if (g_pGpufreq_mfgclock_disable_notify)
g_pGpufreq_mfgclock_disable_notify();
else
pr_err("mt_gpufreq_enable_by_ptpod: no callback!\n");
/* pmic auto mode: the variance of voltage is wide but saves more power. */
regulator_set_mode(mt_gpufreq_pmic->reg_vgpu, REGULATOR_MODE_NORMAL);
if (regulator_get_mode(mt_gpufreq_pmic->reg_vgpu) != REGULATOR_MODE_NORMAL)
pr_err("Vgpu should be REGULATOR_MODE_NORMAL(%d), but mode = %d\n",
REGULATOR_MODE_NORMAL, regulator_get_mode(mt_gpufreq_pmic->reg_vgpu));
}
EXPORT_SYMBOL(mt_gpufreq_enable_by_ptpod);
/************************************************
* DVFS disable API for PTPOD
*************************************************/
void mt_gpufreq_disable_by_ptpod(void)
{
int i = 0, target_idx = 0;
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
#ifdef MTK_GPU_SPM
mtk_gpu_spm_pause();
g_cur_gpu_volt = _mt_gpufreq_get_cur_volt();
g_cur_gpu_freq = _mt_gpufreq_get_cur_freq();
#endif
mt_gpufreq_ptpod_disable = true;
gpufreq_info("mt_gpufreq disabled by ptpod\n");
for (i = 0; i < mt_gpufreqs_num; i++) {
/* VBoot = 0.85v for PTPOD */
target_idx = i;
if (mt_gpufreqs_default[i].gpufreq_volt <= GPU_DVFS_PTPOD_DISABLE_VOLT)
break;
}
if (g_pGpufreq_mfgclock_enable_notify)
g_pGpufreq_mfgclock_enable_notify();
else
pr_err("mt_gpufreq_disable_by_ptpod: no callback!\n");
/* pmic PWM mode: the variance of voltage is narrow but consumes more power. */
regulator_set_mode(mt_gpufreq_pmic->reg_vgpu, REGULATOR_MODE_FAST);
if (regulator_get_mode(mt_gpufreq_pmic->reg_vgpu) != REGULATOR_MODE_FAST)
pr_err("Vgpu should be REGULATOR_MODE_FAST(%d), but mode = %d\n",
REGULATOR_MODE_FAST, regulator_get_mode(mt_gpufreq_pmic->reg_vgpu));
mt_gpufreq_ptpod_disable_idx = target_idx;
mt_gpufreq_voltage_enable_set(1);
mt_gpufreq_target(target_idx);
}
EXPORT_SYMBOL(mt_gpufreq_disable_by_ptpod);
bool mt_gpufreq_IsPowerOn(void)
{
return (mt_gpufreq_volt_enable_state == 1);
}
EXPORT_SYMBOL(mt_gpufreq_IsPowerOn);
/************************************************
* API to switch back default voltage setting for GPU PTPOD disabled
*************************************************/
void mt_gpufreq_restore_default_volt(void)
{
int i;
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
mutex_lock(&mt_gpufreq_lock);
for (i = 0; i < mt_gpufreqs_num; i++) {
mt_gpufreqs[i].gpufreq_volt = mt_gpufreqs_default[i].gpufreq_volt;
gpufreq_dbg("@%s: mt_gpufreqs[%d].gpufreq_volt = %x\n", __func__, i,
mt_gpufreqs[i].gpufreq_volt);
}
#ifndef MTK_GPU_SPM
mt_gpufreq_volt_switch(g_cur_gpu_volt, mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt);
#endif
g_cur_gpu_volt = mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt;
mutex_unlock(&mt_gpufreq_lock);
}
EXPORT_SYMBOL(mt_gpufreq_restore_default_volt);
/* Set voltage because PTP-OD modified voltage table by PMIC wrapper */
unsigned int mt_gpufreq_update_volt(unsigned int pmic_volt[], unsigned int array_size)
{
int i; /* , idx; */
/* unsigned long flags; */
unsigned volt = 0;
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return DRIVER_NOT_READY;
}
if (array_size > mt_gpufreqs_num) {
gpufreq_err("mt_gpufreq_update_volt: array_size = %d, Over-Boundary!\n", array_size);
return 0; /*-ENOSYS;*/
}
mutex_lock(&mt_gpufreq_lock);
for (i = 0; i < array_size; i++) {
volt = GPU_VOLT_TO_MV(pmic_volt[i]);
if ((volt > 95000) && (volt < 130000)) { /* between 950mv~1300mv */
mt_gpufreqs[i].gpufreq_volt = volt;
gpufreq_dbg("@%s: mt_gpufreqs[%d].gpufreq_volt = %x\n", __func__, i,
mt_gpufreqs[i].gpufreq_volt);
} else {
gpufreq_err("@%s: index[%d]._volt = %x Over-Boundary\n", __func__, i, volt);
}
}
#ifndef MTK_GPU_SPM
mt_gpufreq_volt_switch(g_cur_gpu_volt, mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt);
#endif
g_cur_gpu_volt = mt_gpufreqs[g_cur_gpu_OPPidx].gpufreq_volt;
if (g_pGpufreq_ptpod_update_notify != NULL)
g_pGpufreq_ptpod_update_notify();
mutex_unlock(&mt_gpufreq_lock);
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_update_volt);
unsigned int mt_gpufreq_get_dvfs_table_num(void)
{
return mt_gpufreqs_num;
}
EXPORT_SYMBOL(mt_gpufreq_get_dvfs_table_num);
unsigned int mt_gpufreq_get_freq_by_idx(unsigned int idx)
{
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return DRIVER_NOT_READY;
}
if (idx < mt_gpufreqs_num) {
gpufreq_dbg("@%s: idx = %d, frequency= %d\n", __func__, idx,
mt_gpufreqs[idx].gpufreq_khz);
return mt_gpufreqs[idx].gpufreq_khz;
}
gpufreq_dbg("@%s: idx = %d, NOT found! return 0!\n", __func__, idx);
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_get_freq_by_idx);
unsigned int mt_gpufreq_get_volt_by_idx(unsigned int idx)
{
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return DRIVER_NOT_READY;
}
if (idx < mt_gpufreqs_num) {
gpufreq_dbg("@%s: idx = %d, voltage= %d\n", __func__, idx,
mt_gpufreqs[idx].gpufreq_volt);
return mt_gpufreqs[idx].gpufreq_volt;
}
gpufreq_dbg("@%s: idx = %d, NOT found! return 0!\n", __func__, idx);
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_get_volt_by_idx);
#if 0 /* pedro */
#ifdef MT_GPUFREQ_DYNAMIC_POWER_TABLE_UPDATE
static void mt_update_gpufreqs_power_table(void)
{
int i = 0, temp = 0;
unsigned int freq = 0, volt = 0;
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready\n", __func__);
return;
}
#ifdef CONFIG_THERMAL
temp = get_immediate_gpu_wrap() / 1000;
#else
temp = 40;
#endif
gpufreq_dbg("@%s, temp = %d\n", __func__, temp);
mutex_lock(&mt_gpufreq_lock);
if ((temp >= -20) && (temp <= 125)) {
for (i = 0; i < mt_gpufreqs_num; i++) {
freq = mt_gpufreqs_power[i].gpufreq_khz;
volt = mt_gpufreqs_power[i].gpufreq_volt;
mt_gpufreq_power_calculation(i, freq, volt, temp);
gpufreq_ver("update mt_gpufreqs_power[%d].gpufreq_khz = %d\n", i,
mt_gpufreqs_power[i].gpufreq_khz);
gpufreq_ver("update mt_gpufreqs_power[%d].gpufreq_volt = %d\n", i,
mt_gpufreqs_power[i].gpufreq_volt);
gpufreq_ver("update mt_gpufreqs_power[%d].gpufreq_power = %d\n", i,
mt_gpufreqs_power[i].gpufreq_power);
}
} else
gpufreq_err("@%s: temp < 0 or temp > 125, NOT update power table!\n", __func__);
mutex_unlock(&mt_gpufreq_lock);
}
#endif
#endif /* pedro */
static void mt_setup_gpufreqs_power_table(int num)
{
int i = 0, temp = 0;
mt_gpufreqs_power = kzalloc((num) * sizeof(struct mt_gpufreq_power_table_info), GFP_KERNEL);
mt_gpufreqs_power_info = kzalloc((num) * sizeof(struct mtk_gpu_power_info), GFP_KERNEL);
if (mt_gpufreqs_power == NULL)
return;
#ifdef CONFIG_THERMAL
temp = get_immediate_gpu_wrap() / 1000;
#else
temp = 40;
#endif
gpufreq_dbg("@%s: temp = %d\n", __func__, temp);
if ((temp < -20) || (temp > 125)) {
gpufreq_dbg("@%s: temp < 0 or temp > 125!\n", __func__);
temp = 65;
}
for (i = 0; i < num; i++) {
/* fill-in freq and volt in power table */
mt_gpufreqs_power[i].gpufreq_khz = mt_gpufreqs[i].gpufreq_khz;
mt_gpufreqs_power[i].gpufreq_volt = mt_gpufreqs[i].gpufreq_volt;
/* CJ, Fix? need the mt_gpufreq_power_calculation?*/
mt_gpufreq_power_calculation(i,
mt_gpufreqs_power[i].gpufreq_khz,
mt_gpufreqs_power[i].gpufreq_volt,
temp);
mt_gpufreqs_power_info[i].gpufreq_khz = mt_gpufreqs_power[i].gpufreq_khz;
mt_gpufreqs_power_info[i].gpufreq_power = mt_gpufreqs_power[i].gpufreq_power;
gpufreq_info("mt_gpufreqs_power[%d].gpufreq_khz = %u\n", i,
mt_gpufreqs_power[i].gpufreq_khz);
gpufreq_info("mt_gpufreqs_power[%d].gpufreq_volt = %u\n", i,
mt_gpufreqs_power[i].gpufreq_volt);
gpufreq_info("mt_gpufreqs_power[%d].gpufreq_power = %u\n", i,
mt_gpufreqs_power[i].gpufreq_power);
}
#ifdef CONFIG_THERMAL
mtk_gpufreq_register(mt_gpufreqs_power_info, num);
#endif
}
/***********************************************
* register frequency table to gpufreq subsystem
************************************************/
static int mt_setup_gpufreqs_table(struct mt_gpufreq_table_info *freqs, int num)
{
int i = 0;
mt_gpufreqs = kzalloc((num) * sizeof(*freqs), GFP_KERNEL);
mt_gpufreqs_default = kzalloc((num) * sizeof(*freqs), GFP_KERNEL);
if ((mt_gpufreqs == NULL) || (mt_gpufreqs_default == NULL))
return -ENOMEM;
for (i = 0; i < num; i++) {
mt_gpufreqs[i].gpufreq_khz = freqs[i].gpufreq_khz;
mt_gpufreqs[i].gpufreq_volt = freqs[i].gpufreq_volt;
mt_gpufreqs[i].gpufreq_idx = freqs[i].gpufreq_idx;
mt_gpufreqs_default[i].gpufreq_khz = freqs[i].gpufreq_khz;
mt_gpufreqs_default[i].gpufreq_volt = freqs[i].gpufreq_volt;
mt_gpufreqs_default[i].gpufreq_idx = freqs[i].gpufreq_idx;
gpufreq_dbg("freqs[%d].gpufreq_khz = %u\n", i, freqs[i].gpufreq_khz);
gpufreq_dbg("freqs[%d].gpufreq_volt = %u\n", i, freqs[i].gpufreq_volt);
gpufreq_dbg("freqs[%d].gpufreq_idx = %u\n", i, freqs[i].gpufreq_idx);
}
mt_gpufreqs_num = num;
g_limited_max_id = 0;
g_limited_min_id = mt_gpufreqs_num - 1;
gpufreq_info("@%s: g_cur_gpu_freq = %d, g_cur_gpu_volt = %d\n", __func__, g_cur_gpu_freq,
g_cur_gpu_volt);
mt_setup_gpufreqs_power_table(num);
return 0;
}
/**************************************
* check if maximum frequency is needed
***************************************/
static int mt_gpufreq_keep_max_freq(unsigned int freq_old, unsigned int freq_new)
{
if (mt_gpufreq_keep_max_frequency_state == true)
return 1;
return 0;
}
/*****************************
* set GPU DVFS status
******************************/
int mt_gpufreq_state_set(int enabled)
{
if (enabled) {
if (!mt_gpufreq_pause) {
gpufreq_dbg("gpufreq already enabled\n");
return 0;
}
/*****************
* enable GPU DVFS
******************/
g_gpufreq_dvfs_disable_count--;
gpufreq_dbg("enable GPU DVFS: g_gpufreq_dvfs_disable_count = %d\n",
g_gpufreq_dvfs_disable_count);
/***********************************************
* enable DVFS if no any module still disable it
************************************************/
if (g_gpufreq_dvfs_disable_count <= 0)
mt_gpufreq_pause = false;
else
gpufreq_warn("someone still disable gpufreq, cannot enable it\n");
} else {
/******************
* disable GPU DVFS
*******************/
g_gpufreq_dvfs_disable_count++;
gpufreq_dbg("disable GPU DVFS: g_gpufreq_dvfs_disable_count = %d\n",
g_gpufreq_dvfs_disable_count);
if (mt_gpufreq_pause) {
gpufreq_dbg("gpufreq already disabled\n");
return 0;
}
mt_gpufreq_pause = true;
}
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_state_set);
static unsigned int mt_gpufreq_dds_calc(unsigned int freq_khz, enum post_div_order_enum post_div_order)
{
unsigned int dds = 0;
dds = (((freq_khz * 4) / 1000) * 0x4000) / 26;
gpufreq_dbg("@%s: request freq = %d, div_order = %d, dds = %x\n",
__func__, freq_khz, post_div_order, dds);
return dds;
}
static void mt_gpufreq_clock_switch(unsigned int freq_new)
{
unsigned int dds;
if (freq_new == g_cur_gpu_freq)
return;
dds = mt_gpufreq_dds_calc(freq_new, POST_DIV4);
#ifdef CONFIG_MTK_FREQ_HOPPING
mt_dfs_mmpll(dds);
#endif
g_cur_gpu_freq = freq_new;
if (g_pFreqSampler != NULL)
g_pFreqSampler(freq_new);
gpufreq_dbg("mt_gpu_clock_switch, freq_new = %d (KHz)\n", freq_new);
if (g_pFreqSampler != NULL)
g_pFreqSampler(freq_new);
}
static void mt_gpufreq_volt_switch(unsigned int volt_old, unsigned int volt_new)
{
/* unsigned int delay_unit_us = 100; */
gpufreq_dbg("@%s: volt_new = %d\n", __func__, volt_new);
/* #ifdef VGPU_SET_BY_PMIC */
#if 0
if (volt_new > volt_old)
regulator_set_voltage(mt_gpufreq_pmic->reg_vgpu,
volt_new*10, (PMIC_MAX_VGPU*10) + 125);
else
regulator_set_voltage(mt_gpufreq_pmic->reg_vgpu,
volt_new*10, volt_old*10);
udelay(delay_unit_us);
#endif
if (g_pVoltSampler != NULL)
g_pVoltSampler(volt_new);
}
static unsigned int _mt_gpufreq_get_cur_freq(void)
{
return g_cur_gpu_freq;
}
static unsigned int _mt_gpufreq_get_cur_volt(void)
{
unsigned int gpu_volt = 0;
#if defined(VGPU_SET_BY_PMIC)
/* WARRNING: regulator_get_voltage prints uV */
gpu_volt = regulator_get_voltage(mt_gpufreq_pmic->reg_vgpu) / 10;
gpufreq_dbg("gpu_dvfs_get_cur_volt:[PMIC] volt = %d\n", gpu_volt);
#else
gpufreq_dbg("gpu_dvfs_get_cur_volt:[WARN] no tran value\n");
#endif
return gpu_volt;
}
static void _mt_gpufreq_kick_pbm(int enable)
{
#ifndef DISABLE_PBM_FEATURE
int i;
int tmp_idx = -1;
unsigned int found = 0;
unsigned int power;
unsigned int cur_volt = _mt_gpufreq_get_cur_volt();
unsigned int cur_freq = _mt_gpufreq_get_cur_freq();
if (enable) {
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs_power[i].gpufreq_khz == cur_freq) {
/* record idx since current voltage may not in DVFS table */
tmp_idx = i;
if (mt_gpufreqs_power[i].gpufreq_volt == cur_volt) {
power = mt_gpufreqs_power[i].gpufreq_power;
found = 1;
kicker_pbm_by_gpu(true, power, cur_volt / 100);
gpufreq_dbg
("@%s: request GPU power = %d, cur_volt = %d, cur_freq = %d\n",
__func__, power, cur_volt / 100, cur_freq);
return;
}
}
}
if (!found) {
gpufreq_dbg("@%s: tmp_idx = %d\n", __func__, tmp_idx);
if (tmp_idx != -1 && tmp_idx < mt_gpufreqs_num) {
/* use freq to found corresponding power budget */
power = mt_gpufreqs_power[tmp_idx].gpufreq_power;
kicker_pbm_by_gpu(true, power, cur_volt / 100);
gpufreq_dbg
("@%s: request GPU power = %d, cur_volt = %d, cur_freq = %d\n",
__func__, power, cur_volt / 100, cur_freq);
} else {
gpufreq_warn("@%s: Cannot found request power in power table!\n",
__func__);
gpufreq_warn("cur_freq = %dKHz, cur_volt = %dmV\n", cur_freq,
cur_volt / 100);
}
}
} else {
kicker_pbm_by_gpu(false, 0, cur_volt / 100);
}
#endif
}
/*****************************************
* frequency ramp up and ramp down handler
******************************************/
/***********************************************************
* [note]
* 1. frequency ramp up need to wait voltage settle
* 2. frequency ramp down do not need to wait voltage settle
************************************************************/
static void mt_gpufreq_set(unsigned int freq_old, unsigned int freq_new,
unsigned int volt_old, unsigned int volt_new)
{
pr_debug("[ERROR] mt_gpufreq_set set to old volt=%x, new volt=%x", volt_old, volt_new);
pr_debug("[ERROR] mt_gpufreq_set set to old freq=%x, new freq=%x", freq_old, freq_new);
if (freq_new > freq_old) {
/* if(volt_old != volt_new) // ??? */
/* { */
mt_gpufreq_volt_switch(volt_old, volt_new);
/* } */
mt_gpufreq_clock_switch(freq_new);
} else {
mt_gpufreq_clock_switch(freq_new);
/* if(volt_old != volt_new) */
/* { */
mt_gpufreq_volt_switch(volt_old, volt_new);
/* } */
}
g_cur_gpu_freq = freq_new;
g_cur_gpu_volt = volt_new;
_mt_gpufreq_kick_pbm(1);
}
/**********************************
* gpufreq target callback function
***********************************/
/*************************************************
* [note]
* 1. handle frequency change request
* 2. call mt_gpufreq_set to set target frequency
**************************************************/
unsigned int mt_gpufreq_target(unsigned int idx)
{
/* unsigned long flags; */
unsigned int target_freq, target_volt, target_idx, target_OPPidx;
#ifdef MT_GPUFREQ_PERFORMANCE_TEST
return 0;
#endif
mutex_lock(&mt_gpufreq_lock);
if (mt_gpufreq_pause == true) {
gpufreq_warn("GPU DVFS pause!\n");
mutex_unlock(&mt_gpufreq_lock);
return DRIVER_NOT_READY;
}
if (mt_gpufreq_ready == false) {
gpufreq_warn("GPU DVFS not ready!\n");
mutex_unlock(&mt_gpufreq_lock);
return DRIVER_NOT_READY;
}
if (mt_gpufreq_volt_enable_state == 0) {
gpufreq_dbg("mt_gpufreq_volt_enable_state == 0! return\n");
mutex_unlock(&mt_gpufreq_lock);
return DRIVER_NOT_READY;
}
#ifdef MT_GPU_DVFS_RANDOM_TEST
idx = mt_gpufreq_idx_get(5);
gpufreq_dbg("@%s: random test index is %d !\n", __func__, idx);
#endif
if (idx > (mt_gpufreqs_num - 1)) {
mutex_unlock(&mt_gpufreq_lock);
gpufreq_err("@%s: idx out of range! idx = %d\n", __func__, idx);
return -1;
}
/**********************************
* look up for the target GPU OPP
***********************************/
target_freq = mt_gpufreqs[idx].gpufreq_khz;
target_volt = mt_gpufreqs[idx].gpufreq_volt;
target_idx = mt_gpufreqs[idx].gpufreq_idx;
target_OPPidx = idx;
gpufreq_dbg("@%s: begin, receive freq: %d, OPPidx: %d\n", __func__, target_freq,
target_OPPidx);
/**********************************
* Check if need to keep max frequency
***********************************/
if (mt_gpufreq_keep_max_freq(g_cur_gpu_freq, target_freq)) {
target_freq = mt_gpufreqs[g_gpufreq_max_id].gpufreq_khz;
target_volt = mt_gpufreqs[g_gpufreq_max_id].gpufreq_volt;
target_idx = mt_gpufreqs[g_gpufreq_max_id].gpufreq_idx;
target_OPPidx = g_gpufreq_max_id;
gpufreq_dbg("Keep MAX frequency %d !\n", target_freq);
}
/************************************************
* If /proc command keep opp frequency.
*************************************************/
if (mt_gpufreq_keep_opp_frequency_state == true) {
target_freq = mt_gpufreqs[mt_gpufreq_keep_opp_index].gpufreq_khz;
target_volt = mt_gpufreqs[mt_gpufreq_keep_opp_index].gpufreq_volt;
target_idx = mt_gpufreqs[mt_gpufreq_keep_opp_index].gpufreq_idx;
target_OPPidx = mt_gpufreq_keep_opp_index;
gpufreq_dbg("Keep opp! opp frequency %d, opp voltage %d, opp idx %d\n", target_freq,
target_volt, target_OPPidx);
}
/************************************************
* If /proc command fix the frequency.
*************************************************/
if (mt_gpufreq_fixed_freq_volt_state == true) {
target_freq = mt_gpufreq_fixed_frequency;
target_volt = mt_gpufreq_fixed_voltage;
target_idx = 0;
target_OPPidx = 0;
gpufreq_dbg("Fixed! fixed frequency %d, fixed voltage %d\n", target_freq,
target_volt);
}
/************************************************
* If /proc command keep opp max frequency.
*************************************************/
if (mt_gpufreq_opp_max_frequency_state == true) {
if (target_freq > mt_gpufreq_opp_max_frequency) {
target_freq = mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_khz;
target_volt = mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_volt;
target_idx = mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_idx;
target_OPPidx = mt_gpufreq_opp_max_index;
gpufreq_dbg
("opp max freq! opp max frequency %d, opp max voltage %d, opp max idx %d\n",
target_freq, target_volt, target_OPPidx);
}
}
/************************************************
* PBM limit
*************************************************/
#ifndef DISABLE_PBM_FEATURE
if (mt_gpufreq_pbm_limited_index != 0) {
if (target_freq > mt_gpufreqs[mt_gpufreq_pbm_limited_index].gpufreq_khz) {
/*********************************************
* target_freq > limited_freq, need to adjust
**********************************************/
target_freq = mt_gpufreqs[mt_gpufreq_pbm_limited_index].gpufreq_khz;
target_volt = mt_gpufreqs[mt_gpufreq_pbm_limited_index].gpufreq_volt;
target_OPPidx = mt_gpufreq_pbm_limited_index;
gpufreq_dbg("Limit! Thermal/Power limit gpu frequency %d\n",
mt_gpufreqs[mt_gpufreq_pbm_limited_index].gpufreq_khz);
}
}
#endif
/************************************************
* Thermal/Power limit
*************************************************/
if (g_limited_max_id != 0) {
if (target_freq > mt_gpufreqs[g_limited_max_id].gpufreq_khz) {
/*********************************************
* target_freq > limited_freq, need to adjust
**********************************************/
target_freq = mt_gpufreqs[g_limited_max_id].gpufreq_khz;
target_volt = mt_gpufreqs[g_limited_max_id].gpufreq_volt;
target_idx = mt_gpufreqs[g_limited_max_id].gpufreq_idx;
target_OPPidx = g_limited_max_id;
gpufreq_info("Limit! Thermal/Power limit gpu frequency %d\n",
mt_gpufreqs[g_limited_max_id].gpufreq_khz);
}
}
/************************************************
* DVFS keep at max freq when PTPOD initial
*************************************************/
if (mt_gpufreq_ptpod_disable == true) {
#if 1
target_freq = mt_gpufreqs[mt_gpufreq_ptpod_disable_idx].gpufreq_khz;
target_volt = GPU_DVFS_PTPOD_DISABLE_VOLT;
target_idx = mt_gpufreqs[mt_gpufreq_ptpod_disable_idx].gpufreq_idx;
target_OPPidx = mt_gpufreq_ptpod_disable_idx;
gpufreq_dbg("PTPOD disable dvfs, mt_gpufreq_ptpod_disable_idx = %d\n",
mt_gpufreq_ptpod_disable_idx);
#else
mutex_unlock(&mt_gpufreq_lock);
gpufreq_dbg("PTPOD disable dvfs, return\n");
return 0;
#endif
}
/************************************************
* target frequency == current frequency, skip it
*************************************************/
if (g_cur_gpu_freq == target_freq && g_cur_gpu_volt == target_volt) {
mutex_unlock(&mt_gpufreq_lock);
gpufreq_dbg("GPU frequency from %d KHz to %d KHz (skipped) due to same frequency\n",
g_cur_gpu_freq, target_freq);
return 0;
}
gpufreq_dbg("GPU current frequency %d KHz, target frequency %d KHz\n", g_cur_gpu_freq,
target_freq);
#ifdef MT_GPUFREQ_AEE_RR_REC
aee_rr_rec_gpu_dvfs_status(aee_rr_curr_gpu_dvfs_status() | (1 << GPU_DVFS_IS_DOING_DVFS));
aee_rr_rec_gpu_dvfs_oppidx(target_OPPidx);
#endif
/******************************
* set to the target frequency
*******************************/
mt_gpufreq_set(g_cur_gpu_freq, target_freq, g_cur_gpu_volt, target_volt);
g_cur_gpu_idx = target_idx;
g_cur_gpu_OPPidx = target_OPPidx;
#ifdef MT_GPUFREQ_AEE_RR_REC
aee_rr_rec_gpu_dvfs_status(aee_rr_curr_gpu_dvfs_status() & ~(1 << GPU_DVFS_IS_DOING_DVFS));
#endif
mutex_unlock(&mt_gpufreq_lock);
return 0;
}
EXPORT_SYMBOL(mt_gpufreq_target);
/********************************************
* POWER LIMIT RELATED
********************************************/
enum {
IDX_THERMAL_LIMITED,
IDX_LOW_BATT_VOLT_LIMITED,
IDX_LOW_BATT_VOLUME_LIMITED,
IDX_OC_LIMITED,
NR_IDX_POWER_LIMITED,
};
/* NO need to throttle when OC */
#ifdef MT_GPUFREQ_OC_PROTECT
static unsigned int mt_gpufreq_oc_level;
#define MT_GPUFREQ_OC_LIMIT_FREQ_1 GPU_DVFS_FREQ4 /* no need to throttle when OC */
static unsigned int mt_gpufreq_oc_limited_index_0; /* unlimit frequency, index = 0. */
static unsigned int mt_gpufreq_oc_limited_index_1;
static unsigned int mt_gpufreq_oc_limited_index; /* Limited frequency index for oc */
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
static unsigned int mt_gpufreq_low_battery_volume;
#define MT_GPUFREQ_LOW_BATT_VOLUME_LIMIT_FREQ_1 GPU_DVFS_FREQ0
static unsigned int mt_gpufreq_low_bat_volume_limited_index_0; /* unlimit frequency, index = 0. */
static unsigned int mt_gpufreq_low_bat_volume_limited_index_1;
static unsigned int mt_gpufreq_low_batt_volume_limited_index; /* Limited frequency index for low battery volume */
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
static unsigned int mt_gpufreq_low_battery_level;
#define MT_GPUFREQ_LOW_BATT_VOLT_LIMIT_FREQ_1 GPU_DVFS_FREQ0 /* no need to throttle when LV1 */
#define MT_GPUFREQ_LOW_BATT_VOLT_LIMIT_FREQ_2 GPU_DVFS_FREQ4
static unsigned int mt_gpufreq_low_bat_volt_limited_index_0; /* unlimit frequency, index = 0. */
static unsigned int mt_gpufreq_low_bat_volt_limited_index_1;
static unsigned int mt_gpufreq_low_bat_volt_limited_index_2;
static unsigned int mt_gpufreq_low_batt_volt_limited_index; /* Limited frequency index for low battery voltage */
#endif
static unsigned int mt_gpufreq_thermal_limited_gpu_power; /* thermal limit power */
static unsigned int mt_gpufreq_prev_thermal_limited_freq; /* thermal limited freq */
/* limit frequency index array */
static unsigned int mt_gpufreq_power_limited_index_array[NR_IDX_POWER_LIMITED] = { 0 };
/************************************************
* frequency adjust interface for thermal protect
*************************************************/
/******************************************************
* parameter: target power
*******************************************************/
static int mt_gpufreq_power_throttle_protect(void)
{
int ret = 0;
int i = 0;
unsigned int limited_index = 0;
/* Check lowest frequency in all limitation */
for (i = 0; i < NR_IDX_POWER_LIMITED; i++) {
if (mt_gpufreq_power_limited_index_array[i] != 0 && limited_index == 0)
limited_index = mt_gpufreq_power_limited_index_array[i];
else if (mt_gpufreq_power_limited_index_array[i] != 0 && limited_index != 0) {
if (mt_gpufreq_power_limited_index_array[i] > limited_index)
limited_index = mt_gpufreq_power_limited_index_array[i];
}
}
g_limited_max_id = limited_index;
if (g_pGpufreq_power_limit_notify != NULL)
g_pGpufreq_power_limit_notify(g_limited_max_id);
return ret;
}
#ifdef MT_GPUFREQ_OC_PROTECT
/************************************************
* GPU frequency adjust interface for oc protect
*************************************************/
static void mt_gpufreq_oc_protect(unsigned int limited_index)
{
mutex_lock(&mt_gpufreq_power_lock);
gpufreq_dbg("@%s: limited_index = %d\n", __func__, limited_index);
mt_gpufreq_power_limited_index_array[IDX_OC_LIMITED] = limited_index;
mt_gpufreq_power_throttle_protect();
mutex_unlock(&mt_gpufreq_power_lock);
}
void mt_gpufreq_oc_callback(enum BATTERY_OC_LEVEL oc_level)
{
gpufreq_dbg("@%s: oc_level = %d\n", __func__, oc_level);
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
if (g_limited_oc_ignore_state == true) {
gpufreq_info("@%s: g_limited_oc_ignore_state == true!\n", __func__);
return;
}
mt_gpufreq_oc_level = oc_level;
/* BATTERY_OC_LEVEL_1: >= 5.5A */
if (oc_level == BATTERY_OC_LEVEL_1) {
if (mt_gpufreq_oc_limited_index != mt_gpufreq_oc_limited_index_1) {
mt_gpufreq_oc_limited_index = mt_gpufreq_oc_limited_index_1;
mt_gpufreq_oc_protect(mt_gpufreq_oc_limited_index_1); /* Limit GPU 396.5Mhz */
}
}
/* unlimit gpu */
else {
if (mt_gpufreq_oc_limited_index != mt_gpufreq_oc_limited_index_0) {
mt_gpufreq_oc_limited_index = mt_gpufreq_oc_limited_index_0;
mt_gpufreq_oc_protect(mt_gpufreq_oc_limited_index_0); /* Unlimit */
}
}
}
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
/************************************************
* GPU frequency adjust interface for low bat_volume protect
*************************************************/
static void mt_gpufreq_low_batt_volume_protect(unsigned int limited_index)
{
mutex_lock(&mt_gpufreq_power_lock);
gpufreq_dbg("@%s: limited_index = %d\n", __func__, limited_index);
mt_gpufreq_power_limited_index_array[IDX_LOW_BATT_VOLUME_LIMITED] = limited_index;
mt_gpufreq_power_throttle_protect();
mutex_unlock(&mt_gpufreq_power_lock);
}
void mt_gpufreq_low_batt_volume_callback(enum BATTERY_PERCENT_LEVEL low_battery_volume)
{
gpufreq_dbg("@%s: low_battery_volume = %d\n", __func__, low_battery_volume);
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
if (g_limited_low_batt_volume_ignore_state == true) {
gpufreq_info("@%s: g_limited_low_batt_volume_ignore_state == true!\n", __func__);
return;
}
mt_gpufreq_low_battery_volume = low_battery_volume;
/* LOW_BATTERY_VOLUME_1: <= 15%, LOW_BATTERY_VOLUME_0: >15% */
if (low_battery_volume == BATTERY_PERCENT_LEVEL_1) {
if (mt_gpufreq_low_batt_volume_limited_index !=
mt_gpufreq_low_bat_volume_limited_index_1) {
mt_gpufreq_low_batt_volume_limited_index =
mt_gpufreq_low_bat_volume_limited_index_1;
/* Unlimited */
mt_gpufreq_low_batt_volume_protect(mt_gpufreq_low_bat_volume_limited_index_1);
}
}
/* unlimit gpu */
else {
if (mt_gpufreq_low_batt_volume_limited_index !=
mt_gpufreq_low_bat_volume_limited_index_0) {
mt_gpufreq_low_batt_volume_limited_index =
mt_gpufreq_low_bat_volume_limited_index_0;
mt_gpufreq_low_batt_volume_protect(mt_gpufreq_low_bat_volume_limited_index_0); /* Unlimit */
}
}
}
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
/************************************************
* GPU frequency adjust interface for low bat_volt protect
*************************************************/
static void mt_gpufreq_low_batt_volt_protect(unsigned int limited_index)
{
mutex_lock(&mt_gpufreq_power_lock);
gpufreq_dbg("@%s: limited_index = %d\n", __func__, limited_index);
mt_gpufreq_power_limited_index_array[IDX_LOW_BATT_VOLT_LIMITED] = limited_index;
mt_gpufreq_power_throttle_protect();
mutex_unlock(&mt_gpufreq_power_lock);
}
/******************************************************
* parameter: low_battery_level
*******************************************************/
void mt_gpufreq_low_batt_volt_callback(enum LOW_BATTERY_LEVEL low_battery_level)
{
gpufreq_dbg("@%s: low_battery_level = %d\n", __func__, low_battery_level);
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
return;
}
if (g_limited_low_batt_volt_ignore_state == true) {
gpufreq_info("@%s: g_limited_low_batt_volt_ignore_state == true!\n", __func__);
return;
}
mt_gpufreq_low_battery_level = low_battery_level;
/* is_low_battery=1:need limit HW, is_low_battery=0:no limit */
/* 3.25V HW issue int and is_low_battery=1,
* 3.0V HW issue int and is_low_battery=2,
* 3.5V HW issue int and is_low_battery=0
*/
/* no need to throttle when LV1 */
#if 0
if (low_battery_level == LOW_BATTERY_LEVEL_1) {
if (mt_gpufreq_low_batt_volt_limited_index !=
mt_gpufreq_low_bat_volt_limited_index_1) {
mt_gpufreq_low_batt_volt_limited_index =
mt_gpufreq_low_bat_volt_limited_index_1;
/* Limit GPU 416Mhz */
mt_gpufreq_low_batt_volt_protect(mt_gpufreq_low_bat_volt_limited_index_1);
}
} else
#endif
if (low_battery_level == LOW_BATTERY_LEVEL_2) {
if (mt_gpufreq_low_batt_volt_limited_index !=
mt_gpufreq_low_bat_volt_limited_index_2) {
mt_gpufreq_low_batt_volt_limited_index =
mt_gpufreq_low_bat_volt_limited_index_2;
/* Limit GPU 400Mhz */
mt_gpufreq_low_batt_volt_protect(mt_gpufreq_low_bat_volt_limited_index_2);
}
} else { /* unlimit gpu */
if (mt_gpufreq_low_batt_volt_limited_index !=
mt_gpufreq_low_bat_volt_limited_index_0) {
mt_gpufreq_low_batt_volt_limited_index =
mt_gpufreq_low_bat_volt_limited_index_0;
/* Unlimit */
mt_gpufreq_low_batt_volt_protect(mt_gpufreq_low_bat_volt_limited_index_0);
}
}
}
#endif
/************************************************
* frequency adjust interface for thermal protect
*************************************************/
/******************************************************
* parameter: target power
*******************************************************/
static unsigned int _mt_gpufreq_get_limited_freq(unsigned int limited_power)
{
int i = 0;
unsigned int limited_freq = 0;
unsigned int found = 0;
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs_power[i].gpufreq_power <= limited_power) {
limited_freq = mt_gpufreqs_power[i].gpufreq_khz;
found = 1;
break;
}
}
/* not found */
if (!found)
limited_freq = mt_gpufreqs_power[mt_gpufreqs_num - 1].gpufreq_khz;
gpufreq_dbg("@%s: limited_freq = %d\n", __func__, limited_freq);
return limited_freq;
}
void mt_gpufreq_thermal_protect(unsigned int limited_power)
{
int i = 0;
unsigned int limited_freq = 0;
mutex_lock(&mt_gpufreq_power_lock);
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
if (mt_gpufreqs_num == 0) {
gpufreq_warn("@%s: mt_gpufreqs_num == 0!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
if (g_limited_thermal_ignore_state == true) {
gpufreq_info("@%s: g_limited_thermal_ignore_state == true!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
mt_gpufreq_thermal_limited_gpu_power = limited_power;
#ifdef MT_GPUFREQ_DYNAMIC_POWER_TABLE_UPDATE
mt_update_gpufreqs_power_table();
#endif
if (limited_power == 0)
mt_gpufreq_power_limited_index_array[IDX_THERMAL_LIMITED] = 0;
else {
limited_freq = _mt_gpufreq_get_limited_freq(limited_power);
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz <= limited_freq) {
mt_gpufreq_power_limited_index_array[IDX_THERMAL_LIMITED] = i;
break;
}
}
}
if (mt_gpufreq_prev_thermal_limited_freq != limited_freq) {
mt_gpufreq_prev_thermal_limited_freq = limited_freq;
mt_gpufreq_power_throttle_protect();
if (limited_freq < GPU_DVFS_FREQ5)
gpufreq_info("@%s: p %u f %u i %u\n", __func__, limited_power, limited_freq,
mt_gpufreq_power_limited_index_array[IDX_THERMAL_LIMITED]);
}
mutex_unlock(&mt_gpufreq_power_lock);
}
EXPORT_SYMBOL(mt_gpufreq_thermal_protect);
/* for thermal to update power budget */
unsigned int mt_gpufreq_get_max_power(void)
{
if (!mt_gpufreqs_power)
return 0;
else
return mt_gpufreqs_power[0].gpufreq_power;
}
/* for thermal to update power budget */
unsigned int mt_gpufreq_get_min_power(void)
{
if (!mt_gpufreqs_power)
return 0;
else
return mt_gpufreqs_power[mt_gpufreqs_num - 1].gpufreq_power;
}
void mt_gpufreq_set_power_limit_by_pbm(unsigned int limited_power)
{
#ifndef DISABLE_PBM_FEATURE
int i = 0;
unsigned int limited_freq = 0;
mutex_lock(&mt_gpufreq_power_lock);
if (mt_gpufreq_ready == false) {
gpufreq_warn("@%s: GPU DVFS not ready!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
if (mt_gpufreqs_num == 0) {
gpufreq_warn("@%s: mt_gpufreqs_num == 0!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
if (g_limited_pbm_ignore_state == true) {
gpufreq_info("@%s: g_limited_pbm_ignore_state == true!\n", __func__);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
if (limited_power == mt_gpufreq_pbm_limited_gpu_power) {
gpufreq_dbg("@%s: limited_power(%d mW) not changed, skip it!\n",
__func__, limited_power);
mutex_unlock(&mt_gpufreq_power_lock);
return;
}
mt_gpufreq_pbm_limited_gpu_power = limited_power;
gpufreq_dbg("@%s: limited_power = %d\n", __func__, limited_power);
#ifdef MT_GPUFREQ_DYNAMIC_POWER_TABLE_UPDATE
mt_update_gpufreqs_power_table(); /* TODO: need to check overhead? */
#endif
if (limited_power == 0)
mt_gpufreq_pbm_limited_index = 0;
else {
limited_freq = _mt_gpufreq_get_limited_freq(limited_power);
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz <= limited_freq) {
mt_gpufreq_pbm_limited_index = i;
break;
}
}
}
gpufreq_dbg("PBM limit frequency upper bound to id = %d\n", mt_gpufreq_pbm_limited_index);
if (g_pGpufreq_power_limit_notify != NULL)
g_pGpufreq_power_limit_notify(mt_gpufreq_pbm_limited_index);
mutex_unlock(&mt_gpufreq_power_lock);
#endif
}
#if 0 // pedro
unsigned int mt_gpufreq_get_leakage_mw(void)
{
#ifndef DISABLE_PBM_FEATURE
int temp = 0;
#ifdef STATIC_PWR_READY2USE
unsigned int cur_vcore = _mt_gpufreq_get_cur_volt() / 100;
int leak_power;
#endif
#ifdef CONFIG_THERMAL
temp = get_immediate_gpu_wrap() / 1000;
#else
temp = 40;
#endif
#ifdef STATIC_PWR_READY2USE
leak_power = mt_spower_get_leakage(MT_SPOWER_GPU, cur_vcore, temp);
if (mt_gpufreq_volt_enable_state && leak_power > 0)
return leak_power;
else
return 0;
#else
return 130;
#endif
#else /* DISABLE_PBM_FEATURE */
return 0;
#endif
}
#endif
/************************************************
* return current GPU thermal limit index
*************************************************/
unsigned int mt_gpufreq_get_thermal_limit_index(void)
{
gpufreq_dbg("current GPU thermal limit index is %d\n", g_limited_max_id);
return g_limited_max_id;
}
EXPORT_SYMBOL(mt_gpufreq_get_thermal_limit_index);
/************************************************
* return current GPU thermal limit frequency
*************************************************/
unsigned int mt_gpufreq_get_thermal_limit_freq(void)
{
gpufreq_dbg("current GPU thermal limit freq is %d MHz\n",
mt_gpufreqs[g_limited_max_id].gpufreq_khz / 1000);
return mt_gpufreqs[g_limited_max_id].gpufreq_khz;
}
EXPORT_SYMBOL(mt_gpufreq_get_thermal_limit_freq);
/************************************************
* return current GPU frequency index
*************************************************/
unsigned int mt_gpufreq_get_cur_freq_index(void)
{
gpufreq_dbg("current GPU frequency OPP index is %d\n", g_cur_gpu_OPPidx);
return g_cur_gpu_OPPidx;
}
EXPORT_SYMBOL(mt_gpufreq_get_cur_freq_index);
/************************************************
* return current GPU frequency
*************************************************/
unsigned int mt_gpufreq_get_cur_freq(void)
{
#ifdef MTK_GPU_SPM
return _mt_gpufreq_get_cur_freq();
#else
gpufreq_dbg("current GPU frequency is %d MHz\n", g_cur_gpu_freq / 1000);
return g_cur_gpu_freq;
#endif
}
EXPORT_SYMBOL(mt_gpufreq_get_cur_freq);
/************************************************
* return current GPU voltage
*************************************************/
unsigned int mt_gpufreq_get_cur_volt(void)
{
#if 0
return g_cur_gpu_volt;
#else
return _mt_gpufreq_get_cur_volt();
#endif
}
EXPORT_SYMBOL(mt_gpufreq_get_cur_volt);
/************************************************
* register / unregister GPU input boost notifiction CB
*************************************************/
void mt_gpufreq_input_boost_notify_registerCB(gpufreq_input_boost_notify pCB)
{
#ifdef MT_GPUFREQ_INPUT_BOOST
g_pGpufreq_input_boost_notify = pCB;
#endif
}
EXPORT_SYMBOL(mt_gpufreq_input_boost_notify_registerCB);
/************************************************
* register / unregister GPU power limit notifiction CB
*************************************************/
void mt_gpufreq_power_limit_notify_registerCB(gpufreq_power_limit_notify pCB)
{
g_pGpufreq_power_limit_notify = pCB;
}
EXPORT_SYMBOL(mt_gpufreq_power_limit_notify_registerCB);
/************************************************
* register / unregister ptpod update GPU volt CB
*************************************************/
void mt_gpufreq_update_volt_registerCB(gpufreq_ptpod_update_notify pCB)
{
g_pGpufreq_ptpod_update_notify = pCB;
}
EXPORT_SYMBOL(mt_gpufreq_update_volt_registerCB);
/************************************************
* register / unregister set GPU freq CB
*************************************************/
void mt_gpufreq_setfreq_registerCB(sampler_func pCB)
{
g_pFreqSampler = pCB;
}
EXPORT_SYMBOL(mt_gpufreq_setfreq_registerCB);
/************************************************
* register / unregister set GPU volt CB
*************************************************/
void mt_gpufreq_setvolt_registerCB(sampler_func pCB)
{
g_pVoltSampler = pCB;
}
EXPORT_SYMBOL(mt_gpufreq_setvolt_registerCB);
/************************************************
* for ptpod used to open gpu external/internal power.
*************************************************/
void mt_gpufreq_mfgclock_notify_registerCB(gpufreq_mfgclock_notify pEnableCB,
gpufreq_mfgclock_notify pDisableCB)
{
g_pGpufreq_mfgclock_enable_notify = pEnableCB;
g_pGpufreq_mfgclock_disable_notify = pDisableCB;
}
EXPORT_SYMBOL(mt_gpufreq_mfgclock_notify_registerCB);
#ifdef CONFIG_HAS_EARLYSUSPEND
/*********************************
* early suspend callback function
**********************************/
void mt_gpufreq_early_suspend(struct early_suspend *h)
{
/* mt_gpufreq_state_set(0); */
}
/*******************************
* late resume callback function
********************************/
void mt_gpufreq_late_resume(struct early_suspend *h)
{
/* mt_gpufreq_check_freq_and_set_pll(); */
/* mt_gpufreq_state_set(1); */
}
#endif
static int mt_gpufreq_pm_restore_early(struct device *dev)
{
int i = 0;
int found = 0;
g_cur_gpu_freq = _mt_gpufreq_get_cur_freq();
for (i = 0; i < mt_gpufreqs_num; i++) {
if (g_cur_gpu_freq == mt_gpufreqs[i].gpufreq_khz) {
g_cur_gpu_idx = mt_gpufreqs[i].gpufreq_idx;
g_cur_gpu_volt = mt_gpufreqs[i].gpufreq_volt;
g_cur_gpu_OPPidx = i;
found = 1;
gpufreq_dbg("match g_cur_gpu_OPPidx: %d\n", g_cur_gpu_OPPidx);
break;
}
}
if (found == 0) {
g_cur_gpu_idx = mt_gpufreqs[0].gpufreq_idx;
g_cur_gpu_volt = mt_gpufreqs[0].gpufreq_volt;
g_cur_gpu_OPPidx = 0;
gpufreq_err("gpu freq not found, set parameter to max freq\n");
}
gpufreq_dbg("GPU freq SW/HW: %d/%d\n", g_cur_gpu_freq, _mt_gpufreq_get_cur_freq());
gpufreq_dbg("g_cur_gpu_OPPidx: %d\n", g_cur_gpu_OPPidx);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id mt_gpufreq_of_match[] = {
{.compatible = "mediatek,mt8167-gpufreq",},
{ /* sentinel */ },
};
#endif
MODULE_DEVICE_TABLE(of, mt_gpufreq_of_match);
static int mt_gpufreq_pdrv_probe(struct platform_device *pdev)
{
mt_gpufreq_dvfs_table_type = mt_gpufreq_get_dvfs_table_type();
/**********************
* Initial leackage power usage
***********************/
#ifdef STATIC_PWR_READY2USE
mt_spower_init();
#endif
/**********************
* setup gpufreq table
***********************/
gpufreq_info("setup gpufreqs table\n");
switch (mt_gpufreq_dvfs_table_type) {
case 0: /* 400MHz */
mt_setup_gpufreqs_table(mt_gpufreq_opp_tbl_e1_0,
ARRAY_SIZE(mt_gpufreq_opp_tbl_e1_0));
break;
case 1: /* 500MHz */
mt_setup_gpufreqs_table(mt_gpufreq_opp_tbl_e1_1,
ARRAY_SIZE(mt_gpufreq_opp_tbl_e1_1));
break;
case 2: /* 600MHz */
mt_setup_gpufreqs_table(mt_gpufreq_opp_tbl_e1_2,
ARRAY_SIZE(mt_gpufreq_opp_tbl_e1_2));
break;
default: /* 400MHz */
mt_setup_gpufreqs_table(mt_gpufreq_opp_tbl_e1_0,
ARRAY_SIZE(mt_gpufreq_opp_tbl_e1_0));
break;
}
/**********************
* setup PMIC init value
***********************/
#ifdef VGPU_SET_BY_PMIC
gpufreq_info("VGPU Enabled (%d) %d mV\n",
regulator_is_enabled(mt_gpufreq_pmic->reg_vgpu),
_mt_gpufreq_get_cur_volt());
mt_gpufreq_volt_enable_state = 1;
/**********************
* setup initial frequency
***********************/
mt_gpufreq_set_initial();
g_cur_freq_init_keep = g_cur_gpu_freq;
gpufreq_info("GPU current frequency = %dKHz\n", _mt_gpufreq_get_cur_freq());
gpufreq_info("Current Vcore = %dmV\n", _mt_gpufreq_get_cur_volt() / 100);
gpufreq_info("g_cur_gpu_freq = %d, g_cur_gpu_volt = %d\n", g_cur_gpu_freq, g_cur_gpu_volt);
gpufreq_info("g_cur_gpu_idx = %d, g_cur_gpu_OPPidx = %d\n", g_cur_gpu_idx,
g_cur_gpu_OPPidx);
mt_gpufreq_ready = true;
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz == MT_GPUFREQ_LOW_BATT_VOLT_LIMIT_FREQ_1) {
mt_gpufreq_low_bat_volt_limited_index_1 = i;
break;
}
}
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz == MT_GPUFREQ_LOW_BATT_VOLT_LIMIT_FREQ_2) {
mt_gpufreq_low_bat_volt_limited_index_2 = i;
break;
}
}
/* register_low_battery_notify(&mt_gpufreq_low_batt_volt_callback, LOW_BATTERY_PRIO_GPU); */
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
pr_err("[ERROR] mt_gpufreq_pdrv_probe 17");
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz == MT_GPUFREQ_LOW_BATT_VOLUME_LIMIT_FREQ_1) {
mt_gpufreq_low_bat_volume_limited_index_1 = i;
break;
}
}
/* register_battery_percent_notify(&mt_gpufreq_low_batt_volume_callback,*/
/* BATTERY_PERCENT_PRIO_GPU); */
#endif
#ifdef MT_GPUFREQ_OC_PROTECT
pr_err("[ERROR] mt_gpufreq_pdrv_probe 18");
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz == MT_GPUFREQ_OC_LIMIT_FREQ_1) {
mt_gpufreq_oc_limited_index_1 = i;
break;
}
}
/* register_battery_oc_notify(&mt_gpufreq_oc_callback, BATTERY_OC_PRIO_GPU); */
#endif
#ifndef DISABLE_PBM_FEATURE
INIT_DEFERRABLE_WORK(&notify_pbm_gpuoff_work, mt_gpufreq_notify_pbm_gpuoff);
#endif
return 0;
}
/***************************************
* this function should never be called
****************************************/
static int mt_gpufreq_pdrv_remove(struct platform_device *pdev)
{
return 0;
}
static const struct dev_pm_ops mt_gpufreq_pm_ops = {
.suspend = NULL,
.resume = NULL,
.restore_early = mt_gpufreq_pm_restore_early,
};
static struct platform_driver mt_gpufreq_pdrv = {
.probe = mt_gpufreq_pdrv_probe,
.remove = mt_gpufreq_pdrv_remove,
.driver = {
.name = "gpufreq",
.pm = &mt_gpufreq_pm_ops,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = mt_gpufreq_of_match,
#endif
},
};
#ifdef CONFIG_PROC_FS
/* #if 0 */
/*
* PROC
*/
/***************************
* show current debug status
****************************/
static int mt_gpufreq_debug_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_debug)
seq_puts(m, "gpufreq debug enabled\n");
else
seq_puts(m, "gpufreq debug disabled\n");
return 0;
}
/***********************
* enable debug message
************************/
static ssize_t mt_gpufreq_debug_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int debug = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &debug) == 0) {
if (debug == 0)
mt_gpufreq_debug = 0;
else if (debug == 1)
mt_gpufreq_debug = 1;
else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
return count;
}
#ifdef MT_GPUFREQ_OC_PROTECT
/****************************
* show current limited by low batt volume
*****************************/
static int mt_gpufreq_limited_oc_ignore_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, g_limited_oc_ignore_state = %d\n", g_limited_max_id,
g_limited_oc_ignore_state);
return 0;
}
/**********************************
* limited for low batt volume protect
***********************************/
static ssize_t mt_gpufreq_limited_oc_ignore_proc_write(struct file *file,
const char __user *buffer, size_t count,
loff_t *data)
{
char desc[32];
int len = 0;
unsigned int ignore = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &ignore) == 0) {
if (ignore == 1)
g_limited_oc_ignore_state = true;
else if (ignore == 0)
g_limited_oc_ignore_state = false;
else
gpufreq_warn
("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
return count;
}
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
/****************************
* show current limited by low batt volume
*****************************/
static int mt_gpufreq_limited_low_batt_volume_ignore_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, g_limited_low_batt_volume_ignore_state = %d\n",
g_limited_max_id, g_limited_low_batt_volume_ignore_state);
return 0;
}
/**********************************
* limited for low batt volume protect
***********************************/
static ssize_t mt_gpufreq_limited_low_batt_volume_ignore_proc_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
unsigned int ignore = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &ignore) == 0) {
if (ignore == 1)
g_limited_low_batt_volume_ignore_state = true;
else if (ignore == 0)
g_limited_low_batt_volume_ignore_state = false;
else
gpufreq_warn
("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
return count;
}
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
/****************************
* show current limited by low batt volt
*****************************/
static int mt_gpufreq_limited_low_batt_volt_ignore_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, g_limited_low_batt_volt_ignore_state = %d\n",
g_limited_max_id, g_limited_low_batt_volt_ignore_state);
return 0;
}
/**********************************
* limited for low batt volt protect
***********************************/
static ssize_t mt_gpufreq_limited_low_batt_volt_ignore_proc_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
unsigned int ignore = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &ignore) == 0) {
if (ignore == 1)
g_limited_low_batt_volt_ignore_state = true;
else if (ignore == 0)
g_limited_low_batt_volt_ignore_state = false;
else
gpufreq_warn
("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
return count;
}
#endif
/****************************
* show current limited by thermal
*****************************/
static int mt_gpufreq_limited_thermal_ignore_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, g_limited_thermal_ignore_state = %d\n",
g_limited_max_id, g_limited_thermal_ignore_state);
return 0;
}
/**********************************
* limited for thermal protect
***********************************/
static ssize_t mt_gpufreq_limited_thermal_ignore_proc_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
unsigned int ignore = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &ignore) == 0) {
if (ignore == 1)
g_limited_thermal_ignore_state = true;
else if (ignore == 0)
g_limited_thermal_ignore_state = false;
else
gpufreq_warn
("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
return count;
}
#ifndef DISABLE_PBM_FEATURE
/****************************
* show current limited by PBM
*****************************/
static int mt_gpufreq_limited_pbm_ignore_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, g_limited_oc_ignore_state = %d\n", g_limited_max_id,
g_limited_pbm_ignore_state);
return 0;
}
/**********************************
* limited for low batt volume protect
***********************************/
static ssize_t mt_gpufreq_limited_pbm_ignore_proc_write(struct file *file,
const char __user *buffer, size_t count,
loff_t *data)
{
char desc[32];
int len = 0;
unsigned int ignore = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &ignore) == 0) {
if (ignore == 1)
g_limited_pbm_ignore_state = true;
else if (ignore == 0)
g_limited_pbm_ignore_state = false;
else
gpufreq_warn
("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: not ignore, 1: ignore]\n");
return count;
}
#endif
/****************************
* show current limited power
*****************************/
static int mt_gpufreq_limited_power_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "g_limited_max_id = %d, limit frequency = %d\n",
g_limited_max_id, mt_gpufreqs[g_limited_max_id].gpufreq_khz);
return 0;
}
/**********************************
* limited power for thermal protect
***********************************/
static ssize_t mt_gpufreq_limited_power_proc_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
unsigned int power = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &power) == 0)
mt_gpufreq_thermal_protect(power);
else
gpufreq_warn("bad argument!! please provide the maximum limited power\n");
return count;
}
/****************************
* show current limited power by PBM
*****************************/
#ifndef DISABLE_PBM_FEATURE
static int mt_gpufreq_limited_by_pbm_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "pbm_limited_power = %d, limit index = %d\n",
mt_gpufreq_pbm_limited_gpu_power, mt_gpufreq_pbm_limited_index);
return 0;
}
/**********************************
* limited power for thermal protect
***********************************/
static ssize_t mt_gpufreq_limited_by_pbm_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
unsigned int power = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtouint(desc, 0, &power) == 0)
mt_gpufreq_set_power_limit_by_pbm(power);
else
gpufreq_warn("bad argument!! please provide the maximum limited power\n");
return count;
}
#endif
/******************************
* show current GPU DVFS stauts
*******************************/
static int mt_gpufreq_state_proc_show(struct seq_file *m, void *v)
{
if (!mt_gpufreq_pause)
seq_puts(m, "GPU DVFS enabled\n");
else
seq_puts(m, "GPU DVFS disabled\n");
return 0;
}
/****************************************
* set GPU DVFS stauts by sysfs interface
*****************************************/
static ssize_t mt_gpufreq_state_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int enabled = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &enabled) == 0) {
if (enabled == 1) {
mt_gpufreq_keep_max_frequency_state = false;
mt_gpufreq_state_set(1);
} else if (enabled == 0) {
/* Keep MAX frequency when GPU DVFS disabled. */
mt_gpufreq_keep_max_frequency_state = true;
mt_gpufreq_voltage_enable_set(1);
mt_gpufreq_target(g_gpufreq_max_id);
mt_gpufreq_state_set(0);
} else
gpufreq_warn("bad argument!! argument should be \"1\" or \"0\"\n");
} else
gpufreq_warn("bad argument!! argument should be \"1\" or \"0\"\n");
return count;
}
/********************
* show GPU OPP table
*********************/
static int mt_gpufreq_opp_dump_proc_show(struct seq_file *m, void *v)
{
int i = 0;
for (i = 0; i < mt_gpufreqs_num; i++) {
seq_printf(m, "[%d] ", i);
seq_printf(m, "freq = %d, ", mt_gpufreqs[i].gpufreq_khz);
seq_printf(m, "volt = %d, ", mt_gpufreqs[i].gpufreq_volt);
seq_printf(m, "idx = %d\n", mt_gpufreqs[i].gpufreq_idx);
}
return 0;
}
/********************
* show GPU power table
*********************/
static int mt_gpufreq_power_dump_proc_show(struct seq_file *m, void *v)
{
int i = 0;
for (i = 0; i < mt_gpufreqs_num; i++) {
seq_printf(m, "mt_gpufreqs_power[%d].gpufreq_khz = %d\n", i,
mt_gpufreqs_power[i].gpufreq_khz);
seq_printf(m, "mt_gpufreqs_power[%d].gpufreq_volt = %d\n", i,
mt_gpufreqs_power[i].gpufreq_volt);
seq_printf(m, "mt_gpufreqs_power[%d].gpufreq_power = %d\n", i,
mt_gpufreqs_power[i].gpufreq_power);
}
return 0;
}
/***************************
* show current specific frequency status
****************************/
static int mt_gpufreq_opp_freq_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_keep_opp_frequency_state) {
seq_puts(m, "gpufreq keep opp frequency enabled\n");
seq_printf(m, "freq = %d\n", mt_gpufreqs[mt_gpufreq_keep_opp_index].gpufreq_khz);
seq_printf(m, "volt = %d\n", mt_gpufreqs[mt_gpufreq_keep_opp_index].gpufreq_volt);
} else
seq_puts(m, "gpufreq keep opp frequency disabled\n");
return 0;
}
/***********************
* enable specific frequency
************************/
static ssize_t mt_gpufreq_opp_freq_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int i = 0;
int fixed_freq = 0;
int found = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &fixed_freq) == 0) {
if (fixed_freq == 0) {
mt_gpufreq_keep_opp_frequency_state = false;
#ifdef MTK_GPU_SPM
mtk_gpu_spm_reset_fix();
#endif
} else {
for (i = 0; i < mt_gpufreqs_num; i++) {
if (fixed_freq == mt_gpufreqs[i].gpufreq_khz) {
mt_gpufreq_keep_opp_index = i;
found = 1;
break;
}
}
if (found == 1) {
mt_gpufreq_keep_opp_frequency_state = true;
mt_gpufreq_keep_opp_frequency = fixed_freq;
#ifndef MTK_GPU_SPM
mt_gpufreq_voltage_enable_set(1);
mt_gpufreq_target(mt_gpufreq_keep_opp_index);
#else
mtk_gpu_spm_fix_by_idx(mt_gpufreq_keep_opp_index);
#endif
}
}
} else
gpufreq_warn("bad argument!! please provide the fixed frequency\n");
return count;
}
/***************************
* show current specific frequency status
****************************/
static int mt_gpufreq_opp_max_freq_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_opp_max_frequency_state) {
seq_puts(m, "gpufreq opp max frequency enabled\n");
seq_printf(m, "freq = %d\n", mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_khz);
seq_printf(m, "volt = %d\n", mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_volt);
} else
seq_puts(m, "gpufreq opp max frequency disabled\n");
return 0;
}
/***********************
* enable specific frequency
************************/
static ssize_t mt_gpufreq_opp_max_freq_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int i = 0;
int max_freq = 0;
int found = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &max_freq) == 0) {
if (max_freq == 0) {
mt_gpufreq_opp_max_frequency_state = false;
} else {
for (i = 0; i < mt_gpufreqs_num; i++) {
if (mt_gpufreqs[i].gpufreq_khz <= max_freq) {
mt_gpufreq_opp_max_index = i;
found = 1;
break;
}
}
if (found == 1) {
mt_gpufreq_opp_max_frequency_state = true;
mt_gpufreq_opp_max_frequency =
mt_gpufreqs[mt_gpufreq_opp_max_index].gpufreq_khz;
mt_gpufreq_voltage_enable_set(1);
mt_gpufreq_target(mt_gpufreq_opp_max_index);
}
}
} else
gpufreq_warn("bad argument!! please provide the maximum limited frequency\n");
return count;
}
/********************
* show variable dump
*********************/
static int mt_gpufreq_var_dump_proc_show(struct seq_file *m, void *v)
{
int i = 0;
#ifdef MTK_GPU_SPM
seq_puts(m, "DVFS_GPU SPM is on\n");
#endif
seq_printf(m, "g_cur_gpu_freq = %d, g_cur_gpu_volt = %d\n", mt_gpufreq_get_cur_freq(),
mt_gpufreq_get_cur_volt());
seq_printf(m, "g_cur_gpu_idx = %d, g_cur_gpu_OPPidx = %d\n", g_cur_gpu_idx,
g_cur_gpu_OPPidx);
seq_printf(m, "g_limited_max_id = %d\n", g_limited_max_id);
for (i = 0; i < NR_IDX_POWER_LIMITED; i++)
seq_printf(m, "mt_gpufreq_power_limited_index_array[%d] = %d\n", i,
mt_gpufreq_power_limited_index_array[i]);
seq_printf(m, "_mt_gpufreq_get_cur_freq = %d\n", _mt_gpufreq_get_cur_freq());
seq_printf(m, "mt_gpufreq_volt_enable_state = %d\n", mt_gpufreq_volt_enable_state);
seq_printf(m, "mt_gpufreq_dvfs_table_type = %d\n", mt_gpufreq_dvfs_table_type);
seq_printf(m, "mt_gpufreq_ptpod_disable_idx = %d\n", mt_gpufreq_ptpod_disable_idx);
return 0;
}
/***************************
* show current voltage enable status
****************************/
static int mt_gpufreq_volt_enable_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_volt_enable)
seq_puts(m, "gpufreq voltage enabled\n");
else
seq_puts(m, "gpufreq voltage disabled\n");
return 0;
}
/***********************
* enable specific frequency
************************/
static ssize_t mt_gpufreq_volt_enable_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int enable = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &enable) == 0) {
if (enable == 0) {
mt_gpufreq_voltage_enable_set(0);
mt_gpufreq_volt_enable = false;
} else if (enable == 1) {
mt_gpufreq_voltage_enable_set(1);
mt_gpufreq_volt_enable = true;
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
return count;
}
/***************************
* show current specific frequency status
****************************/
static int mt_gpufreq_fixed_freq_volt_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_fixed_freq_volt_state) {
seq_puts(m, "gpufreq fixed frequency enabled\n");
seq_printf(m, "fixed frequency = %d\n", mt_gpufreq_fixed_frequency);
seq_printf(m, "fixed voltage = %d\n", mt_gpufreq_fixed_voltage);
} else
seq_puts(m, "gpufreq fixed frequency disabled\n");
return 0;
}
/***********************
* enable specific frequency
************************/
static void _mt_gpufreq_fixed_freq(int fixed_freq)
{
/* freq (KHz) */
if ((fixed_freq >= GPUFREQ_LAST_FREQ_LEVEL)
&& (fixed_freq <= GPU_DVFS_FREQ0)) {
gpufreq_dbg("@ %s, mt_gpufreq_clock_switch1 fix frq = %d, fix volt = %d, volt = %d\n",
__func__, mt_gpufreq_fixed_frequency, mt_gpufreq_fixed_voltage, g_cur_gpu_volt);
mt_gpufreq_fixed_freq_volt_state = true;
mt_gpufreq_fixed_frequency = fixed_freq;
mt_gpufreq_fixed_voltage = g_cur_gpu_volt;
mt_gpufreq_voltage_enable_set(1);
gpufreq_dbg("@ %s, mt_gpufreq_clock_switch2 fix frq = %d, fix volt = %d, volt = %d\n",
__func__, mt_gpufreq_fixed_frequency, mt_gpufreq_fixed_voltage, g_cur_gpu_volt);
mt_gpufreq_clock_switch(mt_gpufreq_fixed_frequency);
g_cur_gpu_freq = mt_gpufreq_fixed_frequency;
}
}
static void _mt_gpufreq_fixed_volt(int fixed_volt)
{
/* volt (mV) */
#ifdef VGPU_SET_BY_PMIC
if (fixed_volt >= (PMIC_MIN_VGPU / 100) &&
fixed_volt <= (PMIC_MAX_VGPU / 100)) {
#endif
gpufreq_dbg("@ %s, mt_gpufreq_volt_switch1 fix frq = %d, fix volt = %d, volt = %d\n",
__func__, mt_gpufreq_fixed_frequency, mt_gpufreq_fixed_voltage, g_cur_gpu_volt);
mt_gpufreq_fixed_freq_volt_state = true;
mt_gpufreq_fixed_frequency = g_cur_gpu_freq;
mt_gpufreq_fixed_voltage = fixed_volt * 100;
mt_gpufreq_voltage_enable_set(1);
gpufreq_dbg("@ %s, mt_gpufreq_volt_switch2 fix frq = %d, fix volt = %d, volt = %d\n",
__func__, mt_gpufreq_fixed_frequency, mt_gpufreq_fixed_voltage, g_cur_gpu_volt);
mt_gpufreq_volt_switch(g_cur_gpu_volt, mt_gpufreq_fixed_voltage);
g_cur_gpu_volt = mt_gpufreq_fixed_voltage;
}
}
static ssize_t mt_gpufreq_fixed_freq_volt_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int fixed_freq = 0;
int fixed_volt = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (sscanf(desc, "%d %d", &fixed_freq, &fixed_volt) == 2) {
if ((fixed_freq == 0) && (fixed_volt == 0)) {
mt_gpufreq_fixed_freq_volt_state = false;
mt_gpufreq_fixed_frequency = 0;
mt_gpufreq_fixed_voltage = 0;
#ifdef MTK_GPU_SPM
mtk_gpu_spm_reset_fix();
#endif
} else {
g_cur_gpu_freq = _mt_gpufreq_get_cur_freq();
#ifndef MTK_GPU_SPM
if (fixed_freq > g_cur_gpu_freq) {
_mt_gpufreq_fixed_volt(fixed_volt);
_mt_gpufreq_fixed_freq(fixed_freq);
} else {
_mt_gpufreq_fixed_freq(fixed_freq);
_mt_gpufreq_fixed_volt(fixed_volt);
}
#else
if (0) {
_mt_gpufreq_fixed_volt(fixed_volt);
_mt_gpufreq_fixed_freq(fixed_freq);
}
{
int i, found;
for (i = 0; i < mt_gpufreqs_num; i++) {
if (fixed_freq == mt_gpufreqs[i].gpufreq_khz) {
mt_gpufreq_keep_opp_index = i;
found = 1;
break;
}
}
mt_gpufreq_fixed_frequency = fixed_freq;
mt_gpufreq_fixed_voltage = fixed_volt * 100;
mtk_gpu_spm_fix_by_idx(mt_gpufreq_keep_opp_index);
}
#endif
}
} else
gpufreq_warn("bad argument!! should be [enable fixed_freq fixed_volt]\n");
return count;
}
#ifdef MT_GPUFREQ_INPUT_BOOST
/*****************************
* show current input boost status
******************************/
static int mt_gpufreq_input_boost_proc_show(struct seq_file *m, void *v)
{
if (mt_gpufreq_input_boost_state == 1)
seq_puts(m, "gpufreq input boost is enabled\n");
else
seq_puts(m, "gpufreq input boost is disabled\n");
return 0;
}
/***************************
* enable/disable input boost
****************************/
static ssize_t mt_gpufreq_input_boost_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int debug = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &debug) == 0) {
if (debug == 0)
mt_gpufreq_input_boost_state = 0;
else if (debug == 1)
mt_gpufreq_input_boost_state = 1;
else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
return count;
}
/***************************
* show lowpower frequency opp enable status
****************************/
static int mt_gpufreq_lpt_enable_proc_show(struct seq_file *m, void *v)
{
seq_puts(m, "not implemented\n");
return 0;
}
/***********************
* enable lowpower frequency opp
************************/
static ssize_t mt_gpufreq_lpt_enable_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
gpufreq_warn("not implemented\n");
#if 0
char desc[32];
int len = 0;
int enable = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 0, &enable) == 0) {
if (enable == 0)
mt_gpufreq_low_power_test_enable = false;
else if (enable == 1)
mt_gpufreq_low_power_test_enable = true;
else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
} else
gpufreq_warn("bad argument!! should be 0 or 1 [0: disable, 1: enable]\n");
#endif
return count;
}
#endif
#define PROC_FOPS_RW(name) \
static int mt_ ## name ## _proc_open(struct inode *inode, struct file *file) \
{ \
return single_open(file, mt_ ## name ## _proc_show, PDE_DATA(inode)); \
} \
static const struct file_operations mt_ ## name ## _proc_fops = { \
.owner = THIS_MODULE, \
.open = mt_ ## name ## _proc_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
.write = mt_ ## name ## _proc_write, \
}
#define PROC_FOPS_RO(name) \
static int mt_ ## name ## _proc_open(struct inode *inode, struct file *file) \
{ \
return single_open(file, mt_ ## name ## _proc_show, PDE_DATA(inode)); \
} \
static const struct file_operations mt_ ## name ## _proc_fops = { \
.owner = THIS_MODULE, \
.open = mt_ ## name ## _proc_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define PROC_ENTRY(name) {__stringify(name), &mt_ ## name ## _proc_fops}
PROC_FOPS_RW(gpufreq_debug);
PROC_FOPS_RW(gpufreq_limited_power);
#ifdef MT_GPUFREQ_OC_PROTECT
PROC_FOPS_RW(gpufreq_limited_oc_ignore);
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
PROC_FOPS_RW(gpufreq_limited_low_batt_volume_ignore);
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
PROC_FOPS_RW(gpufreq_limited_low_batt_volt_ignore);
#endif
PROC_FOPS_RW(gpufreq_limited_thermal_ignore);
#ifndef DISABLE_PBM_FEATURE
PROC_FOPS_RW(gpufreq_limited_pbm_ignore);
PROC_FOPS_RW(gpufreq_limited_by_pbm);
#endif
PROC_FOPS_RW(gpufreq_state);
PROC_FOPS_RO(gpufreq_opp_dump);
PROC_FOPS_RO(gpufreq_power_dump);
PROC_FOPS_RW(gpufreq_opp_freq);
PROC_FOPS_RW(gpufreq_opp_max_freq);
PROC_FOPS_RO(gpufreq_var_dump);
PROC_FOPS_RW(gpufreq_volt_enable);
PROC_FOPS_RW(gpufreq_fixed_freq_volt);
#ifdef MT_GPUFREQ_INPUT_BOOST
PROC_FOPS_RW(gpufreq_input_boost);
#endif
PROC_FOPS_RW(gpufreq_lpt_enable);
static int mt_gpufreq_create_procfs(void)
{
struct proc_dir_entry *dir = NULL;
int i;
struct pentry {
const char *name;
const struct file_operations *fops;
};
const struct pentry entries[] = {
PROC_ENTRY(gpufreq_debug),
PROC_ENTRY(gpufreq_limited_power),
#ifdef MT_GPUFREQ_OC_PROTECT
PROC_ENTRY(gpufreq_limited_oc_ignore),
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLUME_PROTECT
PROC_ENTRY(gpufreq_limited_low_batt_volume_ignore),
#endif
#ifdef MT_GPUFREQ_LOW_BATT_VOLT_PROTECT
PROC_ENTRY(gpufreq_limited_low_batt_volt_ignore),
#endif
PROC_ENTRY(gpufreq_limited_thermal_ignore),
#ifndef DISABLE_PBM_FEATURE
PROC_ENTRY(gpufreq_limited_pbm_ignore),
PROC_ENTRY(gpufreq_limited_by_pbm),
#endif
PROC_ENTRY(gpufreq_state),
PROC_ENTRY(gpufreq_opp_dump),
PROC_ENTRY(gpufreq_power_dump),
PROC_ENTRY(gpufreq_opp_freq),
PROC_ENTRY(gpufreq_opp_max_freq),
PROC_ENTRY(gpufreq_var_dump),
PROC_ENTRY(gpufreq_volt_enable),
PROC_ENTRY(gpufreq_fixed_freq_volt),
#ifdef MT_GPUFREQ_INPUT_BOOST
PROC_ENTRY(gpufreq_input_boost),
#endif
PROC_ENTRY(gpufreq_lpt_enable),
};
dir = proc_mkdir("gpufreq", NULL);
if (!dir) {
gpufreq_err("fail to create /proc/gpufreq @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++) {
if (!proc_create
(entries[i].name, S_IRUGO | S_IWUSR | S_IWGRP, dir, entries[i].fops))
gpufreq_err("@%s: create /proc/gpufreq/%s failed\n", __func__,
entries[i].name);
}
return 0;
}
#endif /* CONFIG_PROC_FS */
/**********************************
* mediatek gpufreq initialization
***********************************/
static int __init mt_gpufreq_init(void)
{
int ret = 0;
#ifdef BRING_UP
/* Skip driver init in bring up stage */
return 0;
#endif
gpufreq_info("@%s\n", __func__);
#ifdef CONFIG_PROC_FS
/* init proc */
if (mt_gpufreq_create_procfs())
goto out;
#endif /* CONFIG_PROC_FS */
/* register platform device/driver */
#if !defined(CONFIG_OF)
ret = platform_device_register(&mt_gpufreq_pdev);
if (ret) {
gpufreq_err("fail to register gpufreq device @ %s()\n", __func__);
goto out;
}
#endif
ret = platform_driver_register(&mt_gpufreq_pdrv);
if (ret) {
gpufreq_err("fail to register gpufreq driver @ %s()\n", __func__);
#if !defined(CONFIG_OF)
platform_device_unregister(&mt_gpufreq_pdev);
#endif
}
out:
return ret;
}
static void __exit mt_gpufreq_exit(void)
{
platform_driver_unregister(&mt_gpufreq_pdrv);
#if !defined(CONFIG_OF)
platform_device_unregister(&mt_gpufreq_pdev);
#endif
}
module_init(mt_gpufreq_init);
module_exit(mt_gpufreq_exit);
MODULE_DESCRIPTION("MediaTek GPU Frequency Scaling driver");
MODULE_LICENSE("GPL");