diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c index f3890f4ba6..872d2832e2 100644 --- a/arch/arm/cpu/armv7/cache_v7.c +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -16,6 +16,33 @@ #define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4 #ifndef CONFIG_SYS_DCACHE_OFF + +void set_l2_indirect_reg(u32 reg_addr, u32 val) +{ + + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t" + "isb\n\t" + : + : [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val) + ); +} + +u32 get_l2_indirect_reg(u32 reg_addr) +{ + u32 val; + + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t" + : [l2cpdr]"=r" (val) + : [l2cpselr]"r" (reg_addr) + ); + + return val; +} + /* * Write the level and type you want to Cache Size Selection Register(CSSELR) * to get size details from Current Cache Size ID Register(CCSIDR) diff --git a/arch/arm/cpu/armv7/cpu.c b/arch/arm/cpu/armv7/cpu.c index 6eac5ef3fe..9b5d048563 100644 --- a/arch/arm/cpu/armv7/cpu.c +++ b/arch/arm/cpu/armv7/cpu.c @@ -75,6 +75,8 @@ int cleanup_before_linux_select(int flags) */ cpu_cache_initialization(); + clear_l2cache_err(); + return 0; } diff --git a/board/qca/arm/common/board_init.c b/board/qca/arm/common/board_init.c index fbd797050e..8891fbc1b9 100644 --- a/board/qca/arm/common/board_init.c +++ b/board/qca/arm/common/board_init.c @@ -62,6 +62,20 @@ int board_init(void) int ret; uint32_t start_blocks; uint32_t size_blocks; + +#ifdef CONFIG_IPQ_REPORT_L2ERR + u32 l2esr; + + /* Record any kind of L2 errors caused during + * the previous boot stages as we are clearing + * the L2 errors before jumping to linux. + * Refer to cleanup_before_linux() */ +#ifndef CONFIG_SYS_DCACHE_OFF + l2esr = get_l2_indirect_reg(L2ESR_IND_ADDR); +#endif + report_l2err(l2esr); +#endif + qca_smem_flash_info_t *sfi = &qca_smem_flash_info; gd->bd->bi_boot_params = QCA_BOOT_PARAMS_ADDR; @@ -249,6 +263,35 @@ int dram_init(void) return 0; } +#ifdef CONFIG_IPQ_REPORT_L2ERR +void report_l2err(u32 l2esr) +{ + if (l2esr & L2ESR_MPDCD) + printf("L2 Master port decode error\n"); + + if (l2esr & L2ESR_MPSLV) + printf("L2 master port slave error\n"); + + if (l2esr & L2ESR_TSESB) + printf("L2 tag soft error, single-bit\n"); + + if (l2esr & L2ESR_TSEDB) + printf("L2 tag soft error, double-bit\n"); + + if (l2esr & L2ESR_DSESB) + printf("L2 data soft error, single-bit\n"); + + if (l2esr & L2ESR_DSEDB) + printf("L2 data soft error, double-bit\n"); + + if (l2esr & L2ESR_MSE) + printf("L2 modified soft error\n"); + + if (l2esr & L2ESR_MPLDREXNOK) + printf("L2 master port LDREX received Normal OK response\n"); +} +#endif + void enable_caches(void) { icache_enable(); @@ -259,7 +302,7 @@ void disable_caches(void) icache_disable(); } -void clear_l2cache_err(void) +__weak void clear_l2cache_err(void) { return; } diff --git a/board/qca/arm/ipq806x/ipq806x.c b/board/qca/arm/ipq806x/ipq806x.c index f3971bb6e4..8f1c6408e0 100644 --- a/board/qca/arm/ipq806x/ipq806x.c +++ b/board/qca/arm/ipq806x/ipq806x.c @@ -831,3 +831,19 @@ int ipq_get_tz_version(char *version_name, int buf_size) snprintf(version_name, buf_size, "%s\n", version_name); return 0; } + +void clear_l2cache_err(void) +{ + unsigned int val; +#ifndef CONFIG_SYS_DCACHE_OFF + val = get_l2_indirect_reg(L2ESR_IND_ADDR); +#endif + +#ifdef CONFIG_IPQ_REPORT_L2ERR + report_l2err(val); +#endif + +#ifndef CONFIG_SYS_DCACHE_OFF + set_l2_indirect_reg(L2ESR_IND_ADDR, val); +#endif +} diff --git a/board/qca/arm/ipq806x/ipq806x.h b/board/qca/arm/ipq806x/ipq806x.h index a8647b66bb..657701b16b 100644 --- a/board/qca/arm/ipq806x/ipq806x.h +++ b/board/qca/arm/ipq806x/ipq806x.h @@ -42,6 +42,18 @@ */ #define RESET_WDT_BARK_TIME (5 * RESET_WDT_BITE_TIME) +#define L2ESR_IND_ADDR (0x204) + +/* L2ESR bit fields */ +#define L2ESR_MPDCD BIT(0) +#define L2ESR_MPSLV BIT(1) +#define L2ESR_TSESB BIT(2) +#define L2ESR_TSEDB BIT(3) +#define L2ESR_DSESB BIT(4) +#define L2ESR_DSEDB BIT(5) +#define L2ESR_MSE BIT(6) +#define L2ESR_MPLDREXNOK BIT(8) + #define CE1_REG_USAGE (0) #define CE1_ADM_USAGE (1) #define CE1_RESOURCE (1) diff --git a/include/common.h b/include/common.h index a786ff56c5..64c82cec9f 100644 --- a/include/common.h +++ b/include/common.h @@ -778,6 +778,10 @@ void flush_dcache_range(unsigned long start, unsigned long stop); void invalidate_dcache_range(unsigned long start, unsigned long stop); void invalidate_dcache_all(void); void invalidate_icache_all(void); +void set_l2_indirect_reg(u32 reg_addr, u32 val); +void clear_l2cache_err(void); +u32 get_l2_indirect_reg(u32 reg_addr); +void report_l2err(u32 l2esr); enum { /* Disable caches (else flush caches but leave them active) */ diff --git a/include/configs/ipq806x.h b/include/configs/ipq806x.h index 7071acdae5..461f107354 100644 --- a/include/configs/ipq806x.h +++ b/include/configs/ipq806x.h @@ -328,7 +328,7 @@ typedef struct { /* Enabling this flag will report any L2 errors. * By default we are disabling it */ -/*#define CONFIG_IPQ_REPORT_L2ERR*/ +#define CONFIG_IPQ_REPORT_L2ERR /* * Location in IMEM which contains the physical address of