mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2025-02-15 00:18:03 +00:00
3123 lines
79 KiB
C
3123 lines
79 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
/**
|
|
* @file mtk_eem.
|
|
* @brief Driver for EEM
|
|
*
|
|
*/
|
|
|
|
#define __MTK_EEMG_C__
|
|
/*=============================================================
|
|
* Include files
|
|
*=============================================================
|
|
*/
|
|
|
|
/* system includes */
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/hrtimer.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/syscore_ops.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/types.h>
|
|
#include <linux/time.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/thermal.h>
|
|
#include <linux/nvmem-consumer.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <mt-plat/aee.h>
|
|
#include "mtk_eemgpu_config.h"
|
|
#include "mtk_eemgpu.h"
|
|
#include "mtk_defeemgpu.h"
|
|
#include "mtk_eemgpu_internal_ap.h"
|
|
#include "mtk_eemgpu_internal.h"
|
|
#if IS_ENABLED(CONFIG_MTK_GPU_SUPPORT)
|
|
#include "mtk_gpufreq.h"
|
|
#endif
|
|
#include <regulator/consumer.h>
|
|
|
|
#define INFRA_EEMG_RST (infra_base_gpu + 0x150)
|
|
#define INFRA_EEMG_CLR (infra_base_gpu + 0x154)
|
|
#define LVTS_COEFF_A_X_1000 (-250460)
|
|
#define LVTS_COEFF_B_X_1000 (250460)
|
|
#define DEFAULT_EFUSE_GOLDEN_TEMP (50)
|
|
#define WAIT_TIME (2500000)
|
|
|
|
enum thermal_bank_name {
|
|
THERMAL_BANK0 = 0,
|
|
THERMAL_BANK1,
|
|
THERMAL_BANK2,
|
|
THERMAL_BANK3,
|
|
THERMAL_BANK4,
|
|
THERMAL_BANK5,
|
|
THERMAL_BANK_NUM
|
|
};
|
|
|
|
struct TS_PTPOD {
|
|
unsigned int ts_MTS;
|
|
unsigned int ts_BTS;
|
|
};
|
|
|
|
static unsigned int ctrl_EEMG_Enable = 1;
|
|
static unsigned int gpu_2line;
|
|
static unsigned long long eemg_pTime_us, eemg_cTime_us, eemg_diff_us;
|
|
static unsigned int final_init01_flag;
|
|
unsigned int gpu_final_init02_flag;
|
|
unsigned int gpu_cur_init02_flag;
|
|
static unsigned int golden_temp;
|
|
static struct eemg_devinfo eemg_devinfo;
|
|
static struct hrtimer eemg_log_timer;
|
|
static DEFINE_SPINLOCK(eemg_spinlock);
|
|
static int eemg_log_en;
|
|
static unsigned int eemg_checkEfuse = 1;
|
|
void __iomem *eemg_base;
|
|
void __iomem *infra_base_gpu;
|
|
static u32 eemg_irq_number;
|
|
|
|
unsigned int gpu_vb;
|
|
unsigned int gpu_vb_volt;
|
|
unsigned int gpu_vb_turn_pt;
|
|
|
|
DEFINE_MUTEX(gpu_mutex_g);
|
|
|
|
static int create_procfs(struct platform_device *pdev);
|
|
static void eemg_set_eemg_volt(struct eemg_det *det);
|
|
static void eemg_restore_eemg_volt(struct eemg_det *det);
|
|
static unsigned int interpolate(unsigned int y1, unsigned int y0,
|
|
unsigned int x1, unsigned int x0, unsigned int ym);
|
|
static void eemg_fill_freq_table(struct eemg_det *det);
|
|
static void read_volt_from_VOP(struct eemg_det *det);
|
|
|
|
static struct eemg_det *id_to_eemg_det(enum eemg_det_id id)
|
|
{
|
|
if (likely(id < NR_EEMG_DET))
|
|
return &eemg_detectors[id];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static int get_devinfo(struct platform_device *pdev)
|
|
{
|
|
int ret = 1, i = 0;
|
|
int *val;
|
|
unsigned int safeEfuse = 0;
|
|
struct nvmem_device *nvmem_dev;
|
|
//struct device_node *node = pdev->dev.of_node;
|
|
struct device_node *node;
|
|
int devinfo = 0, efuse = 0, efuse_mask = 0;
|
|
int count = 0;
|
|
int mcl50 = 0;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
val = (int *)&eemg_devinfo;
|
|
|
|
nvmem_dev = nvmem_device_get(&pdev->dev, "mtk_efuse");
|
|
if (IS_ERR(nvmem_dev))
|
|
return -ENODEV;
|
|
|
|
node = of_find_compatible_node(NULL, NULL, "mediatek,cpufreq-hw");
|
|
if (node) {
|
|
ret = of_property_read_u32(node, "mcl50_load", &mcl50);
|
|
if (!ret && mcl50)
|
|
mcl50 = 1;
|
|
}
|
|
|
|
node = pdev->dev.of_node;
|
|
ret = of_property_read_u32(node, "gpu-vb", &efuse);
|
|
if (efuse == 1) {
|
|
gpu_vb = 1;
|
|
of_property_read_u32_index(node, "gpu-vb-efuse", 0, &efuse);
|
|
of_property_read_u32_index(node, "gpu-vb-efuse", 1,
|
|
&efuse_mask);
|
|
nvmem_device_read(nvmem_dev, efuse, sizeof(__u32), &devinfo);
|
|
of_property_read_u32_index(node, "gpu-vb-opp0",
|
|
(devinfo & efuse_mask) >> find_first_bit(
|
|
(unsigned long *)&efuse_mask, 32), &gpu_vb_volt);
|
|
if (mcl50) {
|
|
ret = of_property_read_u32(node, "gpu-vb-opp0-mcl50",
|
|
&gpu_vb_volt);
|
|
eemg_error("mc50 load setting\n");
|
|
}
|
|
} else
|
|
gpu_vb = 0;
|
|
|
|
count = of_property_count_u32_elems(node, "devinfo-idx");
|
|
|
|
ret = of_property_read_u32(node, "devinfo-fake-ignore", &efuse_mask);
|
|
|
|
for (i = 0; i < count ; i++) {
|
|
of_property_read_u32_index(node, "devinfo-idx", i, &efuse);
|
|
nvmem_device_read(nvmem_dev, efuse, sizeof(__u32), &devinfo);
|
|
val[i] = devinfo;
|
|
if (efuse_mask & BIT(i))
|
|
continue;
|
|
else if (val[i] == 0) {
|
|
eemg_error("No EFUSE (val[%d]), use safe efuse\n", i);
|
|
safeEfuse = 1;
|
|
}
|
|
}
|
|
|
|
if (safeEfuse) {
|
|
for (i = 0; i < count ; i++) {
|
|
of_property_read_u32_index(node, "devinfo", i, &efuse);
|
|
val[i] = efuse;
|
|
eemg_error("[PTP_DUMP] RES%d: 0x%X\n", i, val[i]);
|
|
}
|
|
} else {
|
|
for (i = 0; i < count ; i++)
|
|
eemg_error("[PTP_DUMP] RES%d: 0x%X\n", i, val[i]);
|
|
}
|
|
|
|
|
|
if (mcl50) {
|
|
for (i = 0; i < count ; i++) {
|
|
of_property_read_u32_index(node, "devinfo-mcl50", i, &efuse);
|
|
val[i] = efuse;
|
|
eemg_error("[PTP_DUMP] RES%d: 0x%X\n", i, val[i]);
|
|
}
|
|
|
|
}
|
|
|
|
gpu_2line = 1;
|
|
|
|
ret = of_property_read_u32(node, "golden-temp", &efuse);
|
|
|
|
nvmem_device_read(nvmem_dev, efuse, sizeof(__u32), &devinfo);
|
|
|
|
ret = of_property_read_u32(node, "golden-temp-mask", &efuse);
|
|
|
|
golden_temp = ((devinfo & efuse)) >> find_first_bit((unsigned long *)&efuse, 32);
|
|
|
|
if (golden_temp != 0) {
|
|
ret = of_property_read_u32(node, "golden-temp-default", &efuse);
|
|
golden_temp = efuse;
|
|
}
|
|
ret = 1;
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return ret;
|
|
}
|
|
|
|
void get_lvts_slope_intercept(struct TS_PTPOD *ts_info)
|
|
{
|
|
struct TS_PTPOD ts_ptpod;
|
|
int temp;
|
|
|
|
/* chip dependent */
|
|
temp = (0 - LVTS_COEFF_A_X_1000) * 2;
|
|
temp /= 1000;
|
|
ts_ptpod.ts_MTS = temp;
|
|
temp = 500 * golden_temp + LVTS_COEFF_B_X_1000;
|
|
temp /= 1000;
|
|
ts_ptpod.ts_BTS = (temp - 25) * 4;
|
|
ts_info->ts_MTS = ts_ptpod.ts_MTS;
|
|
ts_info->ts_BTS = ts_ptpod.ts_BTS;
|
|
}
|
|
|
|
/*============================================================
|
|
* function declarations of EEM detectors
|
|
*============================================================
|
|
*/
|
|
static void mt_ptpgpu_lock(unsigned long *flags);
|
|
static void mt_ptpgpu_unlock(unsigned long *flags);
|
|
|
|
/*=============================================================
|
|
* Local function definition
|
|
*=============================================================
|
|
*/
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
static void _mt_eemg_aee_init(void)
|
|
{
|
|
aee_rr_rec_ptp_vboot(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_gpu_volt(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_gpu_volt_1(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_gpu_volt_2(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_gpu_volt_3(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_temp(0xFFFFFFFFFFFFFFFF);
|
|
aee_rr_rec_ptp_status(0xFF);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
/* common part in thermal */
|
|
int tscpu_get_temp_by_bank(enum thermal_bank_name ts_bank)
|
|
{
|
|
struct thermal_zone_device *zone;
|
|
char cell_name[12];
|
|
int i, ret, temperature = 0, total = 0;
|
|
|
|
switch (ts_bank) {
|
|
case THERMAL_BANK0:
|
|
for (i = 0; i < 4; i++) {
|
|
memset(cell_name, '\0', 12);
|
|
ret = snprintf(cell_name, 9, "cpu_big%d", i + 1);
|
|
if (ret <= 0)
|
|
return EEM_LOW_T;
|
|
zone = thermal_zone_get_zone_by_name(cell_name);
|
|
if (IS_ERR(zone))
|
|
return EEM_LOW_T;
|
|
ret = thermal_zone_get_temp(zone, &temperature);
|
|
if (ret != 0)
|
|
return EEM_LOW_T;
|
|
total += temperature;
|
|
}
|
|
total /= 4;
|
|
break;
|
|
case THERMAL_BANK1:
|
|
return EEM_LOW_T;
|
|
case THERMAL_BANK2:
|
|
for (i = 0; i < 2; i++) {
|
|
memset(cell_name, '\0', 12);
|
|
ret = snprintf(cell_name, 12, "cpu_little%d", i + 1);
|
|
if (ret <= 0)
|
|
return EEM_LOW_T;
|
|
zone = thermal_zone_get_zone_by_name(cell_name);
|
|
if (IS_ERR(zone))
|
|
return EEM_LOW_T;
|
|
ret = thermal_zone_get_temp(zone, &temperature);
|
|
if (ret != 0)
|
|
return EEM_LOW_T;
|
|
total += temperature;
|
|
}
|
|
total /= 2;
|
|
break;
|
|
case THERMAL_BANK3:
|
|
break;
|
|
case THERMAL_BANK4:
|
|
for (i = 0; i < 2; i++) {
|
|
memset(cell_name, '\0', 12);
|
|
ret = snprintf(cell_name, 5, "gpu%d", i + 1);
|
|
if (ret <= 0)
|
|
return EEM_LOW_T;
|
|
zone = thermal_zone_get_zone_by_name(cell_name);
|
|
if (IS_ERR(zone))
|
|
return EEM_LOW_T;
|
|
ret = thermal_zone_get_temp(zone, &temperature);
|
|
if (ret != 0)
|
|
return EEM_LOW_T;
|
|
total += temperature;
|
|
}
|
|
total /= 2;
|
|
break;
|
|
default:
|
|
/* un-handled cases */
|
|
break;
|
|
}
|
|
return total;
|
|
}
|
|
#endif
|
|
|
|
|
|
static struct eemg_ctrl *id_to_eemg_ctrl(enum eemg_ctrl_id id)
|
|
{
|
|
if (likely(id < NR_EEMG_CTRL))
|
|
return &eemg_ctrls[id];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void base_ops_enable_gpu(struct eemg_det *det, int reason)
|
|
{
|
|
/* FIXME: UNDER CONSTRUCTION */
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
det->disabled &= ~reason;
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
void base_ops_switch_bank_gpu(struct eemg_det *det, enum eemg_phase phase)
|
|
{
|
|
unsigned int coresel;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
coresel = (eemg_read(EEMGCORESEL) & ~BITMASK(2:0))
|
|
| BITS(2:0, det->ctrl_id);
|
|
|
|
/* 803f0000 + det->ctrl_id = enable ctrl's swcg clock */
|
|
/* 003f0000 + det->ctrl_id = disable ctrl's swcg clock */
|
|
/* bug: when system resume, need to restore coresel value */
|
|
if (phase == EEMG_PHASE_INIT01) {
|
|
coresel |= CORESEL_VAL;
|
|
} else {
|
|
coresel |= CORESEL_INIT2_VAL;
|
|
#if defined(CFG_THERM_LVTS) && CFG_LVTS_DOMINATOR
|
|
coresel &= 0x0fffffff;
|
|
#else
|
|
coresel &= 0x0ffffeff; /* get temp from AUXADC */
|
|
#endif
|
|
}
|
|
eemg_write(EEMGCORESEL, coresel);
|
|
|
|
if ((eemg_read(EEMGCORESEL) & 0x7) != (coresel & 0x7)) {
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d, EEMGCORESEL %x != %x\n",
|
|
__func__,
|
|
__LINE__,
|
|
eemg_read(EEMGCORESEL),
|
|
coresel);
|
|
#endif
|
|
WARN_ON(eemg_read(EEMGCORESEL) != coresel);
|
|
}
|
|
|
|
eemg_debug("[%s] 0x1100bf00=0x%x\n",
|
|
((char *)(det->name) + 8), eemg_read(EEMGCORESEL));
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
void base_ops_disable_locked_gpu(struct eemg_det *det, int reason)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
switch (reason) {
|
|
case BY_MON_ERROR: /* 4 */
|
|
case BY_INIT_ERROR: /* 2 */
|
|
/* disable EEM */
|
|
eemg_write(EEMGEN, 0x0 | SEC_MOD_SEL);
|
|
|
|
/* Clear EEM interrupt EEMGINTSTS */
|
|
eemg_write(EEMGINTSTS, 0x00ffffff);
|
|
|
|
case BY_PROCFS: /* 1 */
|
|
det->disabled |= reason;
|
|
eemg_restore_eemg_volt(det);
|
|
break;
|
|
|
|
default:
|
|
eemg_debug("det->disabled=%x\n", det->disabled);
|
|
det->disabled &= ~BY_PROCFS;
|
|
eemg_debug("det->disabled=%x\n", det->disabled);
|
|
eemg_set_eemg_volt(det);
|
|
break;
|
|
}
|
|
|
|
eemg_debug("Disable EEM[%s] done. reason=[%d]\n",
|
|
det->name, det->disabled);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
void base_ops_disable_gpu(struct eemg_det *det, int reason)
|
|
{
|
|
unsigned long flags;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
mt_ptpgpu_lock(&flags);
|
|
det->ops->switch_bank_gpu(det, NR_EEMG_PHASE);
|
|
det->ops->disable_locked_gpu(det, reason);
|
|
mt_ptpgpu_unlock(&flags);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
int base_ops_init01_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (unlikely(!HAS_FEATURE(det, FEA_INIT01))) {
|
|
eemg_debug("det %s has no INIT01\n", det->name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -1;
|
|
}
|
|
|
|
det->ops->set_phase_gpu(det, EEMG_PHASE_INIT01);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int base_ops_init02_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (unlikely(!HAS_FEATURE(det, FEA_INIT02))) {
|
|
eemg_debug("det %s has no INIT02\n", det->name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -1;
|
|
}
|
|
|
|
if (det->disabled & BY_INIT_ERROR) {
|
|
eemg_error("[%s] Disabled by INIT_ERROR\n",
|
|
((char *)(det->name) + 8));
|
|
det->ops->dump_status_gpu(det);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -2;
|
|
}
|
|
/* eemg_debug("DCV = 0x%08X, AGEV = 0x%08X\n",
|
|
* det->DCVOFFSETIN, det->AGEVOFFSETIN);
|
|
*/
|
|
|
|
/* det->ops->dump_status(det); */
|
|
det->ops->set_phase_gpu(det, EEMG_PHASE_INIT02);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int base_ops_mon_mode_gpu(struct eemg_det *det)
|
|
{
|
|
struct TS_PTPOD ts_info;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
if (!HAS_FEATURE(det, FEA_MON)) {
|
|
eemg_debug("det %s has no MON mode\n", det->name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -1;
|
|
}
|
|
|
|
if (det->disabled & BY_INIT_ERROR) {
|
|
eemg_error("[%s] Disabled BY_INIT_ERROR\n",
|
|
((char *)(det->name) + 8));
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -2;
|
|
}
|
|
det->MTS = MTS_VAL;
|
|
det->BTS = BTS_VAL;
|
|
get_lvts_slope_intercept(&ts_info);
|
|
det->MTS = ts_info.ts_MTS;
|
|
det->BTS = ts_info.ts_BTS;
|
|
/*
|
|
* eemg_debug("[base_ops_mon_mode_gpu] Bk = %d,
|
|
* MTS = 0x%08X, BTS = 0x%08X\n",
|
|
* det->ctrl_id, det->MTS, det->BTS);
|
|
*/
|
|
/* det->ops->dump_status(det); */
|
|
det->ops->set_phase_gpu(det, EEMG_PHASE_MON);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int base_ops_get_status_gpu(struct eemg_det *det)
|
|
{
|
|
int status;
|
|
unsigned long flags;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
mt_ptpgpu_lock(&flags);
|
|
det->ops->switch_bank_gpu(det, NR_EEMG_PHASE);
|
|
status = (eemg_read(EEMGEN) != 0) ? 1 : 0;
|
|
mt_ptpgpu_unlock(&flags);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return status;
|
|
}
|
|
|
|
void base_ops_dump_status_gpu(struct eemg_det *det)
|
|
{
|
|
unsigned int i;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
eemg_isr_info("[%s]\n", det->name);
|
|
|
|
eemg_isr_info("EEMINITEN = 0x%08X\n", det->EEMINITEN);
|
|
eemg_isr_info("EEMMONEN = 0x%08X\n", det->EEMMONEN);
|
|
eemg_isr_info("MDES = 0x%08X\n", det->MDES);
|
|
eemg_isr_info("BDES = 0x%08X\n", det->BDES);
|
|
eemg_isr_info("DCMDET = 0x%08X\n", det->DCMDET);
|
|
|
|
eemg_isr_info("DCCONFIG = 0x%08X\n", det->DCCONFIG);
|
|
eemg_isr_info("DCBDET = 0x%08X\n", det->DCBDET);
|
|
|
|
eemg_isr_info("AGECONFIG = 0x%08X\n", det->AGECONFIG);
|
|
eemg_isr_info("AGEM = 0x%08X\n", det->AGEM);
|
|
|
|
eemg_isr_info("AGEDELTA = 0x%08X\n", det->AGEDELTA);
|
|
eemg_isr_info("DVTFIXED = 0x%08X\n", det->DVTFIXED);
|
|
eemg_isr_info("MTDES = 0x%08X\n", det->MTDES);
|
|
eemg_isr_info("VCO = 0x%08X\n", det->VCO);
|
|
|
|
eemg_isr_info("DETWINDOW = 0x%08X\n", det->DETWINDOW);
|
|
eemg_isr_info("VMAX = 0x%08X\n", det->VMAX);
|
|
eemg_isr_info("VMIN = 0x%08X\n", det->VMIN);
|
|
eemg_isr_info("DTHI = 0x%08X\n", det->DTHI);
|
|
eemg_isr_info("DTLO = 0x%08X\n", det->DTLO);
|
|
eemg_isr_info("VBOOT = 0x%08X\n", det->VBOOT);
|
|
eemg_isr_info("DETMAX = 0x%08X\n", det->DETMAX);
|
|
|
|
eemg_isr_info("DCVOFFSETIN = 0x%08X\n", det->DCVOFFSETIN);
|
|
eemg_isr_info("AGEVOFFSETIN = 0x%08X\n", det->AGEVOFFSETIN);
|
|
|
|
eemg_isr_info("MTS = 0x%08X\n", det->MTS);
|
|
eemg_isr_info("BTS = 0x%08X\n", det->BTS);
|
|
|
|
eemg_isr_info("num_freq_tbl = %d\n", det->num_freq_tbl);
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
eemg_isr_info("freq_tbl[%d] = %d\n",
|
|
i, det->freq_tbl[i]);
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
eemg_isr_info("volt_tbl[%d] = %d\n",
|
|
i, det->volt_tbl[i]);
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
eemg_isr_info("volt_tbl_init2[%d] = %d\n",
|
|
i, det->volt_tbl_init2[i]);
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
eemg_isr_info("volt_tbl_pmic[%d] = %d\n", i,
|
|
det->volt_tbl_pmic[i]);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
|
|
void dump_register_gpu(void)
|
|
{
|
|
|
|
eemg_debug("EEMG_DESCHAR = 0x%x\n", eemg_read(EEMG_DESCHAR));
|
|
eemg_debug("EEMG_TEMPCHAR = 0x%x\n", eemg_read(EEMG_TEMPCHAR));
|
|
eemg_debug("EEMG_DETCHAR = 0x%x\n", eemg_read(EEMG_DETCHAR));
|
|
eemg_debug("EEMG_AGECHAR = 0x%x\n", eemg_read(EEMG_AGECHAR));
|
|
eemg_debug("EEMG_DCCONFIG = 0x%x\n", eemg_read(EEMG_DCCONFIG));
|
|
eemg_debug("EEMG_AGECONFIG = 0x%x\n", eemg_read(EEMG_AGECONFIG));
|
|
eemg_debug("EEMG_FREQPCT30 = 0x%x\n", eemg_read(EEMG_FREQPCT30));
|
|
eemg_debug("EEMG_FREQPCT74 = 0x%x\n", eemg_read(EEMG_FREQPCT74));
|
|
eemg_debug("EEMG_LIMITVALS = 0x%x\n", eemg_read(EEMG_LIMITVALS));
|
|
eemg_debug("EEMG_VBOOT = 0x%x\n", eemg_read(EEMG_VBOOT));
|
|
eemg_debug("EEMG_DETWINDOW = 0x%x\n", eemg_read(EEMG_DETWINDOW));
|
|
eemg_debug("EEMGCONFIG = 0x%x\n", eemg_read(EEMGCONFIG));
|
|
eemg_debug("EEMG_TSCALCS = 0x%x\n", eemg_read(EEMG_TSCALCS));
|
|
eemg_debug("EEMG_RUNCONFIG = 0x%x\n", eemg_read(EEMG_RUNCONFIG));
|
|
eemg_debug("EEMGEN = 0x%x\n", eemg_read(EEMGEN));
|
|
eemg_debug("EEMG_INIT2VALS = 0x%x\n", eemg_read(EEMG_INIT2VALS));
|
|
eemg_debug("EEMG_DCVALUES = 0x%x\n", eemg_read(EEMG_DCVALUES));
|
|
eemg_debug("EEMG_AGEVALUES = 0x%x\n", eemg_read(EEMG_AGEVALUES));
|
|
eemg_debug("EEMG_VOP30 = 0x%x\n", eemg_read(EEMG_VOP30));
|
|
eemg_debug("EEMG_VOP74 = 0x%x\n", eemg_read(EEMG_VOP74));
|
|
eemg_debug("TEMP = 0x%x\n", eemg_read(TEMPG));
|
|
eemg_debug("EEMGINTSTS = 0x%x\n", eemg_read(EEMGINTSTS));
|
|
eemg_debug("EEMGINTSTSRAW = 0x%x\n", eemg_read(EEMGINTSTSRAW));
|
|
eemg_debug("EEMGINTEN = 0x%x\n", eemg_read(EEMGINTEN));
|
|
eemg_debug("EEMG_CHKSHIFT = 0x%x\n", eemg_read(EEMG_CHKSHIFT));
|
|
eemg_debug("EEMG_VDESIGN30 = 0x%x\n", eemg_read(EEMG_VDESIGN30));
|
|
eemg_debug("EEMG_VDESIGN74 = 0x%x\n", eemg_read(EEMG_VDESIGN74));
|
|
eemg_debug("EEMG_AGECOUNT = 0x%x\n", eemg_read(EEMG_AGECOUNT));
|
|
eemg_debug("EEMG_SMSTATE0 = 0x%x\n", eemg_read(EEMG_SMSTATE0));
|
|
eemg_debug("EEMG_SMSTATE1 = 0x%x\n", eemg_read(EEMG_SMSTATE1));
|
|
eemg_debug("EEMG_CTL0 = 0x%x\n", eemg_read(EEMG_CTL0));
|
|
eemg_debug("EEMGCORESEL = 0x%x\n", eemg_read(EEMGCORESEL));
|
|
eemg_debug("EEMG_THERMINTST = 0x%x\n", eemg_read(EEMG_THERMINTST));
|
|
eemg_debug("EEMGODINTST = 0x%x\n", eemg_read(EEMGODINTST));
|
|
eemg_debug("EEMG_THSTAGE0ST = 0x%x\n", eemg_read(EEMG_THSTAGE0ST));
|
|
eemg_debug("EEMG_THSTAGE1ST = 0x%x\n", eemg_read(EEMG_THSTAGE1ST));
|
|
eemg_debug("EEMG_THSTAGE2ST = 0x%x\n", eemg_read(EEMG_THSTAGE2ST));
|
|
eemg_debug("EEMG_THAHBST0 = 0x%x\n", eemg_read(EEMG_THAHBST0));
|
|
eemg_debug("EEMG_THAHBST1 = 0x%x\n", eemg_read(EEMG_THAHBST1));
|
|
eemg_debug("EEMGSPARE0 = 0x%x\n", eemg_read(EEMGSPARE0));
|
|
eemg_debug("EEMGSPARE1 = 0x%x\n", eemg_read(EEMGSPARE1));
|
|
eemg_debug("EEMGSPARE2 = 0x%x\n", eemg_read(EEMGSPARE2));
|
|
eemg_debug("EEMGSPARE3 = 0x%x\n", eemg_read(EEMGSPARE3));
|
|
eemg_debug("EEMG_THSLPEVEB = 0x%x\n", eemg_read(EEMG_THSLPEVEB));
|
|
eemg_debug("EEMG_TEMP = 0x%x\n", eemg_read(TEMPG));
|
|
eemg_debug("EEMG_THERMAL = 0x%x\n", eemg_read(EEMG_THERMAL));
|
|
|
|
}
|
|
|
|
void base_ops_set_phase_gpu(struct eemg_det *det, enum eemg_phase phase)
|
|
{
|
|
unsigned int i, filter, val;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
det->ops->switch_bank_gpu(det, phase);
|
|
|
|
/* config EEM register */
|
|
eemg_write(EEMG_DESCHAR,
|
|
((det->BDES << 8) & 0xff00) | (det->MDES & 0xff));
|
|
eemg_write(EEMG_TEMPCHAR,
|
|
(((det->VCO << 16) & 0xff0000) |
|
|
((det->MTDES << 8) & 0xff00) | (det->DVTFIXED & 0xff)));
|
|
eemg_write(EEMG_DETCHAR,
|
|
((det->DCBDET << 8) & 0xff00) | (det->DCMDET & 0xff));
|
|
|
|
eemg_write(EEMG_DCCONFIG, det->DCCONFIG);
|
|
eemg_write(EEMG_AGECONFIG, det->AGECONFIG);
|
|
|
|
if (phase == EEMG_PHASE_MON)
|
|
eemg_write(EEMG_TSCALCS,
|
|
((det->BTS << 12) & 0xfff000) | (det->MTS & 0xfff));
|
|
|
|
if (det->AGEM == 0x0)
|
|
eemg_write(EEMG_RUNCONFIG, 0x80000000);
|
|
else {
|
|
val = 0x0;
|
|
|
|
for (i = 0; i < 24; i += 2) {
|
|
filter = 0x3 << i;
|
|
|
|
if (((det->AGECONFIG) & filter) == 0x0)
|
|
val |= (0x1 << i);
|
|
else
|
|
val |= ((det->AGECONFIG) & filter);
|
|
}
|
|
eemg_write(EEMG_RUNCONFIG, val);
|
|
}
|
|
|
|
eemg_fill_freq_table(det);
|
|
|
|
eemg_write(EEMG_LIMITVALS,
|
|
((det->VMAX << 24) & 0xff000000) |
|
|
((det->VMIN << 16) & 0xff0000) |
|
|
((det->DTHI << 8) & 0xff00) |
|
|
(det->DTLO & 0xff));
|
|
/* eemg_write(EEMG_LIMITVALS, 0xFF0001FE); */
|
|
eemg_write(EEMG_VBOOT, (((det->VBOOT) & 0xff)));
|
|
eemg_write(EEMG_DETWINDOW, (((det->DETWINDOW) & 0xffff)));
|
|
eemg_write(EEMGCONFIG, (((det->DETMAX) & 0xffff)));
|
|
|
|
|
|
eemg_write(EEMG_CHKSHIFT, 0x87);
|
|
if (eemg_read(EEMG_CHKSHIFT) != 0x87) {
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d, EEMG_CHKSHIFT %x\n",
|
|
__func__,
|
|
__LINE__,
|
|
eemg_read(EEMG_CHKSHIFT));
|
|
#endif
|
|
WARN_ON(eemg_read(EEMG_CHKSHIFT) != 0x87);
|
|
}
|
|
|
|
/* eem ctrl choose thermal sensors */
|
|
eemg_write(EEMG_CTL0, det->EEMCTL0);
|
|
/* clear all pending EEM interrupt & config EEMGINTEN */
|
|
eemg_write(EEMGINTSTS, 0xffffffff);
|
|
|
|
/* work around set thermal register
|
|
* eemg_write(EEMG_THERMAL, 0x200);
|
|
*/
|
|
|
|
eemg_debug(" %s set phase = %d\n", ((char *)(det->name) + 8), phase);
|
|
switch (phase) {
|
|
case EEMG_PHASE_INIT01:
|
|
eemg_debug("EEMG_SET_PHASE01\n ");
|
|
eemg_write(EEMGINTEN, 0x00005f01);
|
|
/* enable EEM INIT measurement */
|
|
eemg_write(EEMGEN, 0x00000001 | SEC_MOD_SEL);
|
|
if (eemg_read(EEMGEN) != (0x00000001 | SEC_MOD_SEL)) {
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d, EEMGEN %x\n",
|
|
__func__,
|
|
__LINE__,
|
|
eemg_read(EEMGEN));
|
|
#endif
|
|
WARN_ON(eemg_read(EEMGEN) !=
|
|
(0x00000001 | SEC_MOD_SEL));
|
|
}
|
|
eemg_debug("EEMGEN = 0x%x, EEMGINTEN = 0x%x\n",
|
|
eemg_read(EEMGEN), eemg_read(EEMGINTEN));
|
|
//dump_register_gpu();
|
|
udelay(250); /* all banks' phase cannot be set without delay */
|
|
break;
|
|
|
|
case EEMG_PHASE_INIT02:
|
|
/* check if DCVALUES is minus and set DCVOFFSETIN to zero */
|
|
// if ((det->DCVOFFSETIN & 0x8000) || (eemg_devinfo.FT_PGM == 0))
|
|
det->DCVOFFSETIN = 0;
|
|
eemg_debug("EEMG_SET_PHASE02\n ");
|
|
eemg_write(EEMGINTEN, 0x00005f01);
|
|
|
|
eemg_write(EEMG_INIT2VALS,
|
|
((det->AGEVOFFSETIN << 16) & 0xffff0000) |
|
|
(det->DCVOFFSETIN & 0xffff));
|
|
|
|
/* enable EEM INIT measurement */
|
|
eemg_write(EEMGEN, 0x00000005 | SEC_MOD_SEL);
|
|
if (eemg_read(EEMGEN) != (0x00000005 | SEC_MOD_SEL)) {
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d, EEMGEN %x\n",
|
|
__func__,
|
|
__LINE__,
|
|
eemg_read(EEMGEN));
|
|
#endif
|
|
WARN_ON(eemg_read(EEMGEN) !=
|
|
(0x00000005 | SEC_MOD_SEL));
|
|
}
|
|
|
|
//dump_register_gpu();
|
|
udelay(200); /* all banks' phase cannot be set without delay */
|
|
break;
|
|
|
|
case EEMG_PHASE_MON:
|
|
eemg_debug("EEMG_SET_PHASE_MON\n ");
|
|
eemg_write(EEMGINTEN, 0x00FF0000);
|
|
/* enable EEM monitor mode */
|
|
eemg_write(EEMGEN, 0x00000002 | SEC_MOD_SEL);
|
|
/* dump_register_gpu(); */
|
|
break;
|
|
|
|
default:
|
|
WARN_ON(1); /*BUG()*/
|
|
break;
|
|
}
|
|
/* mt_ptpgpu_unlock(&flags); */
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
int base_ops_get_temp_gpu(struct eemg_det *det)
|
|
{
|
|
#ifdef CONFIG_THERMAL
|
|
enum thermal_bank_name ts_bank;
|
|
|
|
if (det_to_id(det) == EEMG_DET_GPU)
|
|
ts_bank = THERMAL_BANK4;
|
|
else if (det_to_id(det) == EEMG_DET_GPU_HI)
|
|
ts_bank = THERMAL_BANK4;
|
|
else
|
|
ts_bank = THERMAL_BANK4;
|
|
return tscpu_get_temp_by_bank(ts_bank);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int base_ops_get_volt_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
eemg_debug("[%s] default func\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int base_ops_set_volt_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
eemg_debug("[%s] default func\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void base_ops_restore_default_volt_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
eemg_debug("[%s] default func\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
void base_ops_get_freq_table_gpu(struct eemg_det *det, unsigned int gpu_freq_base,
|
|
unsigned int gpu_m_freq_base)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
det->freq_tbl[0] = 100;
|
|
det->num_freq_tbl = 1;
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
void base_ops_get_orig_volt_table_gpu(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
static long long eemg_get_current_time_us(void)
|
|
{
|
|
return ktime_to_us(ktime_get());
|
|
}
|
|
|
|
/*=============================================================
|
|
* Global function definition
|
|
*=============================================================
|
|
*/
|
|
static void mt_ptpgpu_lock(unsigned long *flags)
|
|
{
|
|
spin_lock_irqsave(&eemg_spinlock, *flags);
|
|
eemg_pTime_us = eemg_get_current_time_us();
|
|
|
|
}
|
|
|
|
static void mt_ptpgpu_unlock(unsigned long *flags)
|
|
{
|
|
eemg_cTime_us = eemg_get_current_time_us();
|
|
EEMG_IS_TOO_LONG();
|
|
spin_unlock_irqrestore(&eemg_spinlock, *flags);
|
|
}
|
|
|
|
/*
|
|
* timer for log
|
|
*/
|
|
static enum hrtimer_restart eemg_log_timer_func(struct hrtimer *timer)
|
|
{
|
|
struct eemg_det *det;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
for_each_det(det) {
|
|
/* get rid of redundent banks */
|
|
if (det->features == 0)
|
|
continue;
|
|
eemg_debug("Timer Bk=%d (%d)(%d, %d, %d, %d, %d, %d, %d, %d)(0x%x)\n",
|
|
det->ctrl_id,
|
|
det->ops->get_temp_gpu(det),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[0]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[1]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[2]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[3]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[4]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[5]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[6]),
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[7]),
|
|
det->t250);
|
|
}
|
|
|
|
hrtimer_forward_now(timer, ns_to_ktime(LOG_INTERVAL));
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return HRTIMER_RESTART;
|
|
}
|
|
|
|
static void eemg_calculate_aging_margin(struct eemg_det *det,
|
|
int start_oft, int end_oft)
|
|
{
|
|
|
|
int num_bank_freq, offset, i = 0;
|
|
|
|
|
|
num_bank_freq = det->num_freq_tbl;
|
|
|
|
offset = start_oft - end_oft;
|
|
for (i = 0; i < det->num_freq_tbl; i++) {
|
|
if (i == 0)
|
|
det->volt_aging[i] = start_oft;
|
|
else
|
|
det->volt_aging[i] = start_oft -
|
|
((offset * i) / det->num_freq_tbl);
|
|
}
|
|
|
|
}
|
|
|
|
static void eemg_save_final_volt_aee(struct eemg_det *ndet)
|
|
{
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
int i;
|
|
|
|
if (ndet == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < NR_FREQ; i++) {
|
|
switch (ndet->ctrl_id) {
|
|
case EEMG_CTRL_GPU:
|
|
if (i < 8) {
|
|
aee_rr_rec_ptp_gpu_volt_2(
|
|
((unsigned long long)
|
|
(ndet->volt_tbl_pmic[i])
|
|
<< (8 * i)) |
|
|
(aee_rr_curr_ptp_gpu_volt_2() & ~
|
|
((unsigned long long)(0xFF)
|
|
<< (8 * i))
|
|
)
|
|
);
|
|
} else {
|
|
aee_rr_rec_ptp_gpu_volt_3(
|
|
((unsigned long long)(ndet->volt_tbl_pmic[i]) <<
|
|
(8 * (i - 8))) |
|
|
(aee_rr_curr_ptp_gpu_volt_3() & ~
|
|
((unsigned long long)(0xFF) <<
|
|
(8 * (i - 8)))
|
|
)
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void eemg_interpolate_mid_opp(struct eemg_det *ndet)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < gpu_vb_turn_pt; i++) {
|
|
ndet->volt_tbl_pmic[i] = (unsigned int)(interpolate(
|
|
ndet->freq_tbl[0], ndet->freq_tbl[gpu_vb_turn_pt],
|
|
ndet->volt_tbl_pmic[0], ndet->volt_tbl_pmic[
|
|
gpu_vb_turn_pt], ndet->freq_tbl[i]));
|
|
}
|
|
}
|
|
|
|
static void get_volt_table_in_thread(struct eemg_det *det)
|
|
{
|
|
unsigned int init2chk = 0;
|
|
struct eemg_det *highdet;
|
|
struct eemg_det *ndet = det;
|
|
unsigned int i, verr = 0;
|
|
int low_temp_offset = 0, rm_dvtfix_offset = 0;
|
|
unsigned int t_clamp = 0;
|
|
|
|
mutex_lock(det->loo_mutex);
|
|
ndet = (det->loo_role == HIGH_BANK) ?
|
|
id_to_eemg_det(det->loo_couple) : det;
|
|
eemg_debug("@@! In %s\n", __func__);
|
|
read_volt_from_VOP(det);
|
|
/* Copy volt to volt_tbl_init2 */
|
|
if (det->init2_done == 0) {
|
|
gpu_cur_init02_flag |= BIT(det->ctrl_id);
|
|
/* Receive init2 isr and update init2 volt_table */
|
|
if (det->loo_role == HIGH_BANK) {
|
|
memcpy(ndet->volt_tbl_init2, det->volt_tbl,
|
|
sizeof(int) * det->turn_pt);
|
|
memcpy(det->volt_tbl_init2, det->volt_tbl,
|
|
sizeof(int) * det->turn_pt);
|
|
} else if (det->loo_role == LOW_BANK)
|
|
memcpy(&(det->volt_tbl_init2[det->turn_pt]),
|
|
&(det->volt_tbl[det->turn_pt]), sizeof(int) *
|
|
(det->num_freq_tbl - det->turn_pt));
|
|
det->init2_done = 1;
|
|
}
|
|
|
|
/* Copy high bank volt to volt_tbl
|
|
* remap to L/B bank for update dvfs table, also copy
|
|
* high opp volt table
|
|
* Band HIGHL will update its volt table (opp0~7) to bank L
|
|
*/
|
|
if (det->loo_role == HIGH_BANK)
|
|
memcpy(ndet->volt_tbl, det->volt_tbl, sizeof(int) *
|
|
det->turn_pt);
|
|
|
|
for (i = 0; i < NR_FREQ; i++) {
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
switch (ndet->ctrl_id) {
|
|
case EEMG_CTRL_GPU:
|
|
if (i < 8) {
|
|
aee_rr_rec_ptp_gpu_volt(
|
|
((unsigned long long)(ndet->volt_tbl[i])
|
|
<< (8 * i)) |
|
|
(aee_rr_curr_ptp_gpu_volt() & ~
|
|
((unsigned long long)(0xFF)
|
|
<< (8 * i))
|
|
)
|
|
);
|
|
} else {
|
|
aee_rr_rec_ptp_gpu_volt_1(
|
|
((unsigned long long)(ndet->volt_tbl[i]) <<
|
|
(8 * (i - 8))) |
|
|
(aee_rr_curr_ptp_gpu_volt_1() & ~
|
|
((unsigned long long)(0xFF) <<
|
|
(8 * (i - 8)))
|
|
)
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if ((i > 0) && ((i % 4) != 0) &&
|
|
(i != det->turn_pt) &&
|
|
(ndet->volt_tbl[i] > ndet->volt_tbl[i-1])) {
|
|
|
|
verr = 1;
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d; (%s) [%d] = [%x] > [%d] = [%x]\n",
|
|
__func__, __LINE__, ((char *)(ndet->name) + 8),
|
|
i, ndet->volt_tbl[i], i-1, ndet->volt_tbl[i-1]);
|
|
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d; (%s) V30_[0x%x], V74_[0x%x],",
|
|
__func__, __LINE__, ((char *)(det->name) + 8),
|
|
ndet->mon_vop30, ndet->mon_vop74);
|
|
#endif
|
|
WARN_ON(ndet->volt_tbl[i] > ndet->volt_tbl[i-1]);
|
|
}
|
|
|
|
/*
|
|
*eemg_debug("mon_[%s].volt_tbl[%d] = 0x%X (%d)\n",
|
|
* det->name, i, det->volt_tbl[i],
|
|
* det->ops->pmic_2_volt_gpu(det, det->volt_tbl[i]));
|
|
|
|
*if (NR_FREQ > 8) {
|
|
* eemg_isr_info("mon_[%s].volt_tbl[%d] = 0x%X (%d)\n",
|
|
* det->name, i+1, det->volt_tbl[i+1],
|
|
* det->ops->pmic_2_volt_gpu(det, det->volt_tbl[i+1]));
|
|
*}
|
|
*/
|
|
}
|
|
|
|
if (verr == 1)
|
|
memcpy(ndet->volt_tbl, ndet->volt_tbl_init2,
|
|
sizeof(ndet->volt_tbl));
|
|
|
|
/* Check Temperature */
|
|
ndet->temp = ndet->ops->get_temp_gpu(det);
|
|
|
|
|
|
if (ndet->temp <= EXTRA_LOW_TEMP_VAL)
|
|
ndet->isTempInv = EEM_EXTRALOW_T;
|
|
else if (ndet->temp <= LOW_TEMP_VAL)
|
|
ndet->isTempInv = EEM_LOW_T;
|
|
else if (ndet->temp >= HIGH_TEMP_VAL)
|
|
ndet->isTempInv = EEM_HIGH_T;
|
|
else
|
|
ndet->isTempInv = 0;
|
|
|
|
if (ndet->ctrl_id == EEMG_CTRL_GPU) {
|
|
if ((ndet->isTempInv == EEM_EXTRALOW_T) ||
|
|
(ndet->isTempInv == EEM_LOW_T)) {
|
|
ndet->low_temp_off =
|
|
(ndet->isTempInv == EEM_EXTRALOW_T) ?
|
|
EXTRA_LOW_TEMP_OFF_GPU : LOW_TEMP_OFF_GPU;
|
|
t_clamp = ndet->low_temp_off;
|
|
} else
|
|
t_clamp = 0;
|
|
}
|
|
|
|
/* scale of det->volt_offset must equal 10uV */
|
|
/* if has record table, min with record table of each cpu */
|
|
for (i = 0; i < ndet->num_freq_tbl; i++) {
|
|
if (ndet->volt_policy) {
|
|
if (i < det->turn_pt) {
|
|
highdet = id_to_eemg_det(ndet->loo_couple);
|
|
rm_dvtfix_offset = 0 - highdet->DVTFIXED;
|
|
} else
|
|
rm_dvtfix_offset = 0 - ndet->DVTFIXED;
|
|
} else {
|
|
if (i < det->turn_pt) {
|
|
highdet = id_to_eemg_det(ndet->loo_couple);
|
|
rm_dvtfix_offset = min(
|
|
((highdet->DVTFIXED * highdet->freq_tbl[i]
|
|
+ (100 - 1)) / 100),
|
|
highdet->DVTFIXED) - highdet->DVTFIXED;
|
|
} else
|
|
rm_dvtfix_offset = min(
|
|
((ndet->DVTFIXED * ndet->freq_tbl[i]
|
|
+ (100 - 1)) / 100),
|
|
ndet->DVTFIXED) - ndet->DVTFIXED;
|
|
}
|
|
|
|
/* Add low temp offset for each bank if temp inverse */
|
|
if (ndet->isTempInv) {
|
|
low_temp_offset =
|
|
(ndet->isTempInv == EEM_HIGH_T) ?
|
|
ndet->high_temp_off : ndet->low_temp_off;
|
|
}
|
|
|
|
switch (ndet->ctrl_id) {
|
|
case EEMG_CTRL_GPU:
|
|
if ((i == 0) && (gpu_vb != 0 && gpu_vb_turn_pt != 0))
|
|
ndet->volt_tbl_pmic[i] = min((unsigned int)(
|
|
clamp(ndet->ops->eemg_2_pmic(ndet,
|
|
(ndet->ops->volt_2_eemg(ndet,
|
|
gpu_vb_volt))),
|
|
ndet->ops->eemg_2_pmic(ndet,
|
|
ndet->VMIN), ndet->ops->eemg_2_pmic(
|
|
ndet, VMAX_VAL_GPU)) + low_temp_offset
|
|
), ndet->volt_tbl_orig[i] +
|
|
ndet->volt_clamp + t_clamp);
|
|
else
|
|
ndet->volt_tbl_pmic[i] = min((unsigned int)(clamp(
|
|
ndet->ops->eemg_2_pmic(ndet,
|
|
(ndet->volt_tbl[i] + ndet->volt_offset +
|
|
ndet->volt_aging[i]) + rm_dvtfix_offset),
|
|
ndet->ops->eemg_2_pmic(ndet, ndet->VMIN),
|
|
ndet->ops->eemg_2_pmic(ndet, VMAX_VAL_GPU)
|
|
) + low_temp_offset), ndet->volt_tbl_orig[i] +
|
|
ndet->volt_clamp + t_clamp);
|
|
|
|
break;
|
|
default:
|
|
eemg_error("[eemg_set_eemg_volt] incorrect det :%s!!",
|
|
ndet->name);
|
|
break;
|
|
}
|
|
|
|
if ((i > 0) && (ndet->volt_tbl_pmic[i] >
|
|
ndet->volt_tbl_pmic[i-1]) &&
|
|
(ndet->set_volt_to_upower)) {
|
|
/* _ / */
|
|
/* /_/ */
|
|
/* / */
|
|
if (det->loo_role == HIGH_BANK)
|
|
/* Receive high bank isr but low opp */
|
|
/* still using higher volt */
|
|
/* overwrite low bank opp voltage */
|
|
/* / */
|
|
/* _/ */
|
|
/* / */
|
|
ndet->volt_tbl_pmic[i] =
|
|
ndet->volt_tbl_pmic[i-1];
|
|
else {
|
|
/* Receive low bank isr but high opp */
|
|
/* still using lower volt */
|
|
/* overwrite high bank opp voltage */
|
|
/* _/ */
|
|
/* / */
|
|
/* / */
|
|
ndet->volt_tbl_pmic[i-1] =
|
|
ndet->volt_tbl_pmic[i];
|
|
|
|
if ((i > 1) && (ndet->volt_tbl_pmic[i] >
|
|
ndet->volt_tbl_pmic[i-2]))
|
|
ndet->volt_tbl_pmic[i-2] =
|
|
ndet->volt_tbl_pmic[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ndet->ctrl_id == EEMG_CTRL_GPU) && (gpu_vb != 0 && gpu_vb_turn_pt != 0))
|
|
eemg_interpolate_mid_opp(ndet);
|
|
|
|
eemg_save_final_volt_aee(ndet);
|
|
|
|
if (ndet->set_volt_to_upower == 0) {
|
|
if (((ndet->ctrl_id == EEMG_CTRL_GPU) &&
|
|
(gpu_cur_init02_flag != gpu_final_init02_flag)))
|
|
init2chk = 0;
|
|
else
|
|
init2chk = 1;
|
|
eemg_error("cur_flag:0x%x, g_flag:0x%x\n",
|
|
gpu_cur_init02_flag, gpu_final_init02_flag);
|
|
/* only when set_volt_to_upower == 0,
|
|
* the volt will be apply to upower
|
|
*/
|
|
if (init2chk)
|
|
ndet->set_volt_to_upower = 1;
|
|
}
|
|
|
|
if (0 == (ndet->disabled % 2) && ndet->set_volt_to_upower)
|
|
ndet->ops->set_volt_gpu(ndet);
|
|
mutex_unlock(ndet->loo_mutex);
|
|
}
|
|
/*
|
|
* Thread for voltage setting
|
|
*/
|
|
static int eemg_volt_thread_handler(void *data)
|
|
{
|
|
struct eemg_ctrl *ctrl = (struct eemg_ctrl *)data;
|
|
struct eemg_det *det = id_to_eemg_det(ctrl->det_id);
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
int temp = -1;
|
|
#endif
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
do {
|
|
eemg_debug("In thread handler\n");
|
|
wait_event_interruptible(ctrl->wq, ctrl->volt_update);
|
|
if ((ctrl->volt_update & EEMG_VOLT_UPDATE) &&
|
|
det->ops->set_volt_gpu) {
|
|
get_volt_table_in_thread(det);
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
/* update set volt status for this bank */
|
|
switch (det->ctrl_id) {
|
|
case EEMG_CTRL_GPU:
|
|
aee_rr_rec_ptp_status
|
|
(aee_rr_curr_ptp_status() |
|
|
(1 << EEMG_GPU_IS_SET_VOLT));
|
|
temp = EEMG_GPU_IS_SET_VOLT;
|
|
break;
|
|
case EEMG_CTRL_GPU_HI:
|
|
aee_rr_rec_ptp_status
|
|
(aee_rr_curr_ptp_status() |
|
|
(1 << EEMG_GPU_HI_IS_SET_VOLT));
|
|
temp = EEMG_GPU_HI_IS_SET_VOLT;
|
|
break;
|
|
default:
|
|
eemg_error
|
|
("eemg_volt_handler :incorrect det id %d\n",
|
|
det->ctrl_id);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
if ((ctrl->volt_update & EEMG_VOLT_RESTORE) &&
|
|
det->ops->restore_default_volt_gpu) {
|
|
det->ops->restore_default_volt_gpu(det);
|
|
ctrl->volt_update = EEMG_VOLT_NONE;
|
|
}
|
|
if (det->vop_check)
|
|
ctrl->volt_update = EEMG_VOLT_NONE;
|
|
} while (!kthread_should_stop());
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void inherit_base_det(struct eemg_det *det)
|
|
{
|
|
/*
|
|
* Inherit ops from eemg_det_base_ops if ops in det is NULL
|
|
*/
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
#define INIT_OP(ops, func) \
|
|
do { \
|
|
if (ops->func == NULL) \
|
|
ops->func = eemg_det_base_ops.func; \
|
|
} while (0)
|
|
|
|
INIT_OP(det->ops, disable_gpu);
|
|
INIT_OP(det->ops, disable_locked_gpu);
|
|
INIT_OP(det->ops, switch_bank_gpu);
|
|
INIT_OP(det->ops, init01_gpu);
|
|
INIT_OP(det->ops, init02_gpu);
|
|
INIT_OP(det->ops, mon_mode_gpu);
|
|
INIT_OP(det->ops, get_status_gpu);
|
|
INIT_OP(det->ops, dump_status_gpu);
|
|
INIT_OP(det->ops, set_phase_gpu);
|
|
INIT_OP(det->ops, get_temp_gpu);
|
|
INIT_OP(det->ops, get_volt_gpu);
|
|
INIT_OP(det->ops, set_volt_gpu);
|
|
INIT_OP(det->ops, restore_default_volt_gpu);
|
|
INIT_OP(det->ops, get_freq_table_gpu);
|
|
INIT_OP(det->ops, volt_2_pmic_gpu);
|
|
INIT_OP(det->ops, volt_2_eemg);
|
|
INIT_OP(det->ops, pmic_2_volt_gpu);
|
|
INIT_OP(det->ops, eemg_2_pmic);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
static void eemg_init_ctrl(struct eemg_ctrl *ctrl)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
eemg_debug("In %s\n", __func__);
|
|
if (1) {
|
|
init_waitqueue_head(&ctrl->wq);
|
|
ctrl->thread = kthread_run(eemg_volt_thread_handler,
|
|
ctrl, ctrl->name);
|
|
|
|
if (IS_ERR(ctrl->thread))
|
|
eemg_error("Create %s thread failed: %ld\n",
|
|
ctrl->name, PTR_ERR(ctrl->thread));
|
|
}
|
|
|
|
eemg_debug("End %s\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
static void eemg_init_det(struct eemg_det *det, struct eemg_devinfo *devinfo,
|
|
struct platform_device *pdev)
|
|
{
|
|
enum eemg_det_id det_id = det_to_id(det);
|
|
struct nvmem_device *nvmem_dev;
|
|
struct device_node *node = pdev->dev.of_node, *np;
|
|
const char *domain;
|
|
int *val;
|
|
int ret, efuse_offset = 0, efuse_mask = 0, efuse = 0;
|
|
struct eemg_det *h_det, *l_det;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
inherit_base_det(det);
|
|
eemg_debug("IN init_det %s\n", det->name);
|
|
|
|
nvmem_dev = nvmem_device_get(&pdev->dev, "mtk_efuse");
|
|
|
|
val = (int *)&eemg_devinfo;
|
|
|
|
ret = of_property_read_string_index(node, "eemg_detectors", det_id,
|
|
&domain);
|
|
np = of_find_node_by_name(node, domain);
|
|
if (np) {
|
|
of_property_read_u32_index(np, "MDES", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "MDES", 1, &efuse_mask);
|
|
det->MDES = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "BDES", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "BDES", 1, &efuse_mask);
|
|
det->BDES = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "DCMDET", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "DCMDET", 1, &efuse_mask);
|
|
det->DCMDET = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "DCBDET", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "DCBDET", 1, &efuse_mask);
|
|
det->DCBDET = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "EEMINITEN", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "EEMINITEN", 1, &efuse_mask);
|
|
det->EEMINITEN = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "EEMMONEN", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "EEMMONEN", 1, &efuse_mask);
|
|
det->EEMMONEN = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "MTDES", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "MTDES", 1, &efuse_mask);
|
|
det->MTDES = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
of_property_read_u32_index(np, "SPEC", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "SPEC", 1, &efuse_mask);
|
|
det->SPEC = (*(val + efuse_offset) & efuse_mask) >>
|
|
find_first_bit((unsigned long *)&efuse_mask, 32);
|
|
ret = of_property_read_u32(np, "max_freq_khz",
|
|
&det->max_freq_khz);
|
|
ret = of_property_read_u32(np, "loo_role", &det->loo_role);
|
|
ret = of_property_read_u32(np, "DVTFIXED", &det->DVTFIXED);
|
|
of_property_read_u32_index(np, "VMIN", 0, &efuse_offset);
|
|
of_property_read_u32_index(np, "VMIN", 1, &efuse_mask);
|
|
nvmem_device_read(nvmem_dev, efuse_offset, sizeof(__u32),
|
|
&efuse);
|
|
of_property_read_u32_index(np, "VMIN-idx", (efuse & efuse_mask)
|
|
>> find_first_bit((unsigned long *)&efuse_mask, 32),
|
|
&det->VMIN);
|
|
ret = of_property_read_u32(np, "VMAX", &det->VMAX);
|
|
det->VMAX += det->DVTFIXED;
|
|
}
|
|
|
|
switch (det_id) {
|
|
case EEMG_DET_GPU:
|
|
gpu_final_init02_flag |= BIT(det_id);
|
|
break;
|
|
case EEMG_DET_GPU_HI:
|
|
break;
|
|
default:
|
|
eemg_debug("[%s]: Unknown det_id %d\n", __func__, det_id);
|
|
break;
|
|
}
|
|
|
|
/* get DVFS frequency table */
|
|
if (det->ops->get_freq_table_gpu) {
|
|
h_det = (det->loo_role == HIGH_BANK) ? det:id_to_eemg_det(det->loo_couple);
|
|
l_det = (det->loo_role == LOW_BANK) ? det:id_to_eemg_det(det->loo_couple);
|
|
det->ops->get_freq_table_gpu(det, h_det->max_freq_khz, l_det->max_freq_khz);
|
|
}
|
|
|
|
if (gpu_2line && det_id == EEMG_DET_GPU_HI) {
|
|
if (det->turn_pt != 0)
|
|
gpu_final_init02_flag |= BIT(det_id);
|
|
else
|
|
det->features = 0;
|
|
}
|
|
|
|
eemg_debug("END init_det %s, turn_pt:%d\n",
|
|
det->name, det->turn_pt);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
static void eemg_set_eemg_volt(struct eemg_det *det)
|
|
{
|
|
#if SET_PMIC_VOLT
|
|
struct eemg_ctrl *ctrl = id_to_eemg_ctrl(det->ctrl_id);
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
ctrl->volt_update |= EEMG_VOLT_UPDATE;
|
|
dsb(sy);
|
|
eemg_debug("@@!In %s\n", __func__);
|
|
wake_up_interruptible(&ctrl->wq);
|
|
#endif
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
}
|
|
|
|
static void eemg_restore_eemg_volt(struct eemg_det *det)
|
|
{
|
|
#if SET_PMIC_VOLT
|
|
struct eemg_ctrl *ctrl = id_to_eemg_ctrl(det->ctrl_id);
|
|
|
|
if (ctrl == NULL)
|
|
return;
|
|
|
|
ctrl->volt_update |= EEMG_VOLT_RESTORE;
|
|
dsb(sy);
|
|
wake_up_interruptible(&ctrl->wq);
|
|
#endif
|
|
}
|
|
|
|
static inline void handle_init01_isr(struct eemg_det *det)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
|
|
eemg_debug("mode = init1 %s-isr\n", ((char *)(det->name) + 8));
|
|
|
|
det->dcvalues[EEMG_PHASE_INIT01] = eemg_read(EEMG_DCVALUES);
|
|
det->freqpct30[EEMG_PHASE_INIT01] = eemg_read(EEMG_FREQPCT30);
|
|
det->eemg_26c[EEMG_PHASE_INIT01] = eemg_read(EEMG_VDESIGN30);
|
|
det->vop30[EEMG_PHASE_INIT01] = eemg_read(EEMG_VOP30);
|
|
det->eemg_eemEn[EEMG_PHASE_INIT01] = eemg_read(EEMGEN);
|
|
|
|
#if DUMP_DATA_TO_DE
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(reg_gpu_addr_off); i++) {
|
|
det->reg_dump_data[i][EEMG_PHASE_INIT01] =
|
|
eemg_read(EEMG_BASEADDR + reg_gpu_addr_off[i]);
|
|
eemg_isr_info("0x%lx = 0x%08x\n",
|
|
(unsigned long)EEMG_BASEADDR + reg_gpu_addr_off[i],
|
|
det->reg_dump_data[i][EEMG_PHASE_INIT01]
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
* Read & store 16 bit values EEMG_DCVALUES.DCVOFFSET and
|
|
* EEMG_AGEVALUES.AGEVOFFSET for later use in INIT2 procedure
|
|
*/
|
|
/* hw bug, workaround */
|
|
det->DCVOFFSETIN = ~(eemg_read(EEMG_DCVALUES) & 0xffff) + 1;
|
|
|
|
det->AGEVOFFSETIN = eemg_read(EEMG_AGEVALUES) & 0xffff;
|
|
|
|
/*
|
|
* Set EEMGEN.EEMINITEN/EEMGEN.EEMINIT2EN = 0x0 &
|
|
* Clear EEM INIT interrupt EEMGINTSTS = 0x00000001
|
|
*/
|
|
eemg_write(EEMGEN, 0x0 | SEC_MOD_SEL);
|
|
eemg_write(EEMGINTSTS, 0x1);
|
|
/* det->ops->init02_gpu(det); */
|
|
|
|
eemg_debug("@@ END mode = init1 %s-isr\n", ((char *)(det->name) + 8));
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static unsigned int interpolate(unsigned int y1, unsigned int y0,
|
|
unsigned int x1, unsigned int x0, unsigned int ym)
|
|
{
|
|
unsigned int ratio, result;
|
|
|
|
if (x1 == x0) {
|
|
result = x1;
|
|
} else {
|
|
ratio = (((y1 - y0) * 100) + (x1 - x0 - 1)) / (x1 - x0);
|
|
result =
|
|
(x1 - ((((y1 - ym) * 10000) + ratio - 1) / ratio) / 100);
|
|
/*
|
|
*eemg_debug("y1(%d), y0(%d), x1(%d), x0(%d), ym(%d), ratio(%d),
|
|
*rtn(%d)\n",
|
|
* y1, y0, x1, x0, ym, ratio, result);
|
|
*/
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void eemg_fill_freq_table(struct eemg_det *det)
|
|
{
|
|
int tmpfreq30 = 0, tmpfreq74 = 0;
|
|
int i, j, shift_bits;
|
|
int *setfreq;
|
|
|
|
if (det->loo_role == HIGH_BANK) {
|
|
/* From opp0 ~ opp[turn_pt] */
|
|
if (det->turn_pt < 8) {
|
|
for (i = 0; i < det->turn_pt; i++) {
|
|
shift_bits = (i % 4) * 8;
|
|
setfreq = (i < 4) ? &tmpfreq30 : &tmpfreq74;
|
|
*setfreq |= (det->freq_tbl[i] << shift_bits);
|
|
}
|
|
} else {
|
|
tmpfreq30 = det->freq_tbl[0] & 0xFF;
|
|
/* prepare last 7 freq, fill to FREQPCT31, FREQPCT74 */
|
|
for (i = (det->turn_pt - 7), j = 1;
|
|
i < det->turn_pt; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
setfreq = (j < 4) ? &tmpfreq30 : &tmpfreq74;
|
|
*setfreq |= (det->freq_tbl[i] << shift_bits);
|
|
}
|
|
}
|
|
} else if (det->loo_role == LOW_BANK) {
|
|
/* From opp[turn_pt] to final opp */
|
|
/* prepare 2nd line's first freq */
|
|
tmpfreq30 = det->freq_tbl[det->turn_pt] & 0xFF;
|
|
if ((det->num_freq_tbl - det->turn_pt) <= 8) {
|
|
/* For opp9~15 */
|
|
for (i = (det->turn_pt + 1), j = 1;
|
|
i < det->num_freq_tbl; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
setfreq = (j < 4) ? &tmpfreq30 : &tmpfreq74;
|
|
*setfreq |= (det->freq_tbl[i] << shift_bits);
|
|
}
|
|
} else {
|
|
/* prepare last 7 freq, fill to FREQPCT31, FREQPCT74 */
|
|
for (i = (det->num_freq_tbl - 7), j = 1;
|
|
i < det->num_freq_tbl; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
setfreq = (j < 4) ? &tmpfreq30 : &tmpfreq74;
|
|
*setfreq |= (det->freq_tbl[i] << shift_bits);
|
|
}
|
|
}
|
|
} else {
|
|
if (det->num_freq_tbl <= 16) {
|
|
tmpfreq30 =
|
|
((det->freq_tbl[3 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 24) & 0xff000000) |
|
|
((det->freq_tbl[2 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 16) & 0xff0000) |
|
|
((det->freq_tbl[1 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 8) & 0xff00) |
|
|
(det->freq_tbl[0]
|
|
& 0xff);
|
|
|
|
tmpfreq74 =
|
|
((det->freq_tbl[7 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 24) & 0xff000000) |
|
|
((det->freq_tbl[6 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 16) & 0xff0000) |
|
|
((det->freq_tbl[5 * ((det->num_freq_tbl + 7) / 8)]
|
|
<< 8) & 0xff00) |
|
|
((det->freq_tbl[4 * ((det->num_freq_tbl + 7) / 8)])
|
|
& 0xff);
|
|
} else {
|
|
/* For 32 opp */
|
|
eemg_error("Fill 32 opp/n");
|
|
tmpfreq30 = det->freq_tbl[0] & 0xFF;
|
|
/* prepare last 7 freq, fill to FREQPCT31, FREQPCT74 */
|
|
for (i = (det->num_freq_tbl - 7), j = 1;
|
|
i < det->num_freq_tbl; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
setfreq = (j < 4) ? &tmpfreq30 : &tmpfreq74;
|
|
*setfreq |= (det->freq_tbl[i] << shift_bits);
|
|
}
|
|
}
|
|
}
|
|
eemg_write(EEMG_FREQPCT30, tmpfreq30);
|
|
eemg_write(EEMG_FREQPCT74, tmpfreq74);
|
|
}
|
|
|
|
static void read_volt_from_VOP(struct eemg_det *det)
|
|
{
|
|
int temp30, temp74, i, j;
|
|
unsigned int step = NR_FREQ / 8;
|
|
unsigned int read_init2 = 0;
|
|
int ref_idx = ((det->num_freq_tbl + (step - 1)) / step) - 1;
|
|
int readvop, shift_bits;
|
|
struct eemg_det *couple_det;
|
|
|
|
/* Check both high/low bank's voltage are ready */
|
|
if (det->loo_role != 0) {
|
|
couple_det = id_to_eemg_det(det->loo_couple);
|
|
if ((couple_det->init2_done == 0) ||
|
|
(couple_det->mon_vop30 == 0) ||
|
|
(couple_det->mon_vop74 == 0))
|
|
read_init2 = 1;
|
|
}
|
|
#if ENABLE_HT_FT
|
|
read_init2 = 1;
|
|
#endif
|
|
if ((det->init2_done == 0) || (det->mon_vop30 == 0) ||
|
|
(det->mon_vop74 == 0))
|
|
read_init2 = 1;
|
|
if (read_init2) {
|
|
temp30 = det->init2_vop30;
|
|
temp74 = det->init2_vop74;
|
|
} else {
|
|
temp30 = det->mon_vop30;
|
|
temp74 = det->mon_vop74;
|
|
}
|
|
det->vop_check = 1;
|
|
if (det->loo_role == HIGH_BANK) {
|
|
if (det->turn_pt < 8) {
|
|
for (i = 0; i < det->turn_pt; i++) {
|
|
shift_bits = (i % 4) * 8;
|
|
readvop = (i < 4) ? temp30 : temp74;
|
|
det->volt_tbl[i] = (readvop >> shift_bits) & 0xff;
|
|
}
|
|
} else {
|
|
det->volt_tbl[0] = (temp30 & 0xff);
|
|
/* prepare last 7 opp */
|
|
for (i = (det->turn_pt - 7), j = 1; i < det->turn_pt; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
readvop = (j < 4) ? temp30 : temp74;
|
|
det->volt_tbl[i] =
|
|
(readvop >> shift_bits) & 0xff;
|
|
}
|
|
/* prepare middle opp */
|
|
for (i = 1; i < det->turn_pt - 7; i++) {
|
|
det->volt_tbl[i] =
|
|
interpolate(
|
|
det->freq_tbl[0],
|
|
det->freq_tbl[det->turn_pt - 7],
|
|
det->volt_tbl[0],
|
|
det->volt_tbl[det->turn_pt - 7],
|
|
det->freq_tbl[i]
|
|
);
|
|
}
|
|
}
|
|
} else if (det->loo_role == LOW_BANK) {
|
|
/* prepare first opp */
|
|
det->volt_tbl[det->turn_pt] = (temp30 & 0xff);
|
|
if ((det->num_freq_tbl - det->turn_pt) <= 8) {
|
|
/* For opp8~15 */
|
|
for (i = det->turn_pt + 1, j = 1;
|
|
i < det->num_freq_tbl; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
readvop = (j < 4) ? temp30 : temp74;
|
|
det->volt_tbl[i] =
|
|
(readvop >> shift_bits) & 0xff;
|
|
}
|
|
} else {
|
|
/* prepare last 7 opp, for opp9~15 */
|
|
for (i = (det->num_freq_tbl - 7), j = 1; i < det->num_freq_tbl; i++, j++) {
|
|
shift_bits = (j % 4) * 8;
|
|
readvop = (j < 4) ? temp30 : temp74;
|
|
det->volt_tbl[i] =
|
|
(readvop >> shift_bits) & 0xff;
|
|
}
|
|
/* prepare middle opp */
|
|
for (i = (det->turn_pt + 1); i < det->num_freq_tbl - 7; i++) {
|
|
det->volt_tbl[i] = interpolate(
|
|
det->freq_tbl[det->turn_pt],
|
|
det->freq_tbl[det->num_freq_tbl - 7],
|
|
det->volt_tbl[det->turn_pt],
|
|
det->volt_tbl[det->num_freq_tbl - 7],
|
|
det->freq_tbl[i]
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
/* for 16 opp */
|
|
det->volt_tbl[0] = (temp30 & 0xff);
|
|
det->volt_tbl[1 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp30 >> 8) & 0xff;
|
|
det->volt_tbl[2 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp30 >> 16) & 0xff;
|
|
det->volt_tbl[3 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp30 >> 24) & 0xff;
|
|
det->volt_tbl[4 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp74 & 0xff);
|
|
det->volt_tbl[5 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp74 >> 8) & 0xff;
|
|
det->volt_tbl[6 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp74 >> 16) & 0xff;
|
|
det->volt_tbl[7 * ((det->num_freq_tbl + 7) / 8)] =
|
|
(temp74 >> 24) & 0xff;
|
|
if ((det->num_freq_tbl > 8) && (ref_idx > 0)) {
|
|
for (i = 0; i <= ref_idx; i++) { /* i < 8 */
|
|
for (j = 1; j < step; j++) {
|
|
if (i < ref_idx) {
|
|
det->volt_tbl[(i * step) + j] =
|
|
interpolate(
|
|
det->freq_tbl[(i * step)],
|
|
det->freq_tbl[((1 + i) * step)],
|
|
det->volt_tbl[(i * step)],
|
|
det->volt_tbl[((1 + i) * step)],
|
|
det->freq_tbl[(i * step) + j]
|
|
);
|
|
} else {
|
|
det->volt_tbl[(i * step) + j] =
|
|
clamp(
|
|
interpolate(
|
|
det->freq_tbl[((i - 1) * step)],
|
|
det->freq_tbl[((i) * step)],
|
|
det->volt_tbl[((i - 1) * step)],
|
|
det->volt_tbl[((i) * step)],
|
|
det->freq_tbl[(i * step) + j]
|
|
),
|
|
det->VMIN,
|
|
det->VMAX
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* if (det->num_freq_tbl > 8)*/
|
|
}
|
|
}
|
|
|
|
static inline void handle_init02_isr(struct eemg_det *det)
|
|
{
|
|
unsigned int i;
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
|
|
eemg_debug("mode = init2 %s-isr\n", ((char *)(det->name) + 8));
|
|
|
|
det->dcvalues[EEMG_PHASE_INIT02] = eemg_read(EEMG_DCVALUES);
|
|
det->freqpct30[EEMG_PHASE_INIT02] = eemg_read(EEMG_FREQPCT30);
|
|
det->eemg_26c[EEMG_PHASE_INIT02] = eemg_read(EEMG_VDESIGN30);
|
|
det->vop30[EEMG_PHASE_INIT02] = eemg_read(EEMG_VOP30);
|
|
det->eemg_eemEn[EEMG_PHASE_INIT02] = eemg_read(EEMGEN);
|
|
|
|
#if DUMP_DATA_TO_DE
|
|
for (i = 0; i < ARRAY_SIZE(reg_gpu_addr_off); i++) {
|
|
det->reg_dump_data[i][EEMG_PHASE_INIT02] =
|
|
eemg_read(EEMG_BASEADDR + reg_gpu_addr_off[i]);
|
|
eemg_isr_info("0x%lx = 0x%08x\n",
|
|
(unsigned long)EEMG_BASEADDR + reg_gpu_addr_off[i],
|
|
det->reg_dump_data[i][EEMG_PHASE_INIT02]
|
|
);
|
|
}
|
|
#endif
|
|
|
|
det->init2_vop30 = eemg_read(EEMG_VOP30);
|
|
det->init2_vop74 = eemg_read(EEMG_VOP74);
|
|
|
|
det->vop_check = 0;
|
|
eemg_set_eemg_volt(det);
|
|
/*
|
|
* Set EEMGEN.EEMINITEN/EEMGEN.EEMINIT2EN = 0x0 &
|
|
* Clear EEM INIT interrupt EEMGINTSTS = 0x00000001
|
|
*/
|
|
eemg_write(EEMGEN, 0x0 | SEC_MOD_SEL);
|
|
eemg_write(EEMGINTSTS, 0x1);
|
|
|
|
det->ops->mon_mode_gpu(det);
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static inline void handle_init_err_isr(struct eemg_det *det)
|
|
{
|
|
int i;
|
|
/* int *val = (int *)&eemg_devinfo; */
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
eemg_error("====================================================\n");
|
|
eemg_error("BD/MD/DCB/DCM/MTD=0x%X,%X,%X,%X,%X\n",
|
|
det->BDES, det->MDES, det->DCBDET, det->DCMDET,
|
|
det->MTDES);
|
|
eemg_error("DETWINDOW/VMIN/VMAX/DTHI/DTLO=0x%X,%X,%X,%X,%X\n",
|
|
det->DETWINDOW, det->VMIN, det->VMAX, det->DTHI,
|
|
det->DTLO);
|
|
eemg_error("DETMAX/DVTFIXED/VCO/DCCONFIG/=0x%X,%X,%X,%X\n",
|
|
det->DETMAX, det->DVTFIXED, det->VCO,
|
|
det->DCCONFIG);
|
|
eemg_error("DCVOFFSETIN/dcvalues0/1/turn_pt=0x%X,%X,%X,%X\n",
|
|
det->DCVOFFSETIN, det->dcvalues[0], det->dcvalues[1],
|
|
det->turn_pt);
|
|
|
|
/* Depend on EFUSE location */
|
|
for (i = 0; i < sizeof(struct eemg_devinfo) / sizeof(unsigned int);
|
|
i++)
|
|
eemg_error("M_HW_RES%d= 0x%08X\n", i, val[i]);
|
|
|
|
eemg_error("EEM init err: EEMGEN(%p) = 0x%X, EEMGINTSTS(%p) = 0x%X\n",
|
|
EEMGEN, eemg_read(EEMGEN),
|
|
EEMGINTSTS, eemg_read(EEMGINTSTS));
|
|
eemg_error("EEMG_SMSTATE0 (%p) = 0x%X\n",
|
|
EEMG_SMSTATE0, eemg_read(EEMG_SMSTATE0));
|
|
eemg_error("EEMG_SMSTATE1 (%p) = 0x%X\n",
|
|
EEMG_SMSTATE1, eemg_read(EEMG_SMSTATE1));
|
|
eemg_error("EEMG_DESCHAR (%p) = 0x%X,0x%X,0x%X\n",
|
|
EEMG_DESCHAR, eemg_read(EEMG_DESCHAR),
|
|
eemg_read(EEMG_TEMPCHAR), eemg_read(EEMG_DETCHAR));
|
|
eemg_error("EEMG_DCCONFIG/LIMIT/CORESEL (%p) = 0x%X,0x%X,0x%X\n",
|
|
EEMG_DCCONFIG, eemg_read(EEMG_DCCONFIG),
|
|
eemg_read(EEMG_LIMITVALS), eemg_read(EEMGCORESEL));
|
|
eemg_error("EEMG_DETWINDOW/INIT2/DC (%p) = 0x%X,0x%X,0x%X\n",
|
|
EEMG_DETWINDOW, eemg_read(EEMG_DETWINDOW),
|
|
eemg_read(EEMG_INIT2VALS), eemg_read(EEMG_DCVALUES));
|
|
eemg_error("EEMG_CHKSHIFT/VDESIGN/VOP(%p)=0x%X,0x%X,0x%X,0x%X,0x%X\n",
|
|
EEMG_CHKSHIFT, eemg_read(EEMG_CHKSHIFT),
|
|
eemg_read(EEMG_VDESIGN30), eemg_read(EEMG_VDESIGN74),
|
|
eemg_read(EEMG_VOP30), eemg_read(EEMG_VOP74));
|
|
eemg_error("EEMG_FREQPCT/74/CE0/CE4 (%p) = 0x%X,0x%X,0x%X,0x%X\n",
|
|
EEMG_FREQPCT30, eemg_read(EEMG_FREQPCT30),
|
|
eemg_read(EEMG_FREQPCT74), eemg_read(EEMG_BASEADDR + 0xCE0),
|
|
eemg_read(EEMG_BASEADDR + 0xCE4));
|
|
|
|
|
|
eemg_error("====================================================\n");
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem", "@%s():%d, get_volt_gpu(%s) = 0x%08X\n",
|
|
__func__,
|
|
__LINE__,
|
|
det->name,
|
|
det->VBOOT);
|
|
#endif
|
|
det->ops->disable_locked_gpu(det, BY_INIT_ERROR);
|
|
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static inline void handle_mon_mode_isr(struct eemg_det *det)
|
|
{
|
|
unsigned int i;
|
|
#ifdef CONFIG_THERMAL
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
unsigned long long temp_long;
|
|
unsigned long long temp_cur =
|
|
(unsigned long long)aee_rr_curr_ptp_temp();
|
|
#endif
|
|
#endif
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
|
|
eemg_debug("mode = mon %s-isr\n", ((char *)(det->name) + 8));
|
|
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
switch (det->ctrl_id) {
|
|
case EEMG_CTRL_GPU:
|
|
#ifdef CONFIG_THERMAL
|
|
#if defined(__LP64__) || defined(_LP64)
|
|
temp_long = 0;
|
|
#else
|
|
temp_long = 0;
|
|
#endif
|
|
if (temp_long != 0) {
|
|
aee_rr_rec_ptp_temp(temp_long <<
|
|
(8 * EEMG_GPU_IS_SET_VOLT) |
|
|
(temp_cur & ~((0xFFULL)
|
|
<< (8 * EEMG_GPU_IS_SET_VOLT))));
|
|
}
|
|
#endif
|
|
break;
|
|
case EEMG_CTRL_GPU_HI:
|
|
#ifdef CONFIG_THERMAL
|
|
#if defined(__LP64__) || defined(_LP64)
|
|
temp_long = 0;
|
|
#else
|
|
temp_long = 0;
|
|
#endif
|
|
if (temp_long != 0) {
|
|
aee_rr_rec_ptp_temp(temp_long <<
|
|
(8 * EEMG_GPU_HI_IS_SET_VOLT) |
|
|
(temp_cur & ~((0xFFULL)
|
|
<< (8 * EEMG_GPU_HI_IS_SET_VOLT))));
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
det->dcvalues[EEMG_PHASE_MON] = eemg_read(EEMG_DCVALUES);
|
|
det->freqpct30[EEMG_PHASE_MON] = eemg_read(EEMG_FREQPCT30);
|
|
det->eemg_26c[EEMG_PHASE_MON] = eemg_read(EEMGINTEN + 0x10);
|
|
det->vop30[EEMG_PHASE_MON] = eemg_read(EEMG_VOP30);
|
|
det->eemg_eemEn[EEMG_PHASE_MON] = eemg_read(EEMGEN);
|
|
|
|
#if DUMP_DATA_TO_DE
|
|
for (i = 0; i < ARRAY_SIZE(reg_gpu_addr_off); i++) {
|
|
det->reg_dump_data[i][EEMG_PHASE_MON] =
|
|
eemg_read(EEMG_BASEADDR + reg_gpu_addr_off[i]);
|
|
eemg_isr_info("0x%lx = 0x%08x\n",
|
|
(unsigned long)EEMG_BASEADDR + reg_gpu_addr_off[i],
|
|
det->reg_dump_data[i][EEMG_PHASE_MON]
|
|
);
|
|
}
|
|
#endif
|
|
|
|
det->mon_vop30 = eemg_read(EEMG_VOP30);
|
|
det->mon_vop74 = eemg_read(EEMG_VOP74);
|
|
det->vop_check = 0;
|
|
|
|
/* check if thermal sensor init completed? */
|
|
det->t250 = eemg_read(TEMPG);
|
|
|
|
/*
|
|
* 0x64 mappint to 100 + 25 = 125C,
|
|
* 0xB2 mapping to 178 - 128 = 50, -50 + 25 = -25C
|
|
*/
|
|
if (((det->t250 & 0xff) > 0x64) && ((det->t250 & 0xff) < 0xB2)) {
|
|
eemg_error("Bank %s Temperature > 125C or < -25C 0x%x\n",
|
|
det->name, det->t250);
|
|
goto out;
|
|
}
|
|
|
|
eemg_set_eemg_volt(det);
|
|
|
|
out:
|
|
/* Clear EEM INIT interrupt EEMGINTSTS = 0x00ff0000 */
|
|
eemg_write(EEMGINTSTS, 0x00ff0000);
|
|
eemg_debug("finish mon isr\n");
|
|
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static inline void handle_mon_err_isr(struct eemg_det *det)
|
|
{
|
|
#if DUMP_DATA_TO_DE
|
|
unsigned int i;
|
|
#endif
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
|
|
/* EEM Monitor mode error handler */
|
|
eemg_error("====================================================\n");
|
|
eemg_error
|
|
("EEM mon err: EEMGCORESEL(%p) = 0x%08X,EEMG_THERMINTST(%p) = 0x%08X,",
|
|
EEMGCORESEL, eemg_read(EEMGCORESEL),
|
|
EEMG_THERMINTST, eemg_read(EEMG_THERMINTST));
|
|
eemg_error("EEM0DINTST(%p) = 0x%08X",
|
|
EEMGODINTST, eemg_read(EEMGODINTST));
|
|
eemg_error("EEMGINTSTSRAW(%p) = 0x%08X, EEMGINTEN(%p) = 0x%08X\n",
|
|
EEMGINTSTSRAW, eemg_read(EEMGINTSTSRAW),
|
|
EEMGINTEN, eemg_read(EEMGINTEN));
|
|
eemg_error("====================================================\n");
|
|
eemg_error("EEM mon err:EEMGEN(%p)=0x%08X, EEMGINTSTS(%p)=0x%08X\n",
|
|
EEMGEN, eemg_read(EEMGEN),
|
|
EEMGINTSTS, eemg_read(EEMGINTSTS));
|
|
eemg_error("EEMG_SMSTATE0 (%p) = 0x%08X\n",
|
|
EEMG_SMSTATE0, eemg_read(EEMG_SMSTATE0));
|
|
eemg_error("EEMG_SMSTATE1 (%p) = 0x%08X\n",
|
|
EEMG_SMSTATE1, eemg_read(EEMG_SMSTATE1));
|
|
eemg_error("TEMP (%p) = 0x%08X\n",
|
|
TEMPG, eemg_read(TEMPG));
|
|
eemg_error("EEMG_TEMPMSR0 (%p) = 0x%08X\n",
|
|
EEMG_TEMPMSR0, eemg_read(EEMG_TEMPMSR0));
|
|
eemg_error("EEMG_TEMPMSR1 (%p) = 0x%08X\n",
|
|
EEMG_TEMPMSR1, eemg_read(EEMG_TEMPMSR1));
|
|
eemg_error("EEMG_TEMPMSR2 (%p) = 0x%08X\n",
|
|
EEMG_TEMPMSR2, eemg_read(EEMG_TEMPMSR2));
|
|
eemg_error("EEMG_TEMPMONCTL0 (%p) = 0x%08X\n",
|
|
EEMG_TEMPMONCTL0, eemg_read(EEMG_TEMPMONCTL0));
|
|
eemg_error("EEMG_TEMPMSRCTL1 (%p) = 0x%08X\n",
|
|
EEMG_TEMPMSRCTL1, eemg_read(EEMG_TEMPMSRCTL1));
|
|
eemg_error("====================================================\n");
|
|
|
|
#if DUMP_DATA_TO_DE
|
|
for (i = 0; i < ARRAY_SIZE(reg_gpu_addr_off); i++) {
|
|
det->reg_dump_data[i][EEMG_PHASE_MON] =
|
|
eemg_read(EEMG_BASEADDR + reg_gpu_addr_off[i]);
|
|
eemg_error("0x%lx = 0x%08x\n",
|
|
(unsigned long)EEMG_BASEADDR + reg_gpu_addr_off[i],
|
|
det->reg_dump_data[i][EEMG_PHASE_MON]
|
|
);
|
|
}
|
|
#endif
|
|
|
|
eemg_error("====================================================\n");
|
|
eemg_error("EEM mon err: EEMGCORESEL(%p) = 0x%08X,",
|
|
EEMGCORESEL, eemg_read(EEMGCORESEL));
|
|
eemg_error(" EEMG_THERMINTST(%p) = 0x%08X, EEMGODINTST(%p) = 0x%08X",
|
|
EEMG_THERMINTST, eemg_read(EEMG_THERMINTST),
|
|
EEMGODINTST, eemg_read(EEMGODINTST));
|
|
|
|
eemg_error(" EEMGINTSTSRAW(%p) = 0x%08X, EEMGINTEN(%p) = 0x%08X\n",
|
|
EEMGINTSTSRAW, eemg_read(EEMGINTSTSRAW),
|
|
EEMGINTEN, eemg_read(EEMGINTEN));
|
|
eemg_error("====================================================\n");
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
aee_kernel_warning("mt_eem",
|
|
"@%s():%d,get_volt_gpu(%s)=0x%08X,EEMGEN(%p)=0x%08X,EEMGINTSTS(%p)=0x%08X\n",
|
|
__func__, __LINE__,
|
|
det->name, det->VBOOT,
|
|
EEMGEN, eemg_read(EEMGEN),
|
|
EEMGINTSTS, eemg_read(EEMGINTSTS));
|
|
#endif
|
|
det->ops->disable_locked_gpu(det, BY_MON_ERROR);
|
|
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static inline void eemg_isr_handler(struct eemg_det *det)
|
|
{
|
|
unsigned int eemintsts, eemen;
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
|
|
eemintsts = eemg_read(EEMGINTSTS);
|
|
eemen = eemg_read(EEMGEN);
|
|
|
|
eemg_debug("Bk_# = %d %s-isr, 0x%X, 0x%X\n",
|
|
det->ctrl_id, ((char *)(det->name) + 8), eemintsts, eemen);
|
|
|
|
if (eemintsts == 0x1) { /* EEM init1 or init2 */
|
|
if ((eemen & 0x7) == 0x1) /* EEM init1 */
|
|
handle_init01_isr(det);
|
|
else if ((eemen & 0x7) == 0x5) /* EEM init2 */
|
|
handle_init02_isr(det);
|
|
else {
|
|
/* error : init1 or init2 */
|
|
handle_init_err_isr(det);
|
|
}
|
|
} else if ((eemintsts & 0x00ff0000) != 0x0)
|
|
handle_mon_mode_isr(det);
|
|
else { /* EEM error handler */
|
|
/* init 1 || init 2 error handler */
|
|
if (((eemen & 0x7) == 0x1) || ((eemen & 0x7) == 0x5))
|
|
handle_init_err_isr(det);
|
|
else /* EEM Monitor mode error handler */
|
|
handle_mon_err_isr(det);
|
|
}
|
|
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
static irqreturn_t eemg_isr(int irq, void *dev_id)
|
|
{
|
|
unsigned long flags;
|
|
struct eemg_det *det = NULL;
|
|
int i;
|
|
|
|
FUNC_ENTER(FUNC_LV_MODULE);
|
|
|
|
/* mt_ptpgpu_lock(&flags); */
|
|
for (i = 0; i < NR_EEMG_CTRL; i++) {
|
|
mt_ptpgpu_lock(&flags);
|
|
/* TODO: FIXME, it is better to link i @ struct eemg_det */
|
|
if ((BIT(i) & eemg_read(EEMGODINTST))) {
|
|
mt_ptpgpu_unlock(&flags);
|
|
continue;
|
|
}
|
|
|
|
det = &eemg_detectors[i];
|
|
det->ops->switch_bank_gpu(det, NR_EEMG_PHASE);
|
|
|
|
/*mt_eemg_reg_dump_locked(); */
|
|
|
|
eemg_isr_handler(det);
|
|
mt_ptpgpu_unlock(&flags);
|
|
}
|
|
|
|
/* mt_ptpgpu_unlock(&flags); */
|
|
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
void eemg_init02_gpu(const char *str)
|
|
{
|
|
struct eemg_det *det;
|
|
struct eemg_ctrl *ctrl;
|
|
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
eemg_debug("%s called by [%s]\n", __func__, str);
|
|
|
|
for_each_det_ctrl(det, ctrl) {
|
|
if (HAS_FEATURE(det, FEA_INIT02)) {
|
|
unsigned long flag;
|
|
|
|
mt_ptpgpu_lock(&flag);
|
|
det->init2_done = 0;
|
|
det->ops->init02_gpu(det);
|
|
mt_ptpgpu_unlock(&flag);
|
|
}
|
|
}
|
|
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
void eemg_init01_gpu(void)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_LOCAL);
|
|
eemg_init02_gpu(__func__);
|
|
FUNC_EXIT(FUNC_LV_LOCAL);
|
|
}
|
|
|
|
#if EN_EEMGPU
|
|
#if SUPPORT_DCONFIG
|
|
static void eemg_dconfig_set_det(struct eemg_det *det, struct device_node *node)
|
|
{
|
|
enum eemg_det_id det_id = det_to_id(det);
|
|
struct eemg_det *highdet;
|
|
int doe_initmon = 0xF, doe_clamp = 0;
|
|
int doe_offset = 0xFF;
|
|
int rc1 = 0, rc2 = 0, rc3 = 0;
|
|
|
|
if (det_id > EEMG_DET_GPU)
|
|
return;
|
|
|
|
switch (det_id) {
|
|
case EEMG_DET_GPU:
|
|
rc1 = of_property_read_u32(node, "eemg-initmon-gpu",
|
|
&doe_initmon);
|
|
rc2 = of_property_read_u32(node, "eemg-clamp-gpu",
|
|
&doe_clamp);
|
|
rc3 = of_property_read_u32(node, "eemg-offset-gpu",
|
|
&doe_offset);
|
|
break;
|
|
default:
|
|
eemg_debug("[%s]: Unknown det_id %d\n", __func__, det_id);
|
|
break;
|
|
}
|
|
|
|
if (!rc1) {
|
|
if ((((doe_initmon >= 0x0) && (doe_initmon <= 0x3)) ||
|
|
((doe_initmon >= 0x6) && (doe_initmon <= 0x7))) &&
|
|
(det->features != doe_initmon)) {
|
|
det->features = doe_initmon;
|
|
eemg_error("[DCONFIG] feature modified by DT(0x%x)\n",
|
|
doe_initmon);
|
|
|
|
if (HAS_FEATURE(det, FEA_INIT01) == 0)
|
|
final_init01_flag &= ~BIT(det_id);
|
|
else
|
|
final_init01_flag |= BIT(det_id);
|
|
|
|
|
|
switch (det_id) {
|
|
case EEMG_DET_GPU:
|
|
/* Change GPU Low_bank & High_bank */
|
|
highdet = id_to_eemg_det(EEMG_DET_GPU_HI);
|
|
highdet->features = doe_initmon & 0x6;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rc2)
|
|
det->volt_clamp = doe_clamp;
|
|
|
|
if ((!rc3) && (doe_offset != 0xFF)) {
|
|
if (doe_offset < 1000)
|
|
det->volt_offset = doe_offset;
|
|
else
|
|
det->volt_offset = 0 - (doe_offset - 1000);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
static int eemg_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
const phandle *ph;
|
|
struct eemg_det *det;
|
|
struct eemg_ctrl *ctrl;
|
|
struct device_node *node, *node_infra;
|
|
|
|
#if SUPPORT_DCONFIG
|
|
int doe_status;
|
|
#endif
|
|
|
|
FUNC_ENTER(FUNC_LV_MODULE);
|
|
|
|
ret = get_devinfo(pdev);
|
|
if (!ret)
|
|
return ret;
|
|
|
|
node = pdev->dev.of_node;
|
|
if (!node) {
|
|
eemg_error("get eemg device node err\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
#if SUPPORT_DCONFIG
|
|
if (of_property_read_u32(node, "eemg-status",
|
|
&doe_status) < 0) {
|
|
eemg_debug("[DCONFIG] eemg-status read error!\n");
|
|
} else {
|
|
eemg_debug("[DCONFIG] success-> status:%d, EEMG_Enable:%d\n",
|
|
doe_status, ctrl_EEMG_Enable);
|
|
if (((doe_status == 1) || (doe_status == 0)) &&
|
|
(ctrl_EEMG_Enable != doe_status)) {
|
|
ctrl_EEMG_Enable = doe_status;
|
|
eemg_error("[DCONFIG] status modified by DT(0x%x).\n",
|
|
doe_status);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ctrl_EEMG_Enable == 0) {
|
|
eemg_error("ctrl_EEMG_Enable = 0\n");
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
return 0;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_GPU_SUPPORT)
|
|
if (mt_gpufreq_not_ready()) {
|
|
eemg_error("Check gpu status for EEMGPU\n");
|
|
return EPROBE_DEFER;
|
|
}
|
|
#endif
|
|
/* Setup IO addresses */
|
|
eemg_base = of_iomap(node, 0);
|
|
eemg_debug("[EEMG] eemg_base = 0x%p\n", eemg_base);
|
|
eemg_irq_number = irq_of_parse_and_map(node, 0);
|
|
eemg_debug("[THERM_CTRL] eemg_irq_number=%d\n", eemg_irq_number);
|
|
if (!eemg_irq_number) {
|
|
eemg_error("[EEMG] get irqnr failed=0x%x\n", eemg_irq_number);
|
|
return 0;
|
|
}
|
|
|
|
ph = of_get_property(node, "infracfg", NULL);
|
|
if (!ph) {
|
|
eemg_error("infracfg failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
node_infra = of_find_node_by_phandle(be32_to_cpup(ph));
|
|
if (!node_infra) {
|
|
eemg_debug("infracfg Mapping Failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
infra_base_gpu = of_iomap(node_infra, 0);
|
|
if (!infra_base_gpu) {
|
|
eemg_debug("infra_ao Map Failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
create_procfs(pdev);
|
|
|
|
/* set EEM IRQ */
|
|
ret = request_irq(eemg_irq_number, eemg_isr,
|
|
IRQF_TRIGGER_HIGH, "eemg", NULL);
|
|
if (ret) {
|
|
eemg_error("EEMG IRQ register failed (%d)\n", ret);
|
|
WARN_ON(1);
|
|
}
|
|
eemg_debug("Set EEMG IRQ OK.\n");
|
|
|
|
#ifdef CONFIG_EEMG_AEE_RR_REC
|
|
_mt_eemg_aee_init();
|
|
#endif
|
|
|
|
for_each_ctrl(ctrl)
|
|
eemg_init_ctrl(ctrl);
|
|
|
|
eemg_debug("finish eemg_init_ctrl\n");
|
|
|
|
/* for slow idle */
|
|
for_each_det(det)
|
|
eemg_init_det(det, &eemg_devinfo, pdev);
|
|
|
|
|
|
final_init01_flag = EEMG_INIT01_FLAG;
|
|
|
|
/* get original volt from cpu dvfs before init01*/
|
|
for_each_det(det) {
|
|
if (det->ops->get_orig_volt_table_gpu)
|
|
det->ops->get_orig_volt_table_gpu(det);
|
|
}
|
|
|
|
#if SUPPORT_DCONFIG
|
|
for_each_det(det)
|
|
eemg_dconfig_set_det(det, node);
|
|
#endif
|
|
|
|
eemg_init01_gpu();
|
|
|
|
eemg_debug("%s ok\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
return 0;
|
|
}
|
|
|
|
static int eemg_suspend(void)
|
|
{
|
|
struct eemg_det *det;
|
|
unsigned long flag;
|
|
|
|
if (ctrl_EEMG_Enable) {
|
|
eemg_error("Start EEM suspend\n");
|
|
mt_ptpgpu_lock(&flag);
|
|
for_each_det(det) {
|
|
det->ops->switch_bank_gpu(det, NR_EEMG_PHASE);
|
|
eemg_write(EEMGEN, 0x0 | SEC_MOD_SEL);
|
|
/* clear all pending EEM int & config EEMGINTEN */
|
|
eemg_write(EEMGINTSTS, 0xffffffff);
|
|
}
|
|
mt_ptpgpu_unlock(&flag);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eemg_resume(void)
|
|
{
|
|
if (ctrl_EEMG_Enable) {
|
|
eemg_error("Start EEM resume\n");
|
|
/* Reset EEM */
|
|
eemg_write(INFRA_EEMG_RST, (1 << 5));
|
|
eemg_write(INFRA_EEMG_CLR, (1 << 5));
|
|
eemg_init02_gpu(__func__);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id mt_eemg_of_match[] = {
|
|
{ .compatible = "mediatek,eemgpu_fsm", },
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver eemg_driver = {
|
|
.remove = NULL,
|
|
.shutdown = NULL,
|
|
.probe = eemg_probe,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
.driver = {
|
|
.name = "mt-eemgpu",
|
|
.of_match_table = mt_eemg_of_match,
|
|
},
|
|
};
|
|
|
|
int mt_eemg_opp_num(enum eemg_det_id id)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
|
|
FUNC_ENTER(FUNC_LV_API);
|
|
if (det == NULL)
|
|
return 0;
|
|
|
|
FUNC_EXIT(FUNC_LV_API);
|
|
|
|
return det->num_freq_tbl;
|
|
}
|
|
EXPORT_SYMBOL(mt_eemg_opp_num);
|
|
|
|
void mt_eemg_opp_freq(enum eemg_det_id id, unsigned int *freq)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
int i = 0;
|
|
|
|
FUNC_ENTER(FUNC_LV_API);
|
|
|
|
if (det == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
freq[i] = det->freq_tbl[i];
|
|
|
|
FUNC_EXIT(FUNC_LV_API);
|
|
}
|
|
EXPORT_SYMBOL(mt_eemg_opp_freq);
|
|
|
|
void mt_eemg_opp_status(enum eemg_det_id id, unsigned int *temp,
|
|
unsigned int *volt)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
int i = 0;
|
|
|
|
FUNC_ENTER(FUNC_LV_API);
|
|
|
|
#ifdef CONFIG_THERMAL
|
|
if (id == EEMG_DET_GPU)
|
|
*temp = tscpu_get_temp_by_bank(THERMAL_BANK4);
|
|
else if (id == EEMG_DET_GPU_HI)
|
|
*temp = tscpu_get_temp_by_bank(THERMAL_BANK4);
|
|
else
|
|
*temp = tscpu_get_temp_by_bank(THERMAL_BANK4);
|
|
#else
|
|
*temp = 0;
|
|
#endif
|
|
|
|
if (det == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
volt[i] = det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[i]);
|
|
|
|
FUNC_EXIT(FUNC_LV_API);
|
|
}
|
|
EXPORT_SYMBOL(mt_eemg_opp_status);
|
|
|
|
/***************************
|
|
* return current EEM stauts
|
|
***************************
|
|
*/
|
|
int mt_eemg_status(enum eemg_det_id id)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
|
|
FUNC_ENTER(FUNC_LV_API);
|
|
if (det == NULL)
|
|
return 0;
|
|
else if (det->ops == NULL)
|
|
return 0;
|
|
else if (det->ops->get_status_gpu == NULL)
|
|
return 0;
|
|
|
|
FUNC_EXIT(FUNC_LV_API);
|
|
|
|
return det->ops->get_status_gpu(det);
|
|
}
|
|
|
|
/**
|
|
* ===============================================
|
|
* PROCFS interface for debugging
|
|
* ===============================================
|
|
*/
|
|
|
|
/*
|
|
* show current EEM stauts
|
|
*/
|
|
static int eemg_debug_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
struct eemg_det *det = (struct eemg_det *)m->private;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
/* FIXME: EEMGEN sometimes is disabled temp */
|
|
seq_printf(m, "[%s] %s (%d)\n",
|
|
((char *)(det->name) + 8),
|
|
det->disabled ? "disabled" : "enable",
|
|
det->ops->get_status_gpu(det)
|
|
);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* set EEM status by procfs interface
|
|
*/
|
|
static ssize_t eemg_debug_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
int ret;
|
|
int enabled = 0;
|
|
char *buf = (char *) __get_free_page(GFP_USER);
|
|
struct eemg_det *det = (struct eemg_det *)PDE_DATA(file_inode(file));
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (!buf) {
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (count >= PAGE_SIZE)
|
|
goto out;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(buf, buffer, count))
|
|
goto out;
|
|
eemg_debug("in eem debug proc write 2~~~~~~~~\n");
|
|
|
|
buf[count] = '\0';
|
|
|
|
if (!kstrtoint(buf, 10, &enabled)) {
|
|
ret = 0;
|
|
|
|
eemg_debug("in eem debug proc write 3~~~~~~~~\n");
|
|
if (enabled == 0)
|
|
/* det->ops->enable(det, BY_PROCFS); */
|
|
det->ops->disable_gpu(det, 0);
|
|
else if (enabled == 1)
|
|
det->ops->disable_gpu(det, BY_PROCFS);
|
|
} else
|
|
ret = -EINVAL;
|
|
|
|
out:
|
|
eemg_debug("in eem debug proc write 4~~~~~~~~\n");
|
|
free_page((unsigned long)buf);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return (ret < 0) ? ret : count;
|
|
}
|
|
|
|
/*
|
|
* show current aging margin
|
|
*/
|
|
static int eemg_setmargin_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
struct eemg_det *det = (struct eemg_det *)m->private;
|
|
int i;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
/* FIXME: EEMGEN sometimes is disabled temp */
|
|
seq_printf(m, "[%s] volt clamp:%d\n",
|
|
((char *)(det->name) + 8),
|
|
det->volt_clamp);
|
|
|
|
for (i = 0; i < det->num_freq_tbl; i++) {
|
|
seq_printf(m, "[Opp%d] aging margin:%d\n",
|
|
i, det->volt_aging[i]);
|
|
}
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* remove aging margin
|
|
*/
|
|
static ssize_t eemg_setmargin_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
int ret;
|
|
int aging_val[2];
|
|
int i = 0;
|
|
int start_oft, end_oft;
|
|
char *buf = (char *) __get_free_page(GFP_USER);
|
|
struct eemg_det *det = (struct eemg_det *)PDE_DATA(file_inode(file));
|
|
char *tok;
|
|
char *cmd_str = NULL;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (!buf) {
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (count >= PAGE_SIZE)
|
|
goto out;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(buf, buffer, count))
|
|
goto out;
|
|
|
|
buf[count] = '\0';
|
|
|
|
cmd_str = strsep(&buf, " ");
|
|
if (cmd_str == NULL)
|
|
ret = -EINVAL;
|
|
|
|
while ((tok = strsep(&buf, " ")) != NULL) {
|
|
if (i == 3) {
|
|
eemg_error("number of arguments > 3!\n");
|
|
goto out;
|
|
}
|
|
|
|
if (kstrtoint(tok, 10, &aging_val[i])) {
|
|
eemg_error("Invalid input: %s\n", tok);
|
|
goto out;
|
|
} else
|
|
i++;
|
|
}
|
|
|
|
if (!strncmp(cmd_str, "aging", sizeof("aging"))) {
|
|
start_oft = aging_val[0];
|
|
end_oft = aging_val[1];
|
|
eemg_calculate_aging_margin(det, start_oft, end_oft);
|
|
|
|
ret = count;
|
|
} else if (!strncmp(cmd_str, "clamp", sizeof("clamp"))) {
|
|
if (aging_val[0] < 20)
|
|
det->volt_clamp = aging_val[0];
|
|
|
|
ret = count;
|
|
} else if (!strncmp(cmd_str, "setopp", sizeof("setopp"))) {
|
|
if ((aging_val[0] >= 0) && (aging_val[0] < det->num_freq_tbl))
|
|
det->volt_aging[aging_val[0]] = aging_val[1];
|
|
|
|
ret = count;
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
eemg_set_eemg_volt(det);
|
|
|
|
out:
|
|
eemg_debug("in eem debug proc write 4~~~~~~~~\n");
|
|
free_page((unsigned long)buf);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* show current EEM data
|
|
*/
|
|
void eemg_dump_reg_by_det(struct eemg_det *det, struct seq_file *m)
|
|
{
|
|
unsigned int i, k;
|
|
#if DUMP_DATA_TO_DE
|
|
unsigned int j;
|
|
#endif
|
|
|
|
for (i = EEMG_PHASE_INIT01; i <= EEMG_PHASE_MON; i++) {
|
|
seq_printf(m, "Bank_number = %d\n", det->ctrl_id);
|
|
if (i < EEMG_PHASE_MON)
|
|
seq_printf(m, "mode = init%d\n", i+1);
|
|
else
|
|
seq_puts(m, "mode = mon\n");
|
|
if (eemg_log_en) {
|
|
seq_printf(m, "0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n",
|
|
det->dcvalues[i],
|
|
det->freqpct30[i],
|
|
det->eemg_26c[i],
|
|
det->vop30[i],
|
|
det->eemg_eemEn[i]
|
|
);
|
|
|
|
if (det->eemg_eemEn[i] == (0x5 | SEC_MOD_SEL)) {
|
|
seq_printf(m, "EEMG_LOG: Bank_number = [%d] (%d) - (",
|
|
det->ctrl_id, det->ops->get_temp_gpu(det));
|
|
|
|
for (k = 0; k < det->num_freq_tbl - 1; k++)
|
|
seq_printf(m, "%d, ",
|
|
det->ops->pmic_2_volt_gpu(det,
|
|
det->volt_tbl_pmic[k]));
|
|
seq_printf(m, "%d) - (",
|
|
det->ops->pmic_2_volt_gpu(det,
|
|
det->volt_tbl_pmic[k]));
|
|
|
|
for (k = 0; k < det->num_freq_tbl - 1; k++)
|
|
seq_printf(m, "%d, ", det->freq_tbl[k]);
|
|
seq_printf(m, "%d)\n", det->freq_tbl[k]);
|
|
}
|
|
} /* if (eemg_log_en) */
|
|
#if DUMP_DATA_TO_DE
|
|
for (j = 0; j < ARRAY_SIZE(reg_gpu_addr_off); j++)
|
|
seq_printf(m, "0x%08lx = 0x%08x\n",
|
|
(unsigned long)EEMG_BASEADDR + reg_gpu_addr_off[j],
|
|
det->reg_dump_data[j][i]
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static int eemg_dump_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
int *val = (int *)&eemg_devinfo;
|
|
struct eemg_det *det;
|
|
int i;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
/* Depend on EFUSE location */
|
|
for (i = 0; i < sizeof(struct eemg_devinfo) / sizeof(unsigned int);
|
|
i++)
|
|
seq_printf(m, "M_HW_RES%d\t= 0x%08X\n", i, val[i]);
|
|
|
|
for_each_det(det) {
|
|
eemg_dump_reg_by_det(det, m);
|
|
}
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* show current voltage
|
|
*/
|
|
static int eemg_cur_volt_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
struct eemg_det *det = (struct eemg_det *)m->private;
|
|
u32 rdata = 0, i;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
rdata = det->ops->get_volt_gpu(det);
|
|
|
|
if (rdata != 0)
|
|
seq_printf(m, "%d\n", rdata);
|
|
else
|
|
seq_printf(m, "EEM[%s] read current voltage fail\n", det->name);
|
|
|
|
if (det->features != 0) {
|
|
for (i = 0; i < det->num_freq_tbl; i++)
|
|
seq_printf(m, "[%d],eem = [%x], pmic = [%x], volt = [%d]\n",
|
|
i,
|
|
det->volt_tbl[i],
|
|
det->volt_tbl_pmic[i],
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[i]));
|
|
|
|
seq_printf(m, "policy:%d, isTempInv:%d\n",
|
|
det->volt_policy, det->isTempInv);
|
|
}
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* show current EEM status
|
|
*/
|
|
static int eemg_status_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
int i;
|
|
struct eemg_det *det = (struct eemg_det *)m->private;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
seq_printf(m, "bank = %d, feature:%d, T(%d) - (",
|
|
det->ctrl_id, det->features, det->ops->get_temp_gpu(det));
|
|
for (i = 0; i < det->num_freq_tbl - 1; i++)
|
|
seq_printf(m, "%d, ", det->ops->pmic_2_volt_gpu(det,
|
|
det->volt_tbl_pmic[i]));
|
|
seq_printf(m, "%d) - (",
|
|
det->ops->pmic_2_volt_gpu(det, det->volt_tbl_pmic[i]));
|
|
|
|
for (i = 0; i < det->num_freq_tbl - 1; i++)
|
|
seq_printf(m, "%d, ", det->freq_tbl[i]);
|
|
seq_printf(m, "%d)\n", det->freq_tbl[i]);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
* set EEM log enable by procfs interface
|
|
*/
|
|
|
|
static int eemg_log_en_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
seq_printf(m, "%d\n", eemg_log_en);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t eemg_log_en_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
int ret;
|
|
char *buf = (char *) __get_free_page(GFP_USER);
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (!buf) {
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (count >= PAGE_SIZE)
|
|
goto out;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(buf, buffer, count))
|
|
goto out;
|
|
|
|
buf[count] = '\0';
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (kstrtoint(buf, 10, &eemg_log_en)) {
|
|
eemg_debug("bad argument!! Should be \"0\" or \"1\"\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
switch (eemg_log_en) {
|
|
case 0:
|
|
eemg_debug("eem log disabled.\n");
|
|
hrtimer_cancel(&eemg_log_timer);
|
|
break;
|
|
|
|
case 1:
|
|
eemg_debug("eem log enabled.\n");
|
|
hrtimer_start(&eemg_log_timer,
|
|
ns_to_ktime(LOG_INTERVAL), HRTIMER_MODE_REL);
|
|
break;
|
|
|
|
default:
|
|
eemg_debug("bad argument!! Should be \"0\" or \"1\"\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
out:
|
|
free_page((unsigned long)buf);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return (ret < 0) ? ret : count;
|
|
}
|
|
|
|
/*
|
|
* show EEM offset
|
|
*/
|
|
static int eemg_offset_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
struct eemg_det *det = (struct eemg_det *)m->private;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
seq_printf(m, "%d\n", det->volt_offset);
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* set EEM offset by procfs
|
|
*/
|
|
static ssize_t eemg_offset_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
int ret;
|
|
char *buf = (char *) __get_free_page(GFP_USER);
|
|
int offset = 0;
|
|
struct eemg_det *det = (struct eemg_det *)PDE_DATA(file_inode(file));
|
|
unsigned long flags;
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
if (!buf) {
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (count >= PAGE_SIZE)
|
|
goto out;
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_from_user(buf, buffer, count))
|
|
goto out;
|
|
|
|
buf[count] = '\0';
|
|
|
|
if (!kstrtoint(buf, 10, &offset)) {
|
|
ret = 0;
|
|
det->volt_offset = offset;
|
|
mt_ptpgpu_lock(&flags);
|
|
eemg_error("[%s]\n", __func__);
|
|
eemg_set_eemg_volt(det);
|
|
mt_ptpgpu_unlock(&flags);
|
|
} else {
|
|
ret = -EINVAL;
|
|
eemg_debug("bad argument_1!! argument should be \"0\"\n");
|
|
}
|
|
|
|
out:
|
|
free_page((unsigned long)buf);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
|
|
return (ret < 0) ? ret : count;
|
|
}
|
|
|
|
#define PROC_FOPS_RW(name) \
|
|
static int name ## _proc_open(struct inode *inode, \
|
|
struct file *file) \
|
|
{ \
|
|
return single_open(file, name ## _proc_show, \
|
|
PDE_DATA(inode)); \
|
|
} \
|
|
static const struct proc_ops name ## _proc_fops = { \
|
|
.proc_open = name ## _proc_open, \
|
|
.proc_read = seq_read, \
|
|
.proc_lseek = seq_lseek, \
|
|
.proc_release = single_release, \
|
|
.proc_write = name ## _proc_write, \
|
|
}
|
|
|
|
#define PROC_FOPS_RO(name) \
|
|
static int name ## _proc_open(struct inode *inode, \
|
|
struct file *file) \
|
|
{ \
|
|
return single_open(file, name ## _proc_show, \
|
|
PDE_DATA(inode)); \
|
|
} \
|
|
static const struct proc_ops name ## _proc_fops = { \
|
|
.proc_open = name ## _proc_open, \
|
|
.proc_read = seq_read, \
|
|
.proc_lseek = seq_lseek, \
|
|
.proc_release = single_release, \
|
|
}
|
|
|
|
#define PROC_ENTRY(name) {__stringify(name), &name ## _proc_fops}
|
|
|
|
PROC_FOPS_RW(eemg_debug);
|
|
PROC_FOPS_RO(eemg_status);
|
|
PROC_FOPS_RO(eemg_cur_volt);
|
|
PROC_FOPS_RW(eemg_offset);
|
|
PROC_FOPS_RO(eemg_dump);
|
|
PROC_FOPS_RW(eemg_log_en);
|
|
PROC_FOPS_RW(eemg_setmargin);
|
|
|
|
//static int create_procfs(void)
|
|
static int create_procfs(struct platform_device *pdev)
|
|
{
|
|
struct proc_dir_entry *eemg_dir = NULL;
|
|
struct proc_dir_entry *det_dir = NULL;
|
|
int i;
|
|
struct eemg_det *det;
|
|
|
|
struct pentry {
|
|
const char *name;
|
|
const struct proc_ops *fops;
|
|
};
|
|
|
|
struct pentry det_entries[] = {
|
|
PROC_ENTRY(eemg_debug),
|
|
PROC_ENTRY(eemg_status),
|
|
PROC_ENTRY(eemg_cur_volt),
|
|
PROC_ENTRY(eemg_offset),
|
|
PROC_ENTRY(eemg_setmargin),
|
|
};
|
|
|
|
struct pentry eemg_entries[] = {
|
|
PROC_ENTRY(eemg_dump),
|
|
PROC_ENTRY(eemg_log_en),
|
|
};
|
|
|
|
FUNC_ENTER(FUNC_LV_HELP);
|
|
|
|
/* create procfs root /proc/eem */
|
|
eemg_dir = proc_mkdir("eemg", NULL);
|
|
|
|
if (!eemg_dir) {
|
|
eemg_error("[%s]: mkdir /proc/eemg failed\n", __func__);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -1;
|
|
}
|
|
|
|
/* if ctrl_EEMG_Enable =1, and has efuse value,
|
|
* create other banks procfs
|
|
*/
|
|
if (ctrl_EEMG_Enable != 0 && eemg_checkEfuse == 1) {
|
|
for (i = 0; i < ARRAY_SIZE(eemg_entries); i++) {
|
|
if (!proc_create(eemg_entries[i].name, 0664,
|
|
eemg_dir, eemg_entries[i].fops)) {
|
|
eemg_error("[%s]: create /proc/eem/%s failed\n",
|
|
__func__,
|
|
eemg_entries[i].name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -3;
|
|
}
|
|
}
|
|
|
|
for_each_det(det) {
|
|
if (det->features == 0)
|
|
continue;
|
|
|
|
det_dir = proc_mkdir(det->name, eemg_dir);
|
|
|
|
if (!det_dir) {
|
|
eemg_debug("[%s]: mkdir /proc/eemg/%s failed\n"
|
|
, __func__, det->name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -2;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(det_entries); i++) {
|
|
if (!proc_create_data(det_entries[i].name,
|
|
0664,
|
|
det_dir,
|
|
det_entries[i].fops, det)) {
|
|
eemg_debug
|
|
("[%s]: create /proc/eemg/%s/%s failed\n", __func__,
|
|
det->name, det_entries[i].name);
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return -3;
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* if (ctrl_EEMG_Enable != 0) */
|
|
|
|
FUNC_EXIT(FUNC_LV_HELP);
|
|
return 0;
|
|
}
|
|
|
|
void eemg_set_pi_efuse(enum eemg_det_id id,
|
|
unsigned int pi_efuse,
|
|
unsigned int loo_enabled)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
|
|
if (!det)
|
|
return;
|
|
|
|
det->pi_loo_enabled = loo_enabled;
|
|
det->pi_efuse = pi_efuse;
|
|
}
|
|
|
|
void eemg_set_pi_dvtfixed(enum eemg_det_id id, unsigned int pi_dvtfixed)
|
|
{
|
|
struct eemg_det *det = id_to_eemg_det(id);
|
|
|
|
if (!det)
|
|
return;
|
|
|
|
det->pi_dvtfixed = pi_dvtfixed;
|
|
}
|
|
|
|
static int eemg_pm_event(struct notifier_block *notifier,
|
|
unsigned long pm_event, void *unused)
|
|
{
|
|
switch (pm_event) {
|
|
case PM_SUSPEND_PREPARE:
|
|
eemg_suspend();
|
|
return NOTIFY_DONE;
|
|
case PM_POST_SUSPEND:
|
|
eemg_resume();
|
|
return NOTIFY_DONE;
|
|
}
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block eemg_pm_notifier_func = {
|
|
.notifier_call = eemg_pm_event,
|
|
.priority = 0,
|
|
};
|
|
|
|
/*
|
|
* Module driver
|
|
*/
|
|
static int __init eemg_init(void)
|
|
{
|
|
int err = 0;
|
|
|
|
eemg_debug("[EEM] ctrl_EEMG_Enable=%d\n", ctrl_EEMG_Enable);
|
|
|
|
/* move to eemg_probe */
|
|
/* create_procfs(); */
|
|
// FIX ME
|
|
// if (eemg_checkEfuse == 1) {
|
|
|
|
if (eemg_checkEfuse == 0) {
|
|
eemg_error("eemg_checkEfuse = 0\n");
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
return 0;
|
|
}
|
|
|
|
/* init timer for log / volt */
|
|
hrtimer_init(&eemg_log_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
eemg_log_timer.function = eemg_log_timer_func;
|
|
|
|
/*
|
|
* reg platform device driver
|
|
*/
|
|
err = platform_driver_register(&eemg_driver);
|
|
|
|
if (err) {
|
|
eemg_debug("EEM driver callback register failed..\n");
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
return err;
|
|
}
|
|
|
|
err = register_pm_notifier(&eemg_pm_notifier_func);
|
|
if (err) {
|
|
eemg_error("Failed to register PM notifier.\n");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit eemg_exit(void)
|
|
{
|
|
FUNC_ENTER(FUNC_LV_MODULE);
|
|
eemg_error("eem de-initialization\n");
|
|
FUNC_EXIT(FUNC_LV_MODULE);
|
|
}
|
|
|
|
//late_initcall(eemg_init); /* late_initcall */
|
|
module_init(eemg_init);
|
|
module_exit(eemg_exit);
|
|
#endif /* EN_EEM */
|
|
|
|
MODULE_DESCRIPTION("MediaTek EEM Driver v0.3");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#undef __MTK_EEMG_C__
|