// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_plat.h" #endif #include #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__