/* Copyright Statement: * * This software/firmware and related documentation ("MediaTek Software") are * protected under relevant copyright laws. The information contained herein * is confidential and proprietary to MediaTek Inc. and/or its licensors. * Without the prior written permission of MediaTek inc. and/or its licensors, * any reproduction, modification, use or disclosure of MediaTek Software, * and information contained herein, in whole or in part, shall be strictly prohibited. */ /* MediaTek Inc. (C) 2017. All rights reserved. * * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. */ #include #include #include #include #include #include #include #include #include #include #include #include #define late_initcall(a) #define DEFINE_MUTEX(a) #define mutex_lock(a) #define mutex_unlock(a) #define IOMEM(a) (a) extern unsigned long mt_secure_call(unsigned long, unsigned long, unsigned long, unsigned long); #define MTK_SIP_KERNEL_MCSI_NS_ACCESS 0x8200028B #define mcsi_reg_read(offset) \ mt_secure_call(MTK_SIP_KERNEL_MCSI_NS_ACCESS, 0, offset, 0) #define mcsi_reg_write(val, offset) \ mt_secure_call(MTK_SIP_KERNEL_MCSI_NS_ACCESS, 1, offset, val) #define MTK_SIP_KERNEL_DCM 0x82000230 #define dcm_smc_msg(init_type) \ mt_secure_call(MTK_SIP_KERNEL_DCM, init_type, 0, 0) #define dcm_smc_read_cnt(type) \ mt_secure_call(MTK_SIP_KERNEL_DCM, type, 1, 0) #define __raw_readl(addr) DRV_Reg32(addr) #define reg_read(addr) __raw_readl(IOMEM(addr)) #define reg_write(addr, val) mt_reg_sync_writel((val), ((void *)addr)) #define MCUSYS_SMC_WRITE(addr, val) reg_write(addr, val) #define MCSI_SMC_WRITE(addr, val) mcsi_reg_write(val, (addr##_PHYS & 0xFFFF)) #define MCSI_SMC_READ(addr) mcsi_reg_read(addr##_PHYS & 0xFFFF) #define dcm_smc_msg_send(msg) dcm_smc_msg(msg) #define dcm_err(fmt, args...) dprintf(CRITICAL, fmt, ##args) #define dcm_warn(fmt, args...) dprintf(CRITICAL, fmt, ##args) #define dcm_info(fmt, args...) dprintf(INFO, fmt, ##args) #define dcm_dbg(fmt, args...) dprintf(SPEW, fmt, ##args) #define dcm_ver(fmt, args...) dprintf(SPEW, fmt, ##args) #define REG_DUMP(addr) dcm_info("%-30s(0x%08x): 0x%08x\n", #addr, addr, reg_read(addr)) #define SECURE_REG_DUMP(addr) dcm_info("%-30s(0x%08x): 0x%08lx\n", #addr, addr, mcsi_reg_read(addr##_PHYS & 0xFFFF)) /** macro **/ #define and(v, a) ((v) & (a)) #define or(v, o) ((v) | (o)) #define aor(v, a, o) (((v) & (a)) | (o)) /** global **/ static short dcm_initiated; #ifdef CTRL_BIGCORE_DCM_IN_KERNEL static short dcm_cpu_cluster_stat; #endif static unsigned int all_dcm_type = (ARMCORE_DCM_TYPE | MCUSYS_DCM_TYPE | INFRA_DCM_TYPE | EMI_DCM_TYPE | DRAMC_DCM_TYPE ); static unsigned int init_dcm_type = (ARMCORE_DCM_TYPE | MCUSYS_DCM_TYPE | INFRA_DCM_TYPE ); /***************************************** * following is implementation per DCM module. * 1. per-DCM function is 1-argu with ON/OFF/MODE option. *****************************************/ typedef int (*DCM_FUNC)(int); typedef void (*DCM_PRESET_FUNC)(void); int dcm_topckg(ENUM_TOPCKG_DCM on) { return 0; } void dcm_infracfg_ao_emi_indiv(int on) { } int dcm_infra(ENUM_INFRA_DCM on) { /* dcm_infracfg_ao_dcm_dfs_mem_ctrl(on); */ /* dcm_infracfg_ao_dcm_mem_ctrl(on); */ dcm_infracfg_ao_dcm_infra_bus(on); dcm_infracfg_ao_dcm_peri_bus(on); dcm_infracfg_ao_dcm_top_p2p_rx_ck(on); return 0; } int dcm_peri(ENUM_PERI_DCM on) { return 0; } int dcm_armcore(ENUM_ARMCORE_DCM mode) { dcm_topckgen_ao_mcu_armpll_ca7ll(mode); return 0; } int dcm_mcusys(ENUM_MCUSYS_DCM on) { dcm_mcucfg_bus_clock_dcm(on); dcm_mcucfg_bus_fabric_dcm(on); dcm_mcucfg_l2_shared_dcm(on); dcm_mcucfg_mcu_misc_dcm(on); return 0; } int dcm_big_core(ENUM_BIG_CORE_DCM on) { return 0; } int dcm_stall_preset(void) { return 0; } int dcm_stall(ENUM_STALL_DCM on) { return 0; } int dcm_gic_sync(ENUM_GIC_SYNC_DCM on) { return 0; } int dcm_last_core(ENUM_LAST_CORE_DCM on) { return 0; } int dcm_rgu(ENUM_RGU_DCM on) { return 0; } int dcm_dramc_ao(ENUM_DRAMC_AO_DCM on) { dcm_dramc_dramc_dcm(on); return 0; } int dcm_ddrphy(ENUM_DDRPHY_DCM on) { return 0; } int dcm_emi(ENUM_EMI_DCM on) { dcm_emi_dcm_emi_group(on); dcm_chn0_emi_dcm_emi_group(on); return 0; } int dcm_lpdma(ENUM_LPDMA_DCM on) { return 0; } /*****************************************************/ typedef struct _dcm { int current_state; int saved_state; int disable_refcnt; int default_state; DCM_FUNC func; DCM_PRESET_FUNC preset_func; int typeid; char *name; } DCM; static DCM dcm_array[NR_DCM_TYPE] = { { .typeid = ARMCORE_DCM_TYPE, .name = "ARMCORE_DCM", .func = (DCM_FUNC) dcm_armcore, .current_state = ARMCORE_DCM_MODE1, .default_state = ARMCORE_DCM_MODE1, .disable_refcnt = 0, }, { .typeid = MCUSYS_DCM_TYPE, .name = "MCUSYS_DCM", .func = (DCM_FUNC) dcm_mcusys, .current_state = MCUSYS_DCM_ON, .default_state = MCUSYS_DCM_ON, .disable_refcnt = 0, }, { .typeid = INFRA_DCM_TYPE, .name = "INFRA_DCM", .func = (DCM_FUNC) dcm_infra, /*.preset_func = (DCM_PRESET_FUNC) dcm_infra_preset,*/ .current_state = INFRA_DCM_ON, .default_state = INFRA_DCM_ON, .disable_refcnt = 0, }, { .typeid = PERI_DCM_TYPE, .name = "PERI_DCM", .func = (DCM_FUNC) dcm_peri, /*.preset_func = (DCM_PRESET_FUNC) dcm_peri_preset,*/ .current_state = PERI_DCM_ON, .default_state = PERI_DCM_ON, .disable_refcnt = 0, }, { .typeid = EMI_DCM_TYPE, .name = "EMI_DCM", .func = (DCM_FUNC) dcm_emi, .current_state = EMI_DCM_ON, .default_state = EMI_DCM_ON, .disable_refcnt = 0, }, { .typeid = DRAMC_DCM_TYPE, .name = "DRAMC_DCM", .func = (DCM_FUNC) dcm_dramc_ao, .current_state = DRAMC_AO_DCM_ON, .default_state = DRAMC_AO_DCM_ON, .disable_refcnt = 0, }, { .typeid = DDRPHY_DCM_TYPE, .name = "DDRPHY_DCM", .func = (DCM_FUNC) dcm_ddrphy, .current_state = DDRPHY_DCM_ON, .default_state = DDRPHY_DCM_ON, .disable_refcnt = 0, }, { .typeid = STALL_DCM_TYPE, .name = "STALL_DCM", .func = (DCM_FUNC) dcm_stall, .preset_func = (DCM_PRESET_FUNC) dcm_stall_preset, .current_state = STALL_DCM_ON, .default_state = STALL_DCM_ON, .disable_refcnt = 0, }, { .typeid = BIG_CORE_DCM_TYPE, .name = "BIG_CORE_DCM", .func = (DCM_FUNC) dcm_big_core, .current_state = BIG_CORE_DCM_ON, .default_state = BIG_CORE_DCM_ON, .disable_refcnt = 0, }, { .typeid = GIC_SYNC_DCM_TYPE, .name = "GIC_SYNC_DCM", .func = (DCM_FUNC) dcm_gic_sync, .current_state = GIC_SYNC_DCM_ON, .default_state = GIC_SYNC_DCM_ON, .disable_refcnt = 0, }, { .typeid = LAST_CORE_DCM_TYPE, .name = "LAST_CORE_DCM", .func = (DCM_FUNC) dcm_last_core, .current_state = LAST_CORE_DCM_ON, .default_state = LAST_CORE_DCM_ON, .disable_refcnt = 0, }, { .typeid = RGU_DCM_TYPE, .name = "RGU_CORE_DCM", .func = (DCM_FUNC) dcm_rgu, .current_state = RGU_DCM_ON, .default_state = RGU_DCM_ON, .disable_refcnt = 0, }, { .typeid = TOPCKG_DCM_TYPE, .name = "TOPCKG_DCM", .func = (DCM_FUNC) dcm_topckg, .current_state = TOPCKG_DCM_ON, .default_state = TOPCKG_DCM_ON, .disable_refcnt = 0, }, { .typeid = LPDMA_DCM_TYPE, .name = "LPDMA_DCM", .func = (DCM_FUNC) dcm_lpdma, .current_state = LPDMA_DCM_ON, .default_state = LPDMA_DCM_ON, .disable_refcnt = 0, }, }; /***************************************** * DCM driver will provide regular APIs : * 1. dcm_restore(type) to recovery CURRENT_STATE before any power-off reset. * 2. dcm_set_default(type) to reset as cold-power-on init state. * 3. dcm_disable(type) to disable all dcm. * 4. dcm_set_state(type) to set dcm state. * 5. dcm_dump_state(type) to show CURRENT_STATE. * 6. /sys/power/dcm_state interface: 'restore', 'disable', 'dump', 'set'. 4 commands. * * spsecified APIs for workaround: * 1. (definitely no workaround now) *****************************************/ void dcm_set_default(unsigned int type) { int i; DCM *dcm; #ifndef ENABLE_DCM_IN_LK dcm_warn("[%s]type:0x%X, init_dcm_type=0x%X\n", __func__, type, init_dcm_type); #else dcm_warn("[%s]type:0x%X, init_dcm_type=0x%X, INIT_DCM_TYPE_BY_K=0x%X\n", __func__, type, init_dcm_type, INIT_DCM_TYPE_BY_K); #endif mutex_lock(&dcm_lock); for (i = 0, dcm = &dcm_array[0]; i < NR_DCM_TYPE; i++, dcm++) { if (type & dcm->typeid) { dcm->saved_state = dcm->default_state; dcm->current_state = dcm->default_state; dcm->disable_refcnt = 0; #ifdef ENABLE_DCM_IN_LK if (INIT_DCM_TYPE_BY_K & dcm->typeid) { #endif if (dcm->preset_func) dcm->preset_func(); dcm->func(dcm->current_state); #ifdef ENABLE_DCM_IN_LK } #endif dcm_info("[%s 0x%X] current state:%d (%d)\n", dcm->name, dcm->typeid, dcm->current_state, dcm->disable_refcnt); } } dcm_smc_msg_send(init_dcm_type); mutex_unlock(&dcm_lock); } void dcm_set_state(unsigned int type, int state) { int i; DCM *dcm; unsigned int init_dcm_type_pre = init_dcm_type; dcm_warn("[%s]type:0x%X, set:%d, init_dcm_type_pre=0x%X\n", __func__, type, state, init_dcm_type_pre); mutex_lock(&dcm_lock); for (i = 0, dcm = &dcm_array[0]; type && (i < NR_DCM_TYPE); i++, dcm++) { if (type & dcm->typeid) { type &= ~(dcm->typeid); dcm->saved_state = state; if (dcm->disable_refcnt == 0) { if (state) init_dcm_type |= dcm->typeid; else init_dcm_type &= ~(dcm->typeid); dcm->current_state = state; dcm->func(dcm->current_state); } dcm_info("[%s 0x%X] current state:%d (%d)\n", dcm->name, dcm->typeid, dcm->current_state, dcm->disable_refcnt); } } if (init_dcm_type_pre != init_dcm_type) { dcm_warn("[%s]type:0x%X, set:%d, init_dcm_type=0x%X->0x%X\n", __func__, type, state, init_dcm_type_pre, init_dcm_type); dcm_smc_msg_send(init_dcm_type); } mutex_unlock(&dcm_lock); } void dcm_disable(unsigned int type) { int i; DCM *dcm; unsigned int init_dcm_type_pre = init_dcm_type; dcm_warn("[%s]type:0x%X\n", __func__, type); mutex_lock(&dcm_lock); for (i = 0, dcm = &dcm_array[0]; type && (i < NR_DCM_TYPE); i++, dcm++) { if (type & dcm->typeid) { type &= ~(dcm->typeid); dcm->current_state = DCM_OFF; if (dcm->disable_refcnt++ == 0) init_dcm_type &= ~(dcm->typeid); dcm->func(dcm->current_state); dcm_info("[%s 0x%X] current state:%d (%d)\n", dcm->name, dcm->typeid, dcm->current_state, dcm->disable_refcnt); } } if (init_dcm_type_pre != init_dcm_type) { dcm_warn("[%s]type:0x%X, init_dcm_type=0x%X->0x%X\n", __func__, type, init_dcm_type_pre, init_dcm_type); dcm_smc_msg_send(init_dcm_type); } mutex_unlock(&dcm_lock); } void dcm_restore(unsigned int type) { int i; DCM *dcm; unsigned int init_dcm_type_pre = init_dcm_type; dcm_warn("[%s]type:0x%X\n", __func__, type); mutex_lock(&dcm_lock); for (i = 0, dcm = &dcm_array[0]; type && (i < NR_DCM_TYPE); i++, dcm++) { if (type & dcm->typeid) { type &= ~(dcm->typeid); if (dcm->disable_refcnt > 0) dcm->disable_refcnt--; if (dcm->disable_refcnt == 0) { if (dcm->saved_state) init_dcm_type |= dcm->typeid; else init_dcm_type &= ~(dcm->typeid); dcm->current_state = dcm->saved_state; dcm->func(dcm->current_state); } dcm_info("[%s 0x%X] current state:%d (%d)\n", dcm->name, dcm->typeid, dcm->current_state, dcm->disable_refcnt); } } if (init_dcm_type_pre != init_dcm_type) { dcm_warn("[%s]type:0x%X, init_dcm_type=0x%X->0x%X\n", __func__, type, init_dcm_type_pre, init_dcm_type); dcm_smc_msg_send(init_dcm_type); } mutex_unlock(&dcm_lock); } void dcm_dump_state(int type) { int i; DCM *dcm; dcm_info("\n******** dcm dump state *********\n"); for (i = 0, dcm = &dcm_array[0]; i < NR_DCM_TYPE; i++, dcm++) { if (type & dcm->typeid) { dcm_info("[%s 0x%X] current state:%d (%d)\n", dcm->name, dcm->typeid, dcm->current_state, dcm->disable_refcnt); } } } void dcm_dump_regs(void) { dcm_info("\n******** dcm dump register *********\n"); REG_DUMP(INFRA_BUS_DCM_CTRL); REG_DUMP(PERI_BUS_DCM_CTRL); REG_DUMP(MEM_DCM_CTRL); REG_DUMP(DFS_MEM_DCM_CTRL); REG_DUMP(P2P_RX_CLK_ON); REG_DUMP(INFRA_TOPCKGEN_DCMCTL); REG_DUMP(DRAMC_DRAMC_PD_CTRL); REG_DUMP(L2C_SRAM_CTRL); REG_DUMP(CCI_CLK_CTRL); REG_DUMP(BUS_FABRIC_DCM_CTRL); REG_DUMP(MCU_MISC_DCM_CTRL); REG_DUMP(EMI_CONM); REG_DUMP(EMI_CONN); REG_DUMP(CHN0_EMI_CHN_EMI_CONB); REG_DUMP(EFUSEC_DCM_ON); } static int mt_dcm_dts_map(void) { return 0; } int mt_dcm_init(void) { #ifdef DCM_BRINGUP dcm_warn("%s: skipped for bring up\n", __func__); return 0; #endif if (dcm_initiated) return 0; if (mt_dcm_dts_map()) { dcm_err("%s: failed due to DTS failed\n", __func__); return -1; } #if 0 /* WORKAROUND: Disable big core reg protection */ reg_write(0x10202008, aor(reg_read(0x10202008), ~(0x3), 0x1)); dcm_info("%s: 0x10202008=0x%x\n", __func__, reg_read(0x10202008)); #endif #ifdef CTRL_BIGCORE_DCM_IN_KERNEL /* big ext buck iso power on */ reg_write(0x10B00260, reg_read(0x10B00260) & ~(0x1 << 2)); dcm_info("%s: 0x10B00260=0x%x\n", __func__, reg_read(0x10B00260)); dcm_cpu_cluster_stat |= DCM_CPU_CLUSTER_B; #endif #ifndef DCM_DEFAULT_ALL_OFF /** enable all dcm **/ dcm_set_default(init_dcm_type); #else /* DCM_DEFAULT_ALL_OFF */ dcm_set_state(all_dcm_type, DCM_OFF); #endif /* #ifndef DCM_DEFAULT_ALL_OFF */ dcm_dump_regs(); dcm_initiated = 1; return 0; } late_initcall(mt_dcm_init); /**** public APIs *****/ void mt_dcm_disable(void) { if (!dcm_initiated) return; dcm_disable(all_dcm_type); } void mt_dcm_restore(void) { if (!dcm_initiated) return; dcm_restore(all_dcm_type); } unsigned int sync_dcm_convert_freq2div(unsigned int freq) { unsigned int div = 0, min_freq = SYNC_DCM_CLK_MIN_FREQ; if (freq < min_freq) return 0; /* max divided ratio = Floor (CPU Frequency / (4 or 5) * system timer Frequency) */ div = (freq / min_freq) - 1; if (div > SYNC_DCM_MAX_DIV_VAL) return SYNC_DCM_MAX_DIV_VAL; return div; } int sync_dcm_set_cci_div(unsigned int cci) { return 0; } int sync_dcm_set_cci_freq(unsigned int cci) { dcm_dbg("%s: cci=%u\n", __func__, cci); sync_dcm_set_cci_div(sync_dcm_convert_freq2div(cci)); return 0; } int sync_dcm_set_mp0_div(unsigned int mp0) { return 0; } int sync_dcm_set_mp0_freq(unsigned int mp0) { dcm_dbg("%s: mp0=%u\n", __func__, mp0); sync_dcm_set_mp0_div(sync_dcm_convert_freq2div(mp0)); return 0; } int sync_dcm_set_mp1_div(unsigned int mp1) { return 0; } int sync_dcm_set_mp1_freq(unsigned int mp1) { dcm_dbg("%s: mp1=%u\n", __func__, mp1); sync_dcm_set_mp1_div(sync_dcm_convert_freq2div(mp1)); return 0; } int sync_dcm_set_mp2_div(unsigned int mp2) { return 0; } int sync_dcm_set_mp2_freq(unsigned int mp2) { return 0; } /* unit of frequency is MHz */ int sync_dcm_set_cpu_freq(unsigned int cci, unsigned int mp0, unsigned int mp1, unsigned int mp2) { sync_dcm_set_cci_freq(cci); sync_dcm_set_mp0_freq(mp0); sync_dcm_set_mp1_freq(mp1); sync_dcm_set_mp2_freq(mp2); return 0; } int sync_dcm_set_cpu_div(unsigned int cci, unsigned int mp0, unsigned int mp1, unsigned int mp2) { sync_dcm_set_cci_div(cci); sync_dcm_set_mp0_div(mp0); sync_dcm_set_mp1_div(mp1); sync_dcm_set_mp2_div(mp2); return 0; }