// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015-2020 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DBG_ENABLE #include #endif #ifdef CONFIG_MTK_HIBERNATION #include #endif /* CCF */ #include #include "devapc.h" #if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE) #include #endif /* 0 for early porting */ #define DEVAPC_TURN_ON 1 #define DEVAPC_USE_CCF 1 /* Debug message event */ #define DEVAPC_LOG_NONE 0x00000000 #define DEVAPC_LOG_INFO 0x00000001 #define DEVAPC_LOG_DBG 0x00000002 #define DEVAPC_LOG_LEVEL (DEVAPC_LOG_DBG) #define DEVAPC_MSG(fmt, args...) \ do { \ if (DEVAPC_LOG_LEVEL & DEVAPC_LOG_DBG) { \ pr_debug(fmt, ##args); \ } else if (DEVAPC_LOG_LEVEL & DEVAPC_LOG_INFO) { \ pr_info(fmt, ##args); \ } \ } while (0) #define DEVAPC_VIO_LEVEL (DEVAPC_LOG_INFO) #define DEVAPC_VIO_MSG(fmt, args...) \ do { \ if (DEVAPC_VIO_LEVEL & DEVAPC_LOG_DBG) { \ pr_debug_ratelimited(fmt, ##args); \ } else if (DEVAPC_VIO_LEVEL & DEVAPC_LOG_INFO) { \ pr_info_ratelimited(fmt, ##args); \ } \ } while (0) /* bypass clock! */ #if DEVAPC_USE_CCF static struct clk *dapc_infra_clk; #endif static struct cdev *g_devapc_ctrl; static unsigned int devapc_infra_irq; static void __iomem *devapc_pd_infra_base; static unsigned int enable_dynamic_one_core_violation_debug; #if DEVAPC_TURN_ON static struct DEVICE_INFO devapc_infra_devices[] = { /* device name enable_vio_irq */ /* 0 */ {"INFRA_AO_TOPCKGEN", true }, {"INFRA_AO_INFRASYS_CONFIG_REGS", true }, {"IO_CFG_REG", true }, {"INFRA_AO_ PERICFG", true }, {"INFRA_AO_EFUSE_AO_DEBUG", true }, {"INFRA_AO_GPIO", true }, {"INFRA_AO_SLEEP_CONTROLLER", true }, {"INFRA_AO_TOPRGU", true }, {"INFRA_AO_APXGPT", true }, {"INFRA_AO_RESERVE", true }, /* 10 */ {"INFRA_AO_SEJ", true }, {"INFRA_AO_AP_CIRQ_EINT", true }, {"INFRA_AO_APMIXEDSYS", true }, {"INFRA_AO_PMIC_WRAP", true }, {"INFRA_AO_DEVICE_APC_AO_INFRA_PERI", true }, {"INFRA_AO_SLEEP_CONTROLLER_MD", true }, {"INFRA_AO_KEYPAD", true }, {"INFRA_AO_TOP_MISC", true }, {"INFRA_AO_ DVFS_CTRL_PROC", true }, {"INFRA_AO_MBIST_AO_REG", true }, /* 20 */ {"INFRA_AO_CLDMA_AO_AP", true }, {"INFRA_AO_RESERVE", true }, {"INFRA_AO_AES_TOP_0", true }, {"INFRA_AO_SYS_TIMER", true }, {"INFRA_AO_MDEM_TEMP_SHARE", true }, {"INFRA_AO_DEVICE_APC_AO_MD", true }, {"INFRA_AO_SECURITY_AO", true }, {"INFRA_AO_TOPCKGEN_REG", true }, {"INFRA_AO_DEVICE_APC_AO_MM", true }, {"INFRA_AO_DRAMC_REG", true }, /* 30 */ {"INFRA_AO_DDRPHY_REG", true }, {"INFRA_AO_RESERVE", true }, {"INFRASYS_MCUSYS_REG0", true }, {"INFRASYS_MCUSYS_REG1", true }, {"INFRASYS_MCUSYS_REG2", true }, {"INFRASYS_MCUSYS_REG3", true }, {"INFRASYS_SYS_CIRQ", true }, {"INFRASYS_MM_IOMMU", true }, {"INFRASYS_RESERVE", true }, {"INFRASYS_DEVICE_APC", true }, /* 40 */ {"INFRASYS_DBG_TRACKER", true }, {"INFRASYS_CCIF0_AP", true }, {"INFRASYS_CCIF0_MD", true }, {"INFRASYS_CCIF1_MD", true }, {"INFRASYS_CLDMA_MD", true }, {"INFRASYS_MBIST", true }, {"INFRASYS_INFRA_PDN_REGISTER", true }, {"INFRASYS_TRNG", true }, {"INFRASYS_DX_CC", true }, {"INFRASYS_MCUPM_SRAM2", true }, /* 50 */ {"INFRASYS_CQ_DMA", true }, {"INFRASYS_MCUPM_SRAM3", true }, {"INFRASYS_SRAMROM", true }, {"INFRASYS_RESERVE", true }, {"INFRASYS_MCUPM_REG", true }, {"INFRASYS_MCUPM_SRAM0", true }, {"INFRASYS_MCUPM_SRAM1", true }, {"INFRASYS_EMI", true }, {"INFRASYS_CHN_EMI", true }, {"INFRASYS_CLDMA_PDN_AP", true }, /* 60 */ {"INFRASYS_CLDMA_PDN_MD", true }, {"INFRASYS_DRAMC_NAO", true }, {"INFRASYS_RESERVE", true }, {"INFRASYS_RESERVE", true }, {"INFRASYS_RESERVE", true }, {"INFRASYS_EMI_MPU", true }, {"INFRASYS_DVFS_PROC", true }, {"INFRASYS_DRAMC_CH0_TOP0", true }, {"INFRASYS_DRAMC_CH0_TOP1", true }, {"INFRASYS_DRAMC_CH0_TOP2", true }, /* 70 */ {"INFRASYS_DRAMC_CH0_TOP3", true }, {"INFRASYS_DRAMC_CH0_TOP4", true }, {"INFRASYS_DRAMC_CH1_TOP0", true }, {"INFRASYS_DRAMC_CH1_TOP1", true }, {"INFRASYS_DRAMC_CH1_TOP2", true }, {"INFRASYS_DRAMC_CH1_TOP3", true }, {"INFRASYS_DRAMC_CH1_TOP4", true }, {"INFRASYS_GCE", true }, {"INFRASYS_CCIF2_AP", true }, {"INFRASYS_CCIF2_MD", true }, /* 80 */ {"INFRASYS_CCIF3_AP", true }, {"INFRASYS_CCIF3_MD", true }, {"INFRA_AO_SSPM_1_1", true }, {"INFRA_AO_SSPM_1_2", true }, {"INFRA_AO_SSPM_1_3", true }, {"INFRA_AO_SSPM_2", true }, {"INFRA_AO_SSPM_3", true }, {"INFRA_AO_SSPM_4", true }, {"INFRA_AO_SSPM_5", true }, {"INFRA_AO_SSPM_6", true }, /* 90 */ {"INFRA_AO_SSPM_7", true }, {"INFRA_AO_SSPM_8", true }, {"INFRA_AO_SCP", true }, {"INFRA_AO_MCUCFG", true }, {"INFRASYS_DBUGSYS", true }, {"PERISYS_APDMA", true }, {"PERISYS_AUXADC", true }, {"PERISYS_UART0", true }, {"PERISYS_UART1", true }, {"PERISYS_UART2", true }, /* 100 */ {"PERISYS_UART3", true }, {"PERISYS_PWM", true }, {"PERISYS_I2C0", true }, {"PERISYS_I2C1", true }, {"PERISYS_I2C2", true }, {"PERISYS_SPI0", true }, {"PERISYS_PTP", true }, {"PERISYS_BTIF", true }, {"PERISYS_I2C6", true }, {"PERISYS_DISP_PWM", true }, /* 110 */ {"PERISYS_I2C3", true }, {"PERISYS_SPI1", true }, {"PERISYS_I2C4", true }, {"PERISYS_SPI2", true }, {"PERISYS_SPI3", true }, {"PERISYS_SPI4", true }, {"PERISYS_SPI5", true }, {"PERISYS_I2C5", true }, {"PERISYS_IMP_IIC_WRAP", true }, {"PERISYS_NFI", true }, /* 120 */ {"PERISYS_NFIECC", true }, {"PERISYS_USB", true }, {"PERISYS_USB_2.0_SUB", true }, {"PERISYS_MSDC0", true }, {"PERISYS_MSDC1", true }, {"PERISYS_MSDC2", true }, {"PERISYS_MSDC3", true }, {"PERISYS_UFS", true }, {"PERISUS_USB3.0_SIF", true }, {"PERISUS_USB3.0_SIF2", true }, /* 130 */ {"PERISYS_USB_2.0_SIF", true }, {"PERISYS_AUDIO", true }, {"EAST_RESERVE", true }, {"EAST_ CSI_TOP_AO", true }, {"EAST_ RESERVE", true }, {"EAST_ RESERVE", true }, {"SOUTH_MSDC1", true }, {"SOUTH_EFUSE", true }, {"SOUTH_RESERVE_1", true }, {"SOUTH_RESERVE_2", true }, /* 140 */ {"WEST_MIPI_TX_CONFIG", true }, {"WEST_RESERVE_0", true }, {"WEST_RESERVE_1", true }, {"WEST_RESERVE_2", true }, {"NORTH_USBSIF_TOP", true }, {"NORTH_MSDC0", true }, {"NORTH_RESERVE_0", true }, {"NORTH_RESERVE_1", true }, {"PERISYS_CONN", true }, {"PERISYS_RESERVE", true }, /* 150 */ {"PERISYS_RESERVE", true }, {"GPU", true }, {"GPU DVFS MON", true }, {"GPU CONFIG", true }, {"MFG_OTHERS", true }, {"MMSYS_CONFIG", true }, {"DISP_MUTEX", true }, {"SMI_COMMON", true }, {"SMI_LARB0", true }, {"MDP_RDMA0", true }, /* 160 */ {"MDP_CCORR0", true }, {"MDP_RSZ0", true }, {"MDP_RSZ1", true }, {"MDP_WDMA0", true }, {"MDP_WROT0", true }, {"MDP_TDSHP0", true }, {"DISP_OVL0", true }, {"DISP_OVL0_2L", true }, {"DISP_RDMA0", true }, {"DISP_WDMA0", true }, /* 170 */ {"DISP_COLOR0", true }, {"DISP_CCORR0", true }, {"DISP_AAL0", true }, {"DISP_GAMMA0", true }, {"DISP_DITHER0", true }, {"DSI0", true }, {"DISP_RSZ0", true }, {"MM_MUTEX", true }, {"SMI_LARB0", true }, {"SMI_LARB1", true }, /* 180 */ {"SMI_COMMON", true }, {"CAMSYS_CAMSYS_TOP", true }, {"CAMSYS_LARB2", true }, {"CAMSYS_RESERVED", true }, {"CAMSYS_RESERVED", true }, {"CAMSYS_MAIN", true }, {"CAMSYS_LUT", true }, {"CAMSYS_D", true }, {"CAMSYS_DMA", true }, {"CAMSYS_RESERVED", true }, /* 190 */ {"CAMSYS_RESERVED", true }, {"CAMSYS_RESERVED", true }, {"CAMSYS_FDVT", true }, {"CAMSYS_RESERVED", true }, {"CAMSYS_MAIN_INNER", true }, {"CAMSYS_D_INNER", true }, {"CAMSYS_DMA_INNER", true }, {"CAMSYS_SENINF_A", true }, {"CAMSYS_SENINF_B", true }, {"CAMSYS_SENINF_C", true }, /* 200 */ {"CAMSYS_SENINF_D", true }, {"CAMSYS_SENINF_E", true }, {"CAMSYS_SENINF_F", true }, {"CAMSYS_SENINF_G", true }, {"CAMSYS_SENINF_H", true }, {"CAMSYS_CAMSV_TOP_0", true }, {"CAMSYS_CAMSV_TOP_1", true }, {"CAMSYS_RESERVED", true }, {"CAMSYS_RESERVED", true }, {"VCODESYS_VENC_GLOBAL_CON", true }, /* 210 */ {"VCODESYS_SMI_LARB1", true }, {"VCODESYS_VENC", true }, {"VCODESYS_JPGENC", true }, {"VCODESYS_VDEC_FULL_TOP", true }, {"VCODESYS_MBIST_CTRL", true }, }; #endif /* * The extern functions for EMI MPU are removed because EMI MPU and Device APC * do not share the same IRQ now. */ /************************************************************************** *STATIC FUNCTION **************************************************************************/ #ifdef CONFIG_MTK_HIBERNATION static int devapc_pm_restore_noirq(struct device *device) { if (devapc_infra_irq != 0) { mt_irq_set_sens(devapc_infra_irq, MT_LEVEL_SENSITIVE); mt_irq_set_polarity(devapc_infra_irq, MT_POLARITY_LOW); } return 0; } #endif #if DEVAPC_TURN_ON static void unmask_infra_module_irq(unsigned int module) { unsigned int apc_index = 0; unsigned int apc_bit_index = 0; if (module > PD_INFRA_VIO_MASK_MAX_INDEX) { pr_info("[DEVAPC] %s: module overflow!\n", __func__); return; } apc_index = module / (MOD_NO_IN_1_DEVAPC * 2); apc_bit_index = module % (MOD_NO_IN_1_DEVAPC * 2); *DEVAPC_PD_INFRA_VIO_MASK(apc_index) &= (0xFFFFFFFF ^ (1 << apc_bit_index)); } #if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE) #ifdef DBG_ENABLE static void mask_infra_module_irq(unsigned int module) { unsigned int apc_index = 0; unsigned int apc_bit_index = 0; if (module > PD_INFRA_VIO_MASK_MAX_INDEX) { pr_info("[DEVAPC] %s: module overflow!\n", __func__); return; } apc_index = module / (MOD_NO_IN_1_DEVAPC * 2); apc_bit_index = module % (MOD_NO_IN_1_DEVAPC * 2); *DEVAPC_PD_INFRA_VIO_MASK(apc_index) |= (1 << apc_bit_index); } #endif #endif static int clear_infra_vio_status(unsigned int module) { unsigned int apc_index = 0; unsigned int apc_bit_index = 0; if (module > PD_INFRA_VIO_STA_MAX_INDEX) { pr_info("[DEVAPC] %s: module overflow!\n", __func__); return -1; } apc_index = module / (MOD_NO_IN_1_DEVAPC * 2); apc_bit_index = module % (MOD_NO_IN_1_DEVAPC * 2); *DEVAPC_PD_INFRA_VIO_STA(apc_index) = (0x1 << apc_bit_index); return 0; } static int check_infra_vio_status(unsigned int module) { unsigned int apc_index = 0; unsigned int apc_bit_index = 0; if (module > PD_INFRA_VIO_STA_MAX_INDEX) { pr_info("[DEVAPC] %s: module overflow!\n", __func__); return -1; } apc_index = module / (MOD_NO_IN_1_DEVAPC * 2); apc_bit_index = module % (MOD_NO_IN_1_DEVAPC * 2); if (*DEVAPC_PD_INFRA_VIO_STA(apc_index) & (0x1 << apc_bit_index)) return 1; return 0; } static void start_devapc(void) { unsigned int i; writel(0x80000000, DEVAPC_PD_INFRA_APC_CON); writel(0x3FFFFF, DEVAPC_PD_INFRA_VIO_SHIFT_STA); /* SMC call is called to set Device APC in LK instead */ DEVAPC_MSG("[DEVAPC] INFRA VIO_MASK 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", readl(DEVAPC_PD_INFRA_VIO_MASK(0)), readl(DEVAPC_PD_INFRA_VIO_MASK(1)), readl(DEVAPC_PD_INFRA_VIO_MASK(2)), readl(DEVAPC_PD_INFRA_VIO_MASK(3))); DEVAPC_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_MASK(4)), readl(DEVAPC_PD_INFRA_VIO_MASK(5)), readl(DEVAPC_PD_INFRA_VIO_MASK(6)), readl(DEVAPC_PD_INFRA_VIO_MASK(7)), readl(DEVAPC_PD_INFRA_VIO_MASK(8))); DEVAPC_MSG("[DEVAPC] INFRA VIO_STA 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", readl(DEVAPC_PD_INFRA_VIO_STA(0)), readl(DEVAPC_PD_INFRA_VIO_STA(1)), readl(DEVAPC_PD_INFRA_VIO_STA(2)), readl(DEVAPC_PD_INFRA_VIO_STA(3))); DEVAPC_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_STA(4)), readl(DEVAPC_PD_INFRA_VIO_STA(5)), readl(DEVAPC_PD_INFRA_VIO_STA(6)), readl(DEVAPC_PD_INFRA_VIO_STA(7)), readl(DEVAPC_PD_INFRA_VIO_STA(8))); DEVAPC_MSG("[DEVAPC] %s\n", "Clear INFRA VIO_STA and unmask INFRA VIO_MASK..."); for (i = 0; i < ARRAY_SIZE(devapc_infra_devices); i++) if (true == devapc_infra_devices[i].enable_vio_irq) { clear_infra_vio_status(i); unmask_infra_module_irq(i); } DEVAPC_MSG("[DEVAPC] INFRA VIO_MASK 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", readl(DEVAPC_PD_INFRA_VIO_MASK(0)), readl(DEVAPC_PD_INFRA_VIO_MASK(1)), readl(DEVAPC_PD_INFRA_VIO_MASK(2)), readl(DEVAPC_PD_INFRA_VIO_MASK(3))); DEVAPC_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_MASK(4)), readl(DEVAPC_PD_INFRA_VIO_MASK(5)), readl(DEVAPC_PD_INFRA_VIO_MASK(6)), readl(DEVAPC_PD_INFRA_VIO_MASK(7)), readl(DEVAPC_PD_INFRA_VIO_MASK(8))); DEVAPC_MSG("[DEVAPC] INFRA VIO_STA 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", readl(DEVAPC_PD_INFRA_VIO_STA(0)), readl(DEVAPC_PD_INFRA_VIO_STA(1)), readl(DEVAPC_PD_INFRA_VIO_STA(2)), readl(DEVAPC_PD_INFRA_VIO_STA(3))); DEVAPC_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_STA(4)), readl(DEVAPC_PD_INFRA_VIO_STA(5)), readl(DEVAPC_PD_INFRA_VIO_STA(6)), readl(DEVAPC_PD_INFRA_VIO_STA(7)), readl(DEVAPC_PD_INFRA_VIO_STA(8))); } #if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE) #ifdef DBG_ENABLE /* violation index corresponds to subsys */ static const char *index_to_subsys(unsigned int index) { if (index >= 151 && index <= 154) return "MFGSYS"; else if (index == 155) return "MMSYS"; else if (index == 156 || (index >= 166 && index <= 176)) return "MMSYS_DISP"; else if (index == 157 || index == 158 || index == 178 || index == 179 || index == 180 || index == 210) return "SMI"; else if (index >= 159 && index <= 165) return "MMSYS_MDP"; else if (index >= 209 && index <= 214) return "VCODECSYS"; else if (index >= 181 && index <= 208) return "CAMSYS"; else if (index < ARRAY_SIZE(devapc_infra_devices)) return devapc_infra_devices[index].device; else return "OUT_OF_BOUND"; } static void execute_aee(unsigned int i, unsigned int dbg0, unsigned int dbg1) { char subsys_str[32] = {0}; unsigned int domain_id; DEVAPC_VIO_MSG("[DEVAPC] Executing AEE Exception...\n"); /* mask irq for module "i" */ mask_infra_module_irq(i); domain_id = (dbg0 & INFRA_VIO_DBG_DMNID) >> INFRA_VIO_DBG_DMNID_START_BIT; if (domain_id == 1) { strncpy(subsys_str, "MD_SI", sizeof(subsys_str)); } else { strncpy(subsys_str, index_to_subsys(i), sizeof(subsys_str)); } subsys_str[sizeof(subsys_str)-1] = '\0'; aee_kernel_exception("DEVAPC", "%s %s, Vio Addr: 0x%x\n%s%s\n", "[DEVAPC] Violation Slave:", devapc_infra_devices[i].device, dbg1, "CRDISPATCH_KEY:Device APC Violation Issue/", subsys_str ); /* unmask irq for module "i" */ unmask_infra_module_irq(i); } #endif #endif // AEE_FEATURE static irqreturn_t devapc_violation_irq(int irq_number, void *dev_id) { unsigned int dbg0 = 0, dbg1 = 0; unsigned int master_id; unsigned int domain_id; unsigned int vio_addr_high; unsigned int read_violation; unsigned int write_violation; unsigned int device_count; unsigned int shift_done; unsigned int i; struct pt_regs *regs; if (irq_number == devapc_infra_irq) { DEVAPC_MSG("[DEVAPC] %s 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", "INFRA VIO_MASK", readl(DEVAPC_PD_INFRA_VIO_MASK(0)), readl(DEVAPC_PD_INFRA_VIO_MASK(1)), readl(DEVAPC_PD_INFRA_VIO_MASK(2)), readl(DEVAPC_PD_INFRA_VIO_MASK(3))); DEVAPC_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_MASK(4)), readl(DEVAPC_PD_INFRA_VIO_MASK(5)), readl(DEVAPC_PD_INFRA_VIO_MASK(6)), readl(DEVAPC_PD_INFRA_VIO_MASK(7)), readl(DEVAPC_PD_INFRA_VIO_MASK(8))); DEVAPC_VIO_MSG("[DEVAPC] %s 0:0x%x 1:0x%x 2:0x%x 3:0x%x ", "INFRA VIO_STA", readl(DEVAPC_PD_INFRA_VIO_STA(0)), readl(DEVAPC_PD_INFRA_VIO_STA(1)), readl(DEVAPC_PD_INFRA_VIO_STA(2)), readl(DEVAPC_PD_INFRA_VIO_STA(3))); DEVAPC_VIO_MSG("4:0x%x 5:0x%x 6:0x%x 7:0x%x 8:0x%x\n", readl(DEVAPC_PD_INFRA_VIO_STA(4)), readl(DEVAPC_PD_INFRA_VIO_STA(5)), readl(DEVAPC_PD_INFRA_VIO_STA(6)), readl(DEVAPC_PD_INFRA_VIO_STA(7)), readl(DEVAPC_PD_INFRA_VIO_STA(8))); DEVAPC_MSG("[DEVAPC] VIO_SHIFT_STA: 0x%x\n", readl(DEVAPC_PD_INFRA_VIO_SHIFT_STA)); for (i = 0; i <= PD_INFRA_VIO_SHIFT_MAX_BIT; ++i) if (readl(DEVAPC_PD_INFRA_VIO_SHIFT_STA) & (0x1 << i)) { writel(0x1 << i, DEVAPC_PD_INFRA_VIO_SHIFT_SEL); writel(0x1, DEVAPC_PD_INFRA_VIO_SHIFT_CON); for (shift_done = 0; (shift_done < 100) && ((readl(DEVAPC_PD_INFRA_VIO_SHIFT_CON) & 0x3) != 0x3); ++shift_done) DEVAPC_MSG("%s %s (%d, %d)\n", "[DEVAPC]", "Syncing INFRA DBG0 & DBG1", i, shift_done); DEVAPC_MSG("%s %s%X, %s%X\n", "[DEVAPC]", "VIO_SHIFT_SEL=0x", readl(DEVAPC_PD_INFRA_VIO_SHIFT_SEL), "VIO_SHIFT_CON=0x", readl(DEVAPC_PD_INFRA_VIO_SHIFT_CON)); if ((readl(DEVAPC_PD_INFRA_VIO_SHIFT_CON) & 0x3) == 0x3) shift_done = 1; else { shift_done = 0; DEVAPC_VIO_MSG("[DEVAPC] index:%d %s\n", i, "sync failed!"); } writel(0x0, DEVAPC_PD_INFRA_VIO_SHIFT_CON); writel(0x0, DEVAPC_PD_INFRA_VIO_SHIFT_SEL); writel(0x1 << i, DEVAPC_PD_INFRA_VIO_SHIFT_STA); if (shift_done == 0) continue; DEVAPC_MSG("%s %s%X, %s%X, %s%X\n", "[DEVAPC]", "VIO_SHIFT_STA=0x", readl(DEVAPC_PD_INFRA_VIO_SHIFT_STA), "VIO_SHIFT_SEL=0x", readl(DEVAPC_PD_INFRA_VIO_SHIFT_SEL), "VIO_SHIFT_CON=0x", readl(DEVAPC_PD_INFRA_VIO_SHIFT_CON)); dbg0 = readl(DEVAPC_PD_INFRA_VIO_DBG0); dbg1 = readl(DEVAPC_PD_INFRA_VIO_DBG1); master_id = (dbg0 & INFRA_VIO_DBG_MSTID) >> INFRA_VIO_DBG_MSTID_START_BIT; domain_id = (dbg0 & INFRA_VIO_DBG_DMNID) >> INFRA_VIO_DBG_DMNID_START_BIT; write_violation = (dbg0 & INFRA_VIO_DBG_W_VIO) >> INFRA_VIO_DBG_W_VIO_START_BIT; read_violation = (dbg0 & INFRA_VIO_DBG_R_VIO) >> INFRA_VIO_DBG_R_VIO_START_BIT; vio_addr_high = (dbg0 & INFRA_VIO_ADDR_HIGH) >> INFRA_VIO_ADDR_HIGH_START_BIT; /* violation information improvement */ DEVAPC_VIO_MSG("%s%s%s%s%x %s%x, %s%x, %s%x\n", "[DEVAPC] Violation(Infra,", read_violation == 1?"R":" ", write_violation == 1?"W) - ":" ) - ", "Vio Addr:0x", dbg1, "High:0x", vio_addr_high, "Bus ID:0x", master_id, "Dom ID:0x", domain_id); DEVAPC_VIO_MSG("%s - %s%s, %s%i\n", "[DEVAPC] Violation", "Process:", current->comm, "PID:", current->pid); break; } device_count = ARRAY_SIZE(devapc_infra_devices); /* checking and showing violation normal slaves */ for (i = 0; i < device_count; i++) if (devapc_infra_devices[i].enable_vio_irq == true && check_infra_vio_status(i) == 1) { clear_infra_vio_status(i); DEVAPC_VIO_MSG("%s %s %s (%s=%d)\n", "[DEVAPC]", "Access Violation Slave:", devapc_infra_devices[i].device, "infra index", i); #if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE) #ifdef DBG_ENABLE execute_aee(i, dbg0, dbg1); #endif #endif } } else { DEVAPC_VIO_MSG("%s %s %d %s\n", "[DEVAPC]", "(ERROR) irq_number", irq_number, "is not registered!"); } if ((DEVAPC_ENABLE_ONE_CORE_VIOLATION_DEBUG) || (enable_dynamic_one_core_violation_debug)) { DEVAPC_VIO_MSG("%s ====== %s ======\n", "[DEVAPC]", "Start dumping Device APC violation tracing"); DEVAPC_VIO_MSG("%s ****** %s ******\n", "[DEVAPC]", "[All IRQ Registers]"); regs = get_irq_regs(); show_regs(regs); DEVAPC_VIO_MSG("%s ****** %s ******\n", "[DEVAPC]", "[All Current Task Stack]"); show_stack(current, NULL); DEVAPC_VIO_MSG("%s ====== %s ======\n", "[DEVAPC]", "End of dumping Device APC violation tracing"); } return IRQ_HANDLED; } #endif static int devapc_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; #if DEVAPC_TURN_ON int ret; #endif DEVAPC_MSG("[DEVAPC] module probe.\n"); if (devapc_pd_infra_base == NULL) { if (node) { devapc_pd_infra_base = of_iomap(node, DAPC_DEVICE_TREE_NODE_PD_INFRA_INDEX); devapc_infra_irq = irq_of_parse_and_map(node, DAPC_DEVICE_TREE_NODE_PD_INFRA_INDEX); DEVAPC_MSG("[DEVAPC] PD_INFRA_ADDRESS: %p, IRQ: %d\n", devapc_pd_infra_base, devapc_infra_irq); } else { pr_info("[DEVAPC] %s\n", "can't find DAPC_INFRA_PD compatible node"); return -1; } } #if DEVAPC_TURN_ON ret = request_irq(devapc_infra_irq, (irq_handler_t)devapc_violation_irq, IRQF_TRIGGER_LOW | IRQF_SHARED, "devapc", &g_devapc_ctrl); if (ret) { pr_info("[DEVAPC] Failed to request infra irq! (%d)\n", ret); return ret; } #endif /* CCF */ #if DEVAPC_USE_CCF dapc_infra_clk = devm_clk_get(&pdev->dev, "devapc-infra-clock"); if (IS_ERR(dapc_infra_clk)) { pr_info("[DEVAPC] (Infra) %s\n", "Cannot get devapc clock from common clock framework."); return PTR_ERR(dapc_infra_clk); } clk_prepare_enable(dapc_infra_clk); #endif #ifdef CONFIG_MTK_HIBERNATION register_swsusp_restore_noirq_func(ID_M_DEVAPC, devapc_pm_restore_noirq, NULL); #endif #if DEVAPC_TURN_ON start_devapc(); #endif return 0; } static int devapc_remove(struct platform_device *dev) { clk_disable_unprepare(dapc_infra_clk); return 0; } static int devapc_suspend(struct platform_device *dev, pm_message_t state) { return 0; } static int devapc_resume(struct platform_device *dev) { DEVAPC_MSG("[DEVAPC] module resume.\n"); return 0; } #ifdef DBG_ENABLE #ifndef mt_secure_call #define mt_secure_call(x1, x2, x3, x4, x5) ({\ struct arm_smccc_res res;\ arm_smccc_smc(x1, x2, x3, x4, x5, 0, 0, 0, &res);\ res.a0; }) #endif static ssize_t devapc_dbg_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { int ret; ssize_t retval = 0; char msg[256] = "DBG: dump devapc reg...\n"; if (*ppos >= strlen(msg)) return 0; pr_info("enter %s...\n", __func__); pr_info("call smc to ATF.\n"); retval = simple_read_from_buffer(buffer, count, ppos, msg, strlen(msg)); ret = mt_secure_call(MTK_SIP_LK_DAPC, 1, 0, 0, 0); if (ret == 0) pr_info("dump devapc reg success !\n"); else pr_info("dump devapc reg failed !\n"); return retval; } static ssize_t devapc_dbg_write(struct file *file, const char __user *buffer, size_t count, loff_t *data) { char input[32]; char *pinput = NULL; char *tmp = NULL; long i; int len = 0, ret = 0; long slave_type = 0, domain = 0, index = 0; pr_info("[DEVAPC] debugging...\n"); len = (count < (sizeof(input) - 1)) ? count : (sizeof(input) - 1); if (copy_from_user(input, buffer, len)) { pr_info("[DEVAPC] copy from user failed!\n"); return -EFAULT; } input[len] = '\0'; pinput = input; if (sysfs_streq(input, "0")) { enable_dynamic_one_core_violation_debug = 0; pr_info("[DEVAPC] One-Core Debugging: Disabled\n"); } else if (sysfs_streq(input, "1")) { enable_dynamic_one_core_violation_debug = 1; pr_info("[DEVAPC] One-Core Debugging: Enabled\n"); } else { tmp = strsep(&pinput, " "); if (tmp != NULL) i = kstrtol(tmp, 10, &slave_type); else slave_type = E_DAPC_OTHERS_SLAVE; if (slave_type >= E_DAPC_OTHERS_SLAVE) { pr_info("[DEVAPC] wrong input slave type\n"); return -EFAULT; } pr_info("[DEVAPC] slave_type = %lu\n", slave_type); tmp = strsep(&pinput, " "); if (tmp != NULL) i = kstrtol(tmp, 10, &domain); else domain = E_DOMAIN_OTHERS; if (domain >= E_DOMAIN_OTHERS) { pr_info("[DEVAPC] wrong input domain type\n"); return -EFAULT; } pr_info("[DEVAPC] domain id = %lu\n", domain); tmp = strsep(&pinput, " "); if (tmp != NULL) i = kstrtol(tmp, 10, &index); else index = 0xFFFFFFFF; if (index > DEVAPC_TOTAL_SLAVES) { pr_info("[DEVAPC] wrong input index type\n"); return -EFAULT; } pr_info("[DEVAPC] slave index = %lu\n", index); ret = mt_secure_call(MTK_SIP_LK_DAPC, slave_type, domain, index, 0); pr_info("dump devapc reg = 0x%x.\n", ret); } return count; } #endif static int devapc_dbg_open(struct inode *inode, struct file *file) { return 0; } static const struct file_operations devapc_dbg_fops = { .owner = THIS_MODULE, .open = devapc_dbg_open, #ifdef DBG_ENABLE .write = devapc_dbg_write, .read = devapc_dbg_read, #else .write = NULL, .read = NULL, #endif }; static const struct of_device_id plat_devapc_dt_match[] = { { .compatible = "mediatek,devapc" }, {}, }; static struct platform_driver devapc_driver = { .probe = devapc_probe, .remove = devapc_remove, .suspend = devapc_suspend, .resume = devapc_resume, .driver = { .name = "devapc", .owner = THIS_MODULE, .of_match_table = plat_devapc_dt_match, }, }; /* * devapc_init: module init function. */ static int __init devapc_init(void) { int ret; DEVAPC_MSG("[DEVAPC] kernel module init.\n"); ret = platform_driver_register(&devapc_driver); if (ret) { pr_info("[DEVAPC] Unable to register driver (%d)\n", ret); return ret; } g_devapc_ctrl = cdev_alloc(); if (!g_devapc_ctrl) { pr_info("[DEVAPC] Failed to add devapc device! (%d)\n", ret); platform_driver_unregister(&devapc_driver); return ret; } g_devapc_ctrl->owner = THIS_MODULE; proc_create("devapc_dbg", 0664, NULL, &devapc_dbg_fops); /* 0664: (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) */ return 0; } /* * devapc_exit: module exit function. */ static void __exit devapc_exit(void) { DEVAPC_MSG("[DEVAPC] DEVAPC module exit\n"); #ifdef CONFIG_MTK_HIBERNATION unregister_swsusp_restore_noirq_func(ID_M_DEVAPC); #endif } /* Device APC no longer shares IRQ with EMI and * can be changed to use the earlier "arch_initcall" */ arch_initcall(devapc_init); module_exit(devapc_exit); MODULE_LICENSE("GPL");