blob: 851644a3362fa47c25cc8573ff52194e80cf751b [file] [log] [blame]
/*
* 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 <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/exynos-busmon.h>
/* 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 <hosung0.kim@samsung.com");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:exynos-busmon");