kernel_samsung_a34x-permissive/drivers/soc/mediatek/devapc/mt6765/devapc.c

1107 lines
34 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/types.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
#ifdef DBG_ENABLE
#include <linux/arm-smccc.h>
#endif
#ifdef CONFIG_MTK_HIBERNATION
#include <mtk_hibernate_dpm.h>
#endif
/* CCF */
#include <linux/clk.h>
#include "devapc.h"
#if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE)
#include <mt-plat/aee.h>
#endif
/* 0 for early porting */
#define DEVAPC_TURN_ON 1
#define DEVAPC_USE_CCF 1
#define DEVAPC_VIO_DEBUG 0
/* 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 void __iomem *devapc_ao_base;
static unsigned int enable_dynamic_one_core_violation_debug;
#if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE)
unsigned long long devapc_vio_first_trigger_time[DEVAPC_TOTAL_SLAVES];
/* violation times */
unsigned int devapc_vio_count[DEVAPC_TOTAL_SLAVES];
/* show the status of whether AEE warning has been populated */
unsigned int devapc_vio_aee_shown[DEVAPC_TOTAL_SLAVES];
unsigned int devapc_vio_current_aee_trigger_times;
#endif
#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_AP", true },
{"INFRASYS_CCIF1_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_I2C6", 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_ RESERVE", true },
{"SOUTH_EFUSE", true },
{"SOUTH_RESERVE_1", true },
{"SOUTH_RESERVE_2", true },
/* 140 */
{"WEST_MIPI_TX_CONFIG", true },
{"WEST_MSDC1", 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_MD1", true },
/* 150 */
{"PERISYS_RESERVE", true },
{"GPU", true },
{"GPU CONFIG", true },
{"GPU RESERVE", 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 },
{"IMGSYS_CONFIG", true },
{"IMGSYS_SMI_LARB2", true },
{"IMGSYS_DISP_A0", true },
{"IMGSYS_DISP_A1", true },
{"IMGSYS_DISP_A_NBC", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_DPE", true },
/* 190 */
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_FDVT", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"IMGSYS_RESERVE", true },
{"VCODESYS_VENC_GLOBAL_CON", true },
{"VCODESYS_SMI_LARB1", true },
{"VCODESYS_VENC", true },
/* 200 */
{"VCODESYS_JPGENC", true },
{"VCODESYS_VDEC_FULL_TOP", true },
{"VCODESYS_MBIST_CTRL", true },
{"CAMSYS_CAMSYS_TOP", true },
{"CAMSYS_LARB3", true },
{"CAMSYS_CAM_TOP", true },
{"CAMSYS_CAM_A", true },
{"CAMSYS_CAM_B", true },
{"CAMSYS_CAM_TOP_SET", true },
{"CAMSYS_CAM_A_SET", true },
/* 210 */
{"CAMSYS_CAM_B_SET", true },
{"CAMSYS_CAM_TOP_INNER", true },
{"CAMSYS_CAM_A_INNER", true },
{"CAMSYS_CAM_B_INNER", true },
{"CAMSYS_CAM_TOP_CLR", true },
{"CAMSYS_CAM_A_CLR", true },
{"CAMSYS_CAM_B_CLR", true },
{"CAMSYS_CAM_A_EXT", true },
{"CAMSYS_CAM_B_EXT", true },
{"CAMSYS_SENINF_A", true },
/* 220 */
{"CAMSYS_SENINF_B", true },
{"CAMSYS_SENINF_C", true },
{"CAMSYS_SENINF_D", true },
{"CAMSYS_SENINF_E", true },
{"CAMSYS_SENINF_F", true },
{"CAMSYS_SENINF_G", true },
{"CAMSYS_SENINF_H", true },
{"CAMSYS_CAMSV_A", true },
{"CAMSYS_CAMSV_B", true },
{"CAMSYS_CAMSV_C", true },
/* 230 */
{"CAMSYS_CAMSV_D", true },
{"CAMSYS_OTHERS_0", true },
{"CAMSYS_MD32 DMEM", true },
{"CAMSYS_RESERVED", true },
{"CAMSYS_MD32 PMEM", true },
{"CAMSYS_MD32 IP", true },
{"CAMSYS_CCU_CTL", 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)
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
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;
uint32_t vio_shift_sta;
writel(0x80000000, DEVAPC_PD_INFRA_APC_CON);
vio_shift_sta = readl(DEVAPC_PD_INFRA_VIO_SHIFT_STA);
if (vio_shift_sta) {
DEVAPC_MSG("(Pre) clear VIO_SHIFT_STA = 0x%x\n", vio_shift_sta);
writel(vio_shift_sta, DEVAPC_PD_INFRA_VIO_SHIFT_STA);
DEVAPC_MSG("(Post) clear VIO_SHIFT_STA = 0x%x\n",
readl(DEVAPC_PD_INFRA_VIO_SHIFT_STA));
} else
DEVAPC_MSG("No violation happened before booting kernel\n");
/* 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)
devapc_vio_current_aee_trigger_times = 0;
#endif
}
#if defined(CONFIG_MTK_AEE_FEATURE) && defined(DEVAPC_ENABLE_AEE)
/* 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 || index == 156 || (index >= 166 && index <= 176))
return "MMSYS_DISP";
else if (index == 157 || index == 158 || index == 182 || index == 198
|| index == 204)
return "SMI";
else if (index >= 159 && index <= 165)
return "MMSYS_MDP";
else if (index == 181 || (index >= 183 && index <= 196))
return "IMGSYS";
else if (index >= 197 && index <= 202)
return "VCODECSYS";
else if (index == 203 || (index >= 205 && index <= 236))
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);
/* Mark the flag for showing AEE (AEE should be shown only once) */
devapc_vio_aee_shown[i] = 1;
if (devapc_vio_current_aee_trigger_times <
DEVAPC_VIO_MAX_TOTAL_AEE_TRIGGER_TIMES) {
devapc_vio_current_aee_trigger_times++;
domain_id = (dbg0 & INFRA_VIO_DBG_DMNID)
>> INFRA_VIO_DBG_DMNID_START_BIT;
if (domain_id == 1) {
strncpy(subsys_str, "MD_SS", 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);
}
#ifndef DBG_ENABLE
static void evaluate_aee_exception(unsigned int i, unsigned int dbg0,
unsigned int dbg1)
{
unsigned long long current_time;
if (devapc_vio_aee_shown[i] == 0) {
if (devapc_vio_count[i] < DEVAPC_VIO_AEE_TRIGGER_TIMES) {
devapc_vio_count[i]++;
if (devapc_vio_count[i] == 1) {
/* this slave violation is triggered for the first time */
/* get current time from start-up in ns */
devapc_vio_first_trigger_time[i] =
sched_clock();
DEVAPC_VIO_MSG("%s %s %u\n",
"[DEVAPC]",
"devapc_vio_first_trigger_time:",
do_div(devapc_vio_first_trigger_time[i],
1000000)); /* ms */
}
}
if (devapc_vio_count[i] >= DEVAPC_VIO_AEE_TRIGGER_TIMES) {
/* get current time from start-up in ns */
current_time = sched_clock();
DEVAPC_VIO_MSG("[DEVAPC] current_time: %u\n",
do_div(current_time, 1000000)); /* ms */
DEVAPC_VIO_MSG("[DEVAPC] devapc_vio_count[%d]: %d\n",
i, devapc_vio_count[i]);
if (((current_time - devapc_vio_first_trigger_time[i])
/ 1000000) <= (unsigned long long)
DEVAPC_VIO_AEE_TRIGGER_FREQUENCY) {
/* diff time by ms */
execute_aee(i, dbg0, dbg1);
}
}
}
}
#endif // DBG_ENABLE
#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);
#else
/* Frequency-based Violation AEE Exception */
/* it will trigger AEE if violations occur
* "10" times in "1000" ms
*/
evaluate_aee_exception(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_ao_base = of_iomap(node,
DAPC_DEVICE_TREE_NODE_AO_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;
}
static int check_debug_input_type(const char *str)
{
if (sysfs_streq(str, "1"))
return DAPC_INPUT_TYPE_DEBUG_ON;
else if (sysfs_streq(str, "0"))
return DAPC_INPUT_TYPE_DEBUG_OFF;
else if (sysfs_streq(str, "2"))
return DAPC_INPUT_TYPE_VIO_UT;
else
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_KERNEL_DAPC_DUMP, 0, 0, 0, 0);
if (ret == 0)
pr_info("dump devapc reg success !\n");
else
pr_info("dump devapc reg failed !\n");
return retval;
}
#endif
static ssize_t devapc_dbg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *data)
{
char desc[32];
int len = 0;
int input_type;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return -EFAULT;
desc[len] = '\0';
input_type = check_debug_input_type(desc);
if (!input_type)
return -EFAULT;
if (input_type == DAPC_INPUT_TYPE_DEBUG_ON) {
enable_dynamic_one_core_violation_debug = 1;
DEVAPC_MSG("[DEVAPC] One-Core Debugging: Enabled\n");
} else if (input_type == DAPC_INPUT_TYPE_DEBUG_OFF) {
enable_dynamic_one_core_violation_debug = 0;
DEVAPC_MSG("[DEVAPC] One-Core Debugging: Disabled\n");
} else if (input_type == DAPC_INPUT_TYPE_VIO_UT) {
DEVAPC_MSG("%s, test violation...\n", __func__);
if (unlikely(devapc_ao_base == NULL)) {
DEVAPC_MSG("%s:%d NULL pointer\n", __func__, __LINE__);
return -EINVAL;
}
DEVAPC_MSG("%s, devapc_ao_infra_base = 0x%x\n",
__func__,
readl(devapc_ao_base));
DEVAPC_MSG("%s, test done, it should generate violation!\n",
__func__);
}
return count;
}
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,
.write = devapc_dbg_write,
#ifdef DBG_ENABLE
.read = devapc_dbg_read,
#else
.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");