1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-02-15 00:18:03 +00:00
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

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__