/* * Copyright (c) 2015 Samsung Electronics Co., Ltd. * http://www.samsung.com * * BUS Monitor Debugging Driver for Samsung EXYNOS8890 SoC * By Hosung Kim (hosung0.kim@samsung.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) "[detect] abnormal access: " fmt #include #include #include #include #include #include /* S-NODE, M-NODE Common */ #define OFFSET_TIMEOUT_REG (0x2000) #define OFFSET_REQ_R (0x0) #define OFFSET_REQ_W (0x20) #define OFFSET_RESP_R (0x40) #define OFFSET_RESP_W (0x60) #define OFFSET_ERR_REPT (0x20) #define REG_INT_MASK (0x0) #define REG_INT_CLR (0x4) #define REG_INT_INFO (0x8) #define REG_EXT_INFO_0 (0x10) #define REG_EXT_INFO_1 (0x14) #define REG_EXT_INFO_2 (0x18) #define REG_DBG_CTL (0x10) #define REG_TIMEOUT_INIT_VAL (0x14) #define REG_R_TIMEOUT_MO (0x18) #define REG_W_TIMEOUT_MO (0x1C) #define BIT_ERR_CODE(x) (((x) & (0xF << 28)) >> 28) #define BIT_ERR_OCCURRED(x) (((x) & (0x1 << 27)) >> 27) #define BIT_ERR_VALID(x) (((x) & (0x1 << 26)) >> 26) #define BIT_ID_VAL(x) (((x) & (0xFFFF))) #define BIT_AXUSER(x) (((x) & (0xFFFF << 16)) >> 16) #define S_NODE (0) #define M_NODE (1) #define T_S_NODE (2) #define T_M_NODE (3) #define PATH_DATA_CCORE (0) #define PATH_DATA_BUS0 (1) #define PATH_DATA_BUS1 (2) #define PATH_SFR_CCORE (3) #define PATH_SFR_BUS0 (4) #define PATH_SFR_BUS1 (5) #define PATH_NUM (6) #define ERRCODE_SLVERR (0) #define ERRCODE_DECERR (1) #define ERRCODE_UNSUPORTED (2) #define ERRCODE_POWER_DOWN (3) #define ERRCODE_UNKNOWN_4 (4) #define ERRCODE_UNKNOWN_5 (5) #define ERRCODE_TIMEOUT (6) #define TIMEOUT (0xFFFFF) #define TIMEOUT_TEST (0x1) #define NEED_TO_CHECK (0xCAFE) struct busmon_rpathinfo { unsigned int id; char *port_name; char *dest_name; }; struct busmon_masterinfo { char *port_name; unsigned int user; char *master_name; unsigned int bits; }; struct busmon_nodeinfo { unsigned int type; char *name; unsigned int phy_regs; void __iomem *regs; unsigned int irq; unsigned int time_val; bool timeout_enabled; bool err_rpt_enabled; char *need_rpath; char *comment; }; /* Error Code Description */ static char *busmon_errcode[] = { "Error Detect by the Slave(SLVERR)", "Decode error(DECERR)", "Unsupported transaction error", "Power Down access error", "Unsupported transaction", "Unsupported transaction", "Timeout error - response timeout", "Invalid errorcode", }; /* Error Code Description */ static char *busmon_sfr_errmaster[] = { "CPU(Access from either CPU clusters)", "TREX_CCORE(The PERI S-node from the data backbone)", "G3D(SFR accesses from G3D)", "CP(SFR accesses from CP)", "CPU(IMEM access only)", "CPU(IMEM access only)", }; struct busmon_nodegroup { int irq; char *name; struct busmon_nodeinfo *nodeinfo; unsigned int nodesize; }; struct busmon_platdata { struct busmon_rpathinfo *rpathinfo; struct busmon_masterinfo *masterinfo; struct busmon_nodegroup nodegroup[PATH_NUM]; }; static struct busmon_rpathinfo rpathinfo[] = { {0, "G3D1", "MEMS_0"}, {65, "CAM0", "MEMS_0"}, {81, "CAM1", "MEMS_0"}, {1, "DISP0_0", "MEMS_0"}, {17, "DISP0_1", "MEMS_0"}, {33, "DISP1_0", "MEMS_0"}, {49, "DISP1_1", "MEMS_0"}, {97, "ISP0", "MEMS_0"}, {66, "CAM0", "MEMS_0"}, {82, "CAM1", "MEMS_0"}, {2, "DISP0_0", "MEMS_0"}, {18, "DISP0_1", "MEMS_0"}, {34, "DISP1_0", "MEMS_0"}, {50, "DISP1_1", "MEMS_0"}, {98, "ISP0", "MEMS_0"}, {3, "IMEM", "MEMS_0"}, {35, "AUD", "MEMS_0"}, {67, "CORESIGHT", "MEMS_0"}, {11, "CAM1", "MEMS_0"}, {75, "FSYS1", "MEMS_0"}, {43, "ISP0", "MEMS_0"}, {19, "CP", "MEMS_0"}, {4, "FSYS0", "MEMS_0"}, {52, "MFC0", "MEMS_0"}, {68, "MFC1", "MEMS_0"}, {20, "MSCL0", "MEMS_0"}, {36, "MSCL1", "MEMS_0"}, {0, "G3D1", "MEMS_1"}, {65, "CAM0", "MEMS_1"}, {81, "CAM1", "MEMS_1"}, {1, "DISP0_0", "MEMS_1"}, {17, "DISP0_1", "MEMS_1"}, {33, "DISP1_0", "MEMS_1"}, {49, "DISP1_1", "MEMS_1"}, {97, "ISP0", "MEMS_1"}, {66, "CAM0", "MEMS_1"}, {82, "CAM1", "MEMS_1"}, {2, "DISP0_0", "MEMS_1"}, {18, "DISP0_1", "MEMS_1"}, {34, "DISP1_0", "MEMS_1"}, {50, "DISP1_1", "MEMS_1"}, {98, "ISP0", "MEMS_1"}, {3, "IMEM", "MEMS_1"}, {35, "AUD", "MEMS_1"}, {67, "CORESIGHT", "MEMS_1"}, {11, "CAM1", "MEMS_1"}, {75, "FSYS1", "MEMS_1"}, {43, "ISP0", "MEMS_1"}, {19, "CP", "MEMS_1"}, {4, "FSYS0", "MEMS_1"}, {52, "MFC0", "MEMS_1"}, {68, "MFC1", "MEMS_1"}, {20, "MSCL0", "MEMS_1"}, {36, "MSCL1", "MEMS_1"}, {0, "IMEM", "PERI"}, {8, "AUD", "PERI"}, {16, "CORESIGHT", "PERI"}, {1, "FSYS0", "PERI"}, {13, "MFC0", "PERI"}, {17, "MFC1", "PERI"}, {5, "MSCL0", "PERI"}, {9, "MSCL1", "PERI"}, {2, "CAM1", "PERI"}, {18, "FSYS1", "PERI"}, {10, "ISP0", "PERI"}, }; static struct busmon_masterinfo masterinfo[] = { /* DISP0_0 */ {"DISP0_0", 1 << 0, "sysmmu", 0x1}, {"DISP0_0", 1 << 0, "S-IDMA0", 0x3}, {"DISP0_0", 1 << 1, "IDMA3", 0x3}, /* DISP0_1 */ {"DISP0_1", 1 << 0, "sysmmu", 0x1}, {"DISP0_1", 0 << 0, "IDMA0", 0x3}, {"DISP0_1", 1 << 1, "IDMA4", 0x3}, /* DISP1_0 */ {"DISP1_0", 1 << 0, "sysmmu", 0x1}, {"DISP1_0", 0 << 0, "IDMA1", 0x3}, {"DISP1_0", 1 << 1, "VGR0", 0x3}, /* DISP1_1 */ {"DISP1_1", 1 << 0, "sysmmu", 0x1}, {"DISP1_1", 0 << 0, "IDMA2", 0x7}, {"DISP1_1", 1 << 1, "VGR1", 0x7}, {"DISP1_1", 1 << 2, "WDMA", 0x7}, /* MFC0 */ {"MFC0", 1 << 0, "sysmmu", 0x1}, {"MFC0", 0 << 0, "MFC M0", 0x1}, /* MFC1 */ {"MFC1", 1 << 0, "sysmmu", 0x1}, {"MFC1", 0 << 0, "MFC M1", 0x1}, /* IMEM */ {"IMEM", 0 << 0, "SSS M0", 0xF}, {"IMEM", 1 << 2, "RTIC", 0xF}, {"IMEM", 1 << 3, "SSS M1", 0xF}, {"IMEM", 1 << 0, "MCOMP", 0x3}, {"IMEM", 1 << 1, "APM", 0x3}, /* G3D */ {"G3D0", 0 << 0, "G3D0", 0x1}, {"G3D1", 0 << 1, "G3D1", 0x1}, /* AUD */ {"AUD", 1 << 0, "sysmmu", 0x1}, {"AUD", 1 << 1, "DMAC", 0x7}, {"AUD", 1 << 2, "AUD CA5", 0x7}, /* MSCL0 */ {"MSCL0", 1 << 0, "sysmmu", 0x1}, {"MSCL0", 0 << 0, "JPEG", 0x3}, {"MSCL0", 1 << 1, "MSCL0", 0x3}, /* MSCL1 */ {"MSCL1", 1 << 0, "sysmmu", 0x1}, {"MSCL1", 0 << 0, "G2D", 0x3}, {"MSCL1", 1 << 1, "MSCL1", 0x3}, /* FSYS1 */ {"FSYS1", 0 << 0, "MMC51", 0x7}, {"FSYS1", 1 << 2, "UFS", 0x7}, {"FSYS1", 1 << 0, "PCIE_WIFI0", 0x3}, {"FSYS1", 1 << 1, "PCIE_WIFI1", 0x3}, /* FSYS0 */ {"FSYS0", 0 << 0, "ETR USB", 0x7}, {"FSYS0", 1 << 2, "USB30", 0x7}, {"FSYS0", 1 << 0, "UFS", 0x7}, {"FSYS0", 1 << 0 | 1 << 2, "MMC51", 0x7}, {"FSYS0", 1 << 1, "PDMA0", 0x7}, {"FSYS0", 1 << 1 | 1 << 2, "PDMA(secure)", 0x7}, {"FSYS0", 1 << 0 | 1 << 1, "USB20", 0x3}, /* CAM0 */ {"CAM0", 1 << 0, "sysmmu", 0x1}, {"CAM0", 0 << 0, "MIPI_CSIS0", 0x7}, {"CAM0", 1 << 1, "MIPI_CSIS1", 0x7}, {"CAM0", 1 << 2, "FIMC_3AA0", 0x7}, {"CAM0", 1 << 1 | 1 << 2, "FIMC_3AA1", 0x7}, /* CAM1 */ {"CAM1", 1 << 2, "sysmmu_IS_B", 0x7}, {"CAM1", 0 << 0, "MIPI_CSI2", 0x1F}, {"CAM1", 1 << 3, "ISP1", 0x1F}, {"CAM1", 1 << 4, "MIPI_CSI3", 0x1F}, {"CAM1", 1 << 0 | 1 << 2, "sysmmu_SCL", 0x7}, {"CAM1", 1 << 0, "MC_SCALER", 0x7}, {"CAM1", 1 << 0 | 1 << 1 | 1 << 2, "sysmmu_VRA", 0x7}, {"CAM1", 1 << 0 | 1 << 1, "FIMC_VRA", 0x7}, {"CAM1", 1 << 1 | 1 << 2, "sysmmu_CA7", 0x7}, {"CAM1", 1 << 1, "CA7", 0xF}, {"CAM1", 1 << 1 | 1 << 3, "PDMA_IS", 0xF}, /* ISP0 */ {"ISP0", 1 << 0, "sysmmu", 0x1}, {"ISP0", 0 << 0, "FIMC_ISP", 0x3}, {"ISP0", 1 << 1, "FIMC_TPU", 0x3}, /* CP */ {"CP", 0 << 0, "CR7M", 0x18}, {"CP", 1 << 3, "TL3MtoL2", 0x18}, {"CP", 1 << 4, "DMAC", 0x1C}, {"CP", 1 << 2 | 1 << 4, "MEMtoL2", 0x1C}, {"CP", 1 << 3 | 1 << 4, "CSXAP", 0x1F}, {"CP", 1 << 0 | 1 << 3 | 1 << 4, "LMAC", 0x1F}, {"CP", 1 << 1 | 1 << 3 | 1 << 4, "HMtoL2", 0x1F}, }; static struct busmon_nodeinfo ccore_datapath[] = { /* Data Path - CCORE */ {M_NODE, "CCORE_G3D0_M_NODE", 0x10683000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(G3D0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "CCORE_G3D1_M_NODE", 0x10693000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(G3D1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "CCORE_IMEM_M_NODE", 0x106B3000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(IMEM Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "CCORE_AUD_M_NODE", 0x106C3000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(AUD Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "CCORE_CORESIGHT_M_NODE", 0x106D3000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(Coresight) -> Slave(MEM or PERI) ]"}, {M_NODE, "CCORE_CP_M_NODE", 0x10733000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master(CP) -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_0_M_NODE", 0x10603000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_0 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_1_M_NODE", 0x10613000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_1 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS1_0_M_NODE", 0x10623000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS1_0 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS1_1_M_NODE", 0x10633000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS1_1 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_R0_T_M_NODE", 0x10643000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_R0 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_R1_T_M_NODE", 0x10653000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_R1 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_R2_T_M_NODE", 0x10663000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_R2 -> Slave(MEM or PERI) ]"}, {T_M_NODE, "CCORE_BUS0_R3_T_M_NODE", 0x10673000, NULL, 322, 0, false, true, NULL, "DATA Path [ Master -> CCORE_BUS0_R3 -> Slave(MEM or PERI) ]"}, {S_NODE, "CCORE_MEMS_0_S_NODE", 0x10703000, NULL, 322, TIMEOUT, true, true, "MEMS_0", "DATA Path [ Master(Refer Route Information for DATA) -> Slave(MEM0) ]"}, {S_NODE, "CCORE_MEMS_1_S_NODE", 0x10713000, NULL, 322, TIMEOUT, true, true, "MEMS_1", "DATA Path [ Master(Refer Route Information for DATA) -> Slave(MEM1) ]"}, {S_NODE, "CCORE_PERI_S_NODE", 0x10723000, NULL, 322, TIMEOUT, true, true, "PERI", "DATA Path [ Master(Refer Route Information for DATA) -> Slave(PERI) ]"}, }; static struct busmon_nodeinfo bus0_datapath[] = { /* Data Path BUS0 */ {M_NODE, "BUS0_FSYS1_M_NODE", 0x11F03000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(FSYS1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_CAM1_M_NODE", 0X11F13000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(CAM1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_ISP0_M_NODE", 0X11F23000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(ISP0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_DISP0_0_M_NODE", 0X11F33000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(DISP0_0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_DISP0_1_M_NODE", 0X11F43000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(DISP0_1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_DISP1_0_M_NODE", 0X11F53000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(DISP1_0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_DISP1_1_M_NODE", 0X11F63000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(DISP1_1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS0_CAM0_M_NODE", 0X11F73000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master(CAM0 Block) -> Slave(MEM or PERI) ]"}, {T_S_NODE, "BUS0_0_T_S_NODE", 0X11F83000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM or PERI) ]"}, {T_S_NODE, "BUS0_1_T_S_NODE", 0X11F93000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM or PERI) ]"}, {T_S_NODE, "BUS0_R0_T_S_NODE", 0X11FA3000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM0) ]"}, {T_S_NODE, "BUS0_R1_T_S_NODE", 0X11FB3000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM1) ]"}, {T_S_NODE, "BUS0_R2_T_S_NODE", 0X11FC3000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM2) ]"}, {T_S_NODE, "BUS0_R3_T_S_NODE", 0X11FD3000, NULL, 344, 0, false, true, NULL, "DATA Path [ Master -> BUS0 -> Slave(MEM3) ]"}, }; static struct busmon_nodeinfo bus1_datapath[] = { /* Data Path - BUS1 */ {M_NODE, "BUS1_FSYS0_M_NODE", 0x11D03000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master(FSYS0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS1_MSCL0_M_NODE", 0x11D13000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master(MSCL0 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS1_MSCL1_M_NODE", 0x11D23000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master(MSCL1 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS1_MFC0_M_NODE", 0x11D33000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master(MFC01 Block) -> Slave(MEM or PERI) ]"}, {M_NODE, "BUS1_MFC1_M_NODE", 0x11D43000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master(MFC1 Block) -> Slave(MEM or PERI) ]"}, {T_S_NODE, "BUS1_0_T_S_NODE", 0x11D53000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master -> BUS1 -> Slave) ]"}, {T_S_NODE, "BUS1_1_T_S_NODE", 0x11D63000, NULL, 368, 0, false, true, NULL, "DATA Path [ Master -> BUS1 -> Slave) ]"}, }; static struct busmon_nodeinfo ccore_sfrpath[] = { /* SFR Path CCORE */ {M_NODE, "P_CCORE_BUS_M_NODE", 0x104E3000, NULL, 323, 0, false, true, NULL, "SFR Path - Connected with CCORE"}, {S_NODE, "P_CCORE_APL_S_NODE", 0x10443000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(APL(Apollo) Block) ]"}, {S_NODE, "P_CCORE_AUD_S_NODE", 0x10493000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(AUD(Audio) Block) ]"}, {S_NODE, "P_CCORE_CCORE_SFR_S_NODE", 0x104B3000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(CCORE Block) ]"}, {S_NODE, "P_CCORE_CORESIGHT_S_NODE", 0x10423000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(Coresight Access) ]"}, {S_NODE, "P_CCORE_G3D_S_NODE", 0x104A3000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(G3D block) ]"}, {S_NODE, "P_CCORE_MIF0_S_NODE", 0x10453000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MIF0 Block) ]"}, {S_NODE, "P_CCORE_MIF1_S_NODE", 0x10463000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MIF1 Block) ]"}, {S_NODE, "P_CCORE_MIF2_S_NODE", 0x10473000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MIF2 Block) ]"}, {S_NODE, "P_CCORE_MIF3_S_NODE", 0x10483000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MIF3 Block) ]"}, {S_NODE, "P_CCORE_MNGS_NODE", 0x10433000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MNG(mongoose) Block) ]"}, {S_NODE, "P_CCORE_TREX_MIF_S_NODE", 0x104D3000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX_MIF Block) ]"}, {S_NODE, "P_CCORE_TREX_MIF_PERI_S_NODE",0x104C3000, NULL, 323, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX_MIF_PERI Block) ]"}, {T_S_NODE, "P_CCORE_BUS1_T_S_NODE", 0x10403000, NULL, 323, 0, false, true, NULL, "SFR Path [ (Master(Refer Route Information for SFR) -> BUS1 Block) ]"}, {T_S_NODE, "P_CCORE_BUS0_T_S_NODE", 0x10413000, NULL, 323, 0, false, true, NULL, "SFR Path [ (Master(Refer Route Information for SFR) -> BUS0 Block) ]"}, }; static struct busmon_nodeinfo bus1_sfrpath[] = { /* Path BUS1 */ {T_M_NODE, "P_BUS1_CCORE_BUS1_T_M_NODE",0x11CE3000, NULL, 373, 0, false, true, NULL, "SFR Path - Connected with BUS1"}, {S_NODE, "P_BUS1_BUS1_SFR_S_NODE", 0x11C43000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(BUS1 Block) ]"}, {S_NODE, "P_BUS1_FSYS0_S_NODE", 0x11C03000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(FSYS0 Block) ]"}, {S_NODE, "P_BUS1_MFC_S_NODE", 0x11C23000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MFC Block) ]"}, {S_NODE, "P_BUS1_MSCL_S_NODE", 0x11C13000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(MSCL Block) ]"}, {S_NODE, "P_BUS1_TREX_BUS1_S_NODE", 0x11C63000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX_BUS1 Block) ]"}, {S_NODE, "P_BUS1_TREX_BUS1_PERI_S_NODE",0x11C53000, NULL, 373, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX_BUS1_PERI Block) ]"}, }; static struct busmon_nodeinfo bus0_sfrpath[] = { /* Path BUS0 */ {T_M_NODE, "P_BUS0_CCORE_BUS0_T_M_NODE",0x11EE3000, NULL, 352, 0, false, true, NULL, "SFR Path - Connected with BUS0"}, {S_NODE, "P_BUS0_BUS0_SFR_S_NODE", 0x11E73000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(BUS0 Block) ]"}, {S_NODE, "P_BUS0_CAM0_S_NODE", 0x11E63000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(CAM0 Block) ]"}, {S_NODE, "P_BUS0_CAM1_S_NODE", 0x11E53000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(CAM1 Block) ]"}, {S_NODE, "P_BUS0_DISP_S_NODE", 0x11E33000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(DISP Block) ]"}, {S_NODE, "P_BUS0_FSYS1_S_NODE", 0x11E13000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(FSYS1 Block) ]"}, {S_NODE, "P_BUS0_PERIC0_S_NODE", 0x11E23000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(PERIC0 Block) ]"}, {S_NODE, "P_BUS0_PERIC1_S_NODE", 0x11EA3000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(PERIC1 Block) ]"}, {S_NODE, "P_BUS0_PERIS_S_NODE", 0x11E03000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(PERIS Block) ]"}, {S_NODE, "P_BUS0_TREX_BUS0_S_NODE", 0x11E93000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX_BUS0 Block) ]"}, {S_NODE, "P_BUS0_TREX_BUS0_PERI_S_NODE",0x11E83000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(TREX BUS0 PERI) ]"}, {S_NODE, "P_BUS0_VPP_S_NODE", 0x11E43000, NULL, 352, TIMEOUT, true, true, NULL, "SFR Path [ Master(Refer Route Information for SFR) -> Slave(VPP Block) ]"}, }; struct busmon_dev { struct device *dev; struct busmon_platdata *pdata; struct of_device_id *match; int irq; int id; void __iomem *regs; spinlock_t ctrl_lock; struct busmon_notifier notifier_info; }; struct busmon_panic_block { struct notifier_block nb_panic_block; struct busmon_dev *pdev; }; /* declare notifier_list */ static ATOMIC_NOTIFIER_HEAD(busmon_notifier_list); static const struct of_device_id busmon_dt_match[] = { { .compatible = "samsung,exynos-busmonitor", .data = NULL, }, {}, }; MODULE_DEVICE_TABLE(of, busmon_dt_match); static struct busmon_rpathinfo* busmon_get_rpathinfo (struct busmon_dev *busmon, unsigned int id, char *dest_name) { struct busmon_platdata *pdata = busmon->pdata; struct busmon_rpathinfo *rpath = NULL; int i; for (i = 0; i < ARRAY_SIZE(rpathinfo); i++) { if (pdata->rpathinfo[i].id == id) { if (dest_name && !strncmp(pdata->rpathinfo[i].dest_name, dest_name, strlen(pdata->rpathinfo[i].dest_name))) { rpath = &pdata->rpathinfo[i]; break; } } } return rpath; } static struct busmon_masterinfo* busmon_get_masterinfo (struct busmon_dev *busmon, char *port_name, unsigned int user) { struct busmon_platdata *pdata = busmon->pdata; struct busmon_masterinfo *master = NULL; unsigned int val; int i; for (i = 0; i < ARRAY_SIZE(masterinfo); i++) { if (!strncmp(pdata->masterinfo[i].port_name, port_name, strlen(port_name))) { val = user & pdata->masterinfo[i].bits; if (val == pdata->masterinfo[i].user) { master = &pdata->masterinfo[i]; break; } } } return master; } static void busmon_report_ext_info(struct busmon_dev *busmon, struct busmon_nodeinfo *node, unsigned int offset) { unsigned int info_int, info0, info1, info2; info_int = __raw_readl(node->regs + offset + REG_INT_INFO); info0 = __raw_readl(node->regs + offset + REG_EXT_INFO_0); info1 = __raw_readl(node->regs + offset + REG_EXT_INFO_1); info2 = __raw_readl(node->regs + offset + REG_EXT_INFO_2); pr_info("\n--------------------------------------------------------------------------------\n" "Detail NODE information for debuggging\n\n" "Node Name : %s [address: 0x%08X]\n" "INTERRUPT_INFO : 0x%08X\n" "EXT_INFO_0 : 0x%08X\n" "EXT_INFO_1 : 0x%08X\n" "EXT_INFO_2 : 0x%08X\n" "--------------------------------------------------------------------------------\n", node->name, node->phy_regs + offset, info_int, info0, info1, info2); } static void busmon_report_route(struct busmon_dev *busmon, struct busmon_nodeinfo *node, unsigned int offset) { struct busmon_masterinfo *master = NULL; struct busmon_rpathinfo *rpath = NULL; unsigned int val, id, user, addr; val = __raw_readl(node->regs + offset + REG_INT_INFO); id = BIT_ID_VAL(val); val = __raw_readl(node->regs + offset + REG_EXT_INFO_2); user = BIT_AXUSER(val); rpath = busmon_get_rpathinfo(busmon, id, node->need_rpath); if (!rpath) { pr_info("failed to get route path - %s, id:%x\n", node->need_rpath, id); } else { val = __raw_readl(node->regs + offset + REG_EXT_INFO_2); user = BIT_AXUSER(val); master = busmon_get_masterinfo(busmon, rpath->port_name, user); if (!master) { pr_info("failed to get master IP with " "port:%s, id:%x, user:%x\n", rpath->port_name, id, user); } else { addr = __raw_readl(node->regs + offset + REG_EXT_INFO_0); pr_info("\n--------------------------------------------------------------------------------\n" "Route Information for DATA transaction\n\n" "Master IP:%s's %s ---> Target:%s(addr: 0x%08X)\n", master->port_name, master->master_name, rpath->dest_name, addr); } } } static void busmon_report_info(struct busmon_dev *busmon, struct busmon_nodegroup *group, struct busmon_nodeinfo *node, unsigned int offset) { unsigned int val, errcode, addr; bool read = false, req; val = __raw_readl(node->regs + offset + REG_INT_INFO); if (!BIT_ERR_VALID(val)) { if (!strncmp(node->name, "P_CCORE_BUS_M_NODE", strlen(node->name))) { unsigned int master; master = BIT_ID_VAL(val) & 0x7; pr_info("\n--------------------------------------------------------------------------------\n" "Route Information for SFR transaction\n\n" "Master IP : %s\n" "AxID : 0x%X\n" "--------------------------------------------------------------------------------\n", busmon_sfr_errmaster[master], BIT_ID_VAL(val)); } else { pr_info("no information, %s/offset:%x is stopover, " "check other node\n", node->name, offset); } return; } errcode = BIT_ERR_CODE(val); addr = __raw_readl(node->regs + offset + REG_EXT_INFO_0); switch(offset) { case OFFSET_REQ_R: read = true; /* fall down */ case OFFSET_REQ_W: req = true; if (node->type == S_NODE) { /* Only S-Node is able to make log to registers */ pr_info("invalid logged, see more following information\n"); break; } break; case OFFSET_RESP_R: read = true; /* fall down */ case OFFSET_RESP_W: req = false; if (node->type != S_NODE) { /* Only S-Node is able to make log to registers */ pr_info("invalid logged, see more following information\n"); break; } if (node->need_rpath) { /* Data Path */ busmon_report_route(busmon, node, offset); } break; default: pr_info("Unknown Error - offset:%u\n", offset); break; } pr_info("\n--------------------------------------------------------------------------------\n" "Transaction information => Fail to access %s %s \n\n" "reason : %s\n" "Target address : 0x%08X\n" "error type : %s\n" "type : %s\n", read ? "reading" : "wrting", req ? "request" : "response", node->comment, addr, busmon_errcode[errcode], group->name); /* 0x6 means timeout */ if (node->type == S_NODE && errcode == 0x6) { unsigned int mo_offset = read ? REG_R_TIMEOUT_MO : REG_W_TIMEOUT_MO; pr_info("\n--------------------------------------------------------------------------------\n" "additional information for timeout error\n\n" "Timeout MO val : 0x%08X\n", __raw_readl(node->regs + offset + mo_offset)); } /* report extention raw information of register */ busmon_report_ext_info(busmon, node, offset); } static int busmon_parse_info(struct busmon_dev *busmon, struct busmon_nodegroup *group, bool clear) { struct busmon_platdata *pdata = busmon->pdata; struct busmon_nodeinfo *node = NULL; unsigned int val, offset; unsigned long flags; int ret = 0; int i, j, k; spin_lock_irqsave(&busmon->ctrl_lock, flags); if (group) { /* Processing only this group */ node = group->nodeinfo; for (i = 0; i < group->nodesize; i++) { for (j = 0; j < 4; j++) { offset = j * OFFSET_ERR_REPT; /* Check Request information */ val = __raw_readl(node[i].regs + offset + REG_INT_INFO); if (BIT_ERR_OCCURRED(val)) { /* This node occurs the error */ busmon_report_info(busmon, group, &node[i], offset); if (clear) __raw_writel(1, node[i].regs + offset + REG_INT_CLR); ret = 1; } } } } else { /* Processing all group & nodes */ for (i = 0; i < ARRAY_SIZE(pdata->nodegroup); i++) { group = &pdata->nodegroup[i]; node = group->nodeinfo; for (j = 0; j < group->nodesize; j++) { for (k = 0; k < 4; k++) { offset = k * OFFSET_ERR_REPT; /* Check Request information */ val = __raw_readl(node[j].regs + offset + REG_INT_INFO); if (BIT_ERR_OCCURRED(val)) { /* This node occurs the error */ busmon_report_info(busmon, group, &node[j], offset); if (clear) __raw_writel(1, node[j].regs + offset + REG_INT_CLR); ret = 1; } } } } } spin_unlock_irqrestore(&busmon->ctrl_lock, flags); return ret; } static irqreturn_t busmon_irq_handler(int irq, void *data) { struct busmon_dev *busmon = (struct busmon_dev *)data; struct busmon_platdata *pdata = busmon->pdata; struct busmon_nodegroup *group = NULL; int i, ret; /* Check error has been logged */ pr_info("%d interrupt detected\n", (irq - 32)); /* Search busmon group */ for (i = 0; i < ARRAY_SIZE(pdata->nodegroup); i++) { if ((irq - 32) == pdata->nodegroup[i].irq) { pr_info("%s group, %d interrupt occurrs \n", pdata->nodegroup[i].name, irq - 32); group = &pdata->nodegroup[i]; break; } } if (group) { ret = busmon_parse_info(busmon, group, true); if (!ret) { pr_info("%s can't find the error - interrupt %d\n", pdata->nodegroup[i].name, irq - 32); } } else { pr_info("%s can't find interrupt %d number \n", pdata->nodegroup[i].name, irq - 32); } #if 0 /* Disable to call notifier_call_chain of busmon in Exynos8890 EVT0 */ atomic_notifier_call_chain(&busmon_notifier_list, 0, &busmon->notifier_info); panic("Error detected by BUS monitor."); #endif return IRQ_HANDLED; } void busmon_notifier_chain_register(struct notifier_block *block) { atomic_notifier_chain_register(&busmon_notifier_list, block); } static int busmon_logging_panic_handler(struct notifier_block *nb, unsigned long l, void *buf) { struct busmon_panic_block *busmon_panic = (struct busmon_panic_block *)nb; struct busmon_dev *busmon = busmon_panic->pdev; int ret; if (!IS_ERR_OR_NULL(busmon)) { /* Check error has been logged */ ret = busmon_parse_info(busmon, NULL, false); if (!ret) pr_info("No found error in %s\n", __func__); else pr_info("Found errors in %s\n", __func__); } return 0; } static void busmon_init(struct busmon_dev *busmon) { struct busmon_platdata *pdata = busmon->pdata; struct busmon_nodeinfo *node; unsigned int offset; int i, j; for (i = 0; i < ARRAY_SIZE(pdata->nodegroup); i++) { node = pdata->nodegroup[i].nodeinfo; for (j = 0; j < pdata->nodegroup[i].nodesize; j++) { if (node[j].type == S_NODE && node[j].timeout_enabled) { offset = OFFSET_TIMEOUT_REG; /* Enable Timeout setting */ __raw_writel(1, node[j].regs + offset + REG_DBG_CTL); /* set timeout interval value */ __raw_writel(node[j].time_val, node[j].regs + offset + REG_TIMEOUT_INIT_VAL); pr_debug("Exynos BUS Monitor irq:%u - %s timeout enabled\n", node[j].irq, node[j].name); } if (node[j].err_rpt_enabled) { /* clear previous interrupt of req_read */ offset = OFFSET_REQ_R; __raw_writel(1, node[j].regs + offset + REG_INT_CLR); /* enable interrupt */ __raw_writel(1, node[j].regs + offset + REG_INT_MASK); /* clear previous interrupt of req_write */ offset = OFFSET_REQ_W; __raw_writel(1, node[j].regs + offset + REG_INT_CLR); /* enable interrupt */ __raw_writel(1, node[j].regs + offset + REG_INT_MASK); /* clear previous interrupt of response_read */ offset = OFFSET_RESP_R; __raw_writel(1, node[j].regs + offset + REG_INT_CLR); /* enable interrupt */ __raw_writel(1, node[j].regs + offset + REG_INT_MASK); /* clear previous interrupt of response_write */ offset = OFFSET_RESP_W; __raw_writel(1, node[j].regs + offset + REG_INT_CLR); /* enable interrupt */ __raw_writel(1, node[j].regs + offset + REG_INT_MASK); pr_debug("Exynos BUS Monitor irq:%u - %s error reporting enabled\n", node[j].irq, node[j].name); } } } } static int busmon_probe(struct platform_device *pdev) { struct busmon_dev *busmon; struct busmon_panic_block *busmon_panic = NULL; struct busmon_platdata *pdata; struct busmon_nodeinfo *node; char *dev_name; int ret, i, j; busmon = devm_kzalloc(&pdev->dev, sizeof(struct busmon_dev), GFP_KERNEL); if (!busmon) { dev_err(&pdev->dev, "failed to allocate memory for driver's " "private data\n"); return -ENOMEM; } busmon->dev = &pdev->dev; spin_lock_init(&busmon->ctrl_lock); pdata = devm_kzalloc(&pdev->dev, sizeof(struct busmon_platdata), GFP_KERNEL); if (!pdata) { dev_err(&pdev->dev, "failed to allocate memory for driver's " "platform data\n"); return -ENOMEM; } busmon->pdata = pdata; busmon->pdata->masterinfo = masterinfo; busmon->pdata->rpathinfo = rpathinfo; for (i = 0; i < ARRAY_SIZE(pdata->nodegroup); i++) { switch (i) { case PATH_DATA_CCORE: node = ccore_datapath; dev_name = "DATA_PATH_CCORE"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(ccore_datapath); break; case PATH_DATA_BUS0: node = bus0_datapath; dev_name = "DATA_PATH_BUS0"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(bus0_datapath); break; case PATH_DATA_BUS1: node = bus1_datapath; dev_name = "DATA_PATH_BUS1"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(bus1_datapath); break; case PATH_SFR_CCORE: node = ccore_sfrpath; dev_name = "SFR_PATH_CCORE"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(ccore_sfrpath); break; case PATH_SFR_BUS0: node = bus0_sfrpath; dev_name = "SFR_PATH_BUS0"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(bus0_sfrpath); break; case PATH_SFR_BUS1: node = bus1_sfrpath; dev_name = "SFR_PATH_BUS1"; pdata->nodegroup[i].nodesize = ARRAY_SIZE(bus1_sfrpath); break; default: break; }; pdata->nodegroup[i].nodeinfo = node; pdata->nodegroup[i].irq = node[0].irq; pdata->nodegroup[i].name = dev_name; ret = devm_request_irq(&pdev->dev, pdata->nodegroup[i].irq + 32, busmon_irq_handler, 0, //IRQF_GIC_MULTI_TARGET, dev_name, busmon); for (j = 0; j < pdata->nodegroup[i].nodesize; j++) { node[j].regs = devm_ioremap_nocache(&pdev->dev, node[j].phy_regs, SZ_16K); if (node[j].regs == NULL) { dev_err(&pdev->dev, "failed to claim register region - %s\n", dev_name); return -ENOENT; } } } busmon_panic = devm_kzalloc(&pdev->dev, sizeof(struct busmon_panic_block), GFP_KERNEL); if (!busmon_panic) { dev_err(&pdev->dev, "failed to allocate memory for driver's " "panic handler data\n"); } else { busmon_panic->nb_panic_block.notifier_call = busmon_logging_panic_handler; busmon_panic->pdev = busmon; atomic_notifier_chain_register(&panic_notifier_list, &busmon_panic->nb_panic_block); } platform_set_drvdata(pdev, busmon); busmon_init(busmon); dev_info(&pdev->dev, "success to probe bus monitor driver\n"); return 0; } static int busmon_remove(struct platform_device *pdev) { platform_set_drvdata(pdev, NULL); return 0; } #ifdef CONFIG_PM_SLEEP static int busmon_suspend(struct device *dev) { return 0; } static int busmon_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct busmon_dev *busmon = platform_get_drvdata(pdev); busmon_init(busmon); return 0; } static SIMPLE_DEV_PM_OPS(busmon_pm_ops, busmon_suspend, busmon_resume); #define BUSMON_PM (busmon_pm_ops) #else #define BUSMON_PM NULL #endif static struct platform_driver exynos_busmon_driver = { .probe = busmon_probe, .remove = busmon_remove, .driver = { .name = "exynos-busmon", .of_match_table = busmon_dt_match, .pm = &busmon_pm_ops, }, }; module_platform_driver(exynos_busmon_driver); MODULE_DESCRIPTION("Samsung Exynos8890 BUS MONITOR DRIVER"); MODULE_AUTHOR("Hosung Kim