summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSantosh Shilimkar <santosh.shilimkar@ti.com>2010-09-12 13:24:09 +0530
committerSebastien Jan <s-jan@ti.com>2010-11-03 15:57:59 +0100
commit5a41892872bd08fc05ffae6ffe092f3d6ce097a1 (patch)
tree8c17faab9fa06da31e690674d8032afd757965da
parentf91a4e83ddac4b1c1e6b3c46ede0f159d76638ec (diff)
omap4: debug: L3 interconnect error handling
This patch adds L3 interconnect error handling for OMAP4. For now it is intended to be a debug only patch. To enable L3 errors: System Type ---> [ ] Enable L3 error logging Some examples to create L3 errors: ./readmem 0x4bf0a00C : Standard error with GPMC ./readmem 0x480ba000 : Custom error with L4 PER2 domain ./readmem 0x4b00a000 : Standard error with source SHA1 Signed-off-by: Sricharan <r.sricharan@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
-rw-r--r--arch/arm/mach-omap2/Kconfig6
-rw-r--r--arch/arm/mach-omap2/omap4-common.c343
2 files changed, 349 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index ed0b88df0d43..0421744c1018 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -254,3 +254,9 @@ config OMAP3_SDRC_AC_TIMING
wish to say no. Selecting yes without understanding what is
going on could result in system crashes;
+config ENABLE_L3_ERRORS
+ bool "Enable L3 error logging"
+ depends on ARCH_OMAP4
+ default n
+ help
+ Select this option to enable L3 error detection on OMAP4
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index d01bf1add99d..e11553cd43af 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -15,12 +15,166 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <asm/hardware/gic.h>
#include <asm/hardware/cache-l2x0.h>
#include <mach/hardware.h>
#include <mach/omap4-common.h>
+#include <plat/control.h>
+
+#ifdef CONFIG_ENABLE_L3_ERRORS
+/*
+ * L3 register offsets
+ */
+#define L3_44XX_BASE_CLK1 0x44000000
+#define L3_44XX_BASE_CLK2 0x44800000
+#define L3_44XX_BASE_CLK3 0x45000000
+#define L3_44XX_BASE_FIREWALL 0x4A204000
+#define CUSTOM_ERROR 0x2
+#define STANDARD_ERROR 0x0
+#define INBAND_ERROR 0x0
+#define CLEAR_STDERR_LOG 80000000
+#define EMIF_KERRLOG_OFFSET 0x10
+#define L3_SLAVE_ADDRESS_OFFSET 0x14
+#define LOGICAL_ADDR_ERRORLOG 0x4
+
+u32 l3_flagmux_regerr[3] = {
+ 0x50C,
+ 0x100C,
+ 0X020C
+};
+
+/*
+ * L3 Target standard Error register offsets
+ */
+u32 l3_targ_stderrlog_main_clk1[5] = {
+ 0x148, /* DMM1 */
+ 0x248, /* DMM2 */
+ 0x348, /* ABE */
+ 0x448, /* L4CFG */
+ 0x648 /* CLK2 PWR DISC */
+};
+
+u32 l3_targ_stderrlog_main_clk2[18] = {
+ 0x548, /* COREX M3 */
+ 0x348, /* DSS */
+ 0x148, /* GPMC */
+ 0x448, /* ISS */
+ 0x748, /* IVAHD */
+ 0xD48, /* missing in TRM corresponds to AES1*/
+ 0x948, /* L4 PER0*/
+ 0x248, /* OCMRAM */
+ 0x148, /* missing in TRM corresponds to GPMC sERROR*/
+ 0x648, /* SGX */
+ 0x848, /* SL2 */
+ 0x1648, /* C2C */
+ 0x1148, /* missing in TRM corresponds PWR DISC CLK1*/
+ 0xF48, /* missing in TRM corrsponds to SHA1*/
+ 0xE48, /* missing in TRM corresponds to AES2*/
+ 0xC48, /* L4 PER3 */
+ 0xA48, /* L4 PER1*/
+ 0xB48 /* L4 PER2*/
+};
+
+/* Firewall Register Offsets BASE ADDRES 0x4A204000 */
+u32 kerrlog_firewall[15] = {
+ 0xE000, /* L3RAM SOURCE = 1*/
+ 0xC000, /* GPMC SOURCE = 2*/
+ 0x8000, /* EMIF SOURCE = 3*/
+ 0x1C000, /* IVAHD SOURCE = 4*/
+ 0x14000, /* DUAL CORTEX M3 SOURCE = 5*/
+ 0x1A000, /* SL2 SOURCE = 6*/
+ 0x0, /* C2C Master SOURCE = 12*/
+ 0x10000, /* SGX SOURCE = 13*/
+ 0x18000, /* DSS SOURCE = 14*/
+ 0x12000, /* ISS SOURCE = 15*/
+ 0x0, /* L4 PER1. Missing in TRM SOURCE = 16*/
+ 0x0, /* L4 CONFIG. Missing in TRM SOURCE = 17*/
+ 0x0, /* DEBUGSS. Missing in TRM SOURCE = 18*/
+ 0x24000, /* L4 ABE SOURCE = 19*/
+ 0x2000 /* C2C SLAVE SOURCE = 20*/
+};
+
+/* Firewall Register source names */
+char *kerrlog_firewall_sourcename[15] = {
+ "L3RAM",
+ "GPMC",
+ "EMIF",
+ "IVAHD",
+ "DUAL CORTEX M3",
+ "SL2",
+ "C2C Master",
+ "SGX",
+ "DSS",
+ "ISS",
+ "L4 PER1",
+ "L4 CONFIG",
+ "DEBUGSS",
+ "L4 ABE",
+ "C2C SLAVE"
+};
+
+
+u32 l3_targ_stderrlog_main_clk3[1] = {
+ 0x0148 /* EMUSS */
+};
+
+
+char *l3_targ_stderrlog_main_clk1_name[5] = {
+ "DMM1",
+ "DMM2",
+ "ABE",
+ "L4CFG",
+ "CLK2 PWR DISC"
+};
+
+char *l3_targ_stderrlog_main_clk2_name[18] = {
+ "COREX M3" ,
+ "DSS ",
+ "GPMC ",
+ "ISS ",
+ "IVAHD ",
+ "AES1",
+ "L4 PER0",
+ "OCMRAM ",
+ "GPMC sERROR",
+ "SGX ",
+ "SL2 ",
+ "C2C ",
+ "PWR DISC CLK1",
+ "SHA1",
+ "AES2",
+ "L4 PER3",
+ "L4 PER1",
+ "L4 PER2"
+};
+
+char *l3_targ_stderrlog_main_clk3_name[1] = {
+ "EMUSS"
+};
+
+u32 *l3_targ_stderrlog_main[3] = {
+ l3_targ_stderrlog_main_clk1,
+ l3_targ_stderrlog_main_clk2,
+ l3_targ_stderrlog_main_clk3,
+};
+
+u32 *l3_targ_stderrlog_main_sourcename[3] = {
+ (u32 *)l3_targ_stderrlog_main_clk1_name,
+ (u32 *)l3_targ_stderrlog_main_clk2_name,
+ (u32 *)l3_targ_stderrlog_main_clk3_name
+};
+
+u32 ctrl_sec_err_stat[2] = {
+ 0x2D0,
+ 0x2D4
+};
+
+void __iomem *l3_base[4];
+void __iomem *ctrl_base;
+#endif
#ifdef CONFIG_CACHE_L2X0
void __iomem *l2cache_base;
@@ -115,3 +269,192 @@ static int __init omap4_syslink_init(void)
return retval;
}
device_initcall(omap4_syslink_init);
+
+#ifdef CONFIG_ENABLE_L3_ERRORS
+static void omap_fw_error_handler(u32 ctrl_sec_err_status,
+ u32 ctrl_sec_err_status_regval)
+{
+ u32 err_source_firewall = 0;
+ u32 kerror_log_fw = 0;
+ u32 kerror_log_val = 0;
+ u32 j = 0;
+ u32 k = 0;
+
+ /* Identify the source */
+ for (j = 0; !err_source_firewall; j++)
+ err_source_firewall = ctrl_sec_err_status_regval & (1<<j);
+ /*
+ * -1 Since array offset is from Zero.One more -1 since CONTROL SEC
+ * register starts from 1 and not 0.
+ */
+ err_source_firewall = j-1-1;
+
+ /*
+ * Get the details from KerrorLog registers and print the firewall
+ * error details
+ */
+ if (err_source_firewall == 0x3) {
+ /* only for EMIF there are three kerrlog registers*/
+ for (k = 0; k < 3; k++) {
+ kerror_log_fw = (u32)l3_base[3] +
+ kerrlog_firewall[err_source_firewall] +
+ (EMIF_KERRLOG_OFFSET * k);
+ kerror_log_val = readl(kerror_log_fw);
+ /*
+ * Print the error log and clear it . The Command is
+ * yet to be updated after checking with HARDWARE TEAM
+ */
+ pr_crit("FireWall Error : SOURCE:%s at ADDRESS 0x%x\n",
+ kerrlog_firewall_sourcename[err_source_firewall],
+ readl((kerror_log_fw+LOGICAL_ADDR_ERRORLOG)));
+ writel(kerror_log_val, kerror_log_fw);
+ }
+ } else {
+ kerror_log_fw = (u32)l3_base[3] +
+ kerrlog_firewall[err_source_firewall];
+ kerror_log_val = readl(kerror_log_fw);
+ /* Print the error log and clear it.*/
+ pr_crit("FireWall Error: SOURCE :%s at ADDRESS 0x%x\n", \
+ kerrlog_firewall_sourcename[err_source_firewall], \
+ readl((kerror_log_fw+LOGICAL_ADDR_ERRORLOG))); \
+ writel(kerror_log_val, kerror_log_fw);
+ }
+
+ /* Clear control status bits . This has to be tested*/
+ writel(ctrl_sec_err_status_regval, ctrl_sec_err_status);
+ return;
+}
+
+/*
+ * Interrupt Handler for L3 error detection
+ * 1) Identify the clock domain to which the error belongs to
+ * 2) Identify the slave where the error information is logged
+ * 3) Print the logged information
+ */
+static irqreturn_t l3_interrupt_handler(int irq, void *dev_id)
+{
+ int inttype, i, j;
+ int err_source = 0;
+ u32 stderrlog_main, stderrlog_main_reg_val, error_source_reg;
+ u32 ctrl_sec_err_status, ctrl_sec_err_status_regval, slave_addr;
+ char *source_name;
+
+ /*
+ * Get the Type of interrupt
+ * 0- Application
+ * 1 - Debug
+ */
+ if (irq == OMAP44XX_IRQ_L3_APP)
+ inttype = 0;
+ else
+ inttype = 1;
+
+ for (i = 0; i < 3; i++) {
+ /*
+ * Read the regerr register of the clock domain
+ * to determine the source
+ */
+ error_source_reg = readl((l3_base[i] + l3_flagmux_regerr[i]
+ + (inttype << 3)));
+ /* Get the corresponding error and analyse */
+ if (error_source_reg) {
+ /* Identify the source from control status register */
+ for (j = 0; !err_source; j++)
+ err_source = error_source_reg & (1<<j);
+ /* Since array offset is from Zero */
+ err_source = j-1;
+ /* Read the stderrlog_main_source from clk domain */
+ stderrlog_main = (u32)l3_base[i] +
+ (*(l3_targ_stderrlog_main[i] + err_source));
+ stderrlog_main_reg_val = readl(stderrlog_main);
+
+ switch ((stderrlog_main_reg_val & CUSTOM_ERROR)) {
+ case STANDARD_ERROR:
+ /* check if this is a firewall violation */
+ ctrl_sec_err_status = (u32)ctrl_base +
+ ctrl_sec_err_stat[inttype];
+ ctrl_sec_err_status_regval =
+ readl(ctrl_sec_err_status);
+ source_name =
+ (char *)(*(l3_targ_stderrlog_main_sourcename[i]
+ + err_source));
+ slave_addr = stderrlog_main +
+
+ L3_SLAVE_ADDRESS_OFFSET;
+ if (!ctrl_sec_err_status_regval) {
+ /*
+ * get the details about the inband
+ * error as command etc and print
+ * details
+ */
+ pr_crit("L3 standard error: SOURCE:%s"
+ "at address 0x%x\n",
+ source_name, readl(slave_addr));
+ } else {
+ /* Then this is a Fire Wall Error */
+ omap_fw_error_handler(
+ ctrl_sec_err_status,
+ ctrl_sec_err_status_regval);
+ }
+ /* clear the stderr log */
+ writel((stderrlog_main_reg_val |
+ CLEAR_STDERR_LOG), stderrlog_main);
+ break;
+ case CUSTOM_ERROR:
+ pr_crit("CUSTOM SRESP error with SOURCE:%s\n",
+ (char *)(*(l3_targ_stderrlog_main_sourcename[i]
+ + err_source)));
+ /* clear the std error log*/
+ writel((stderrlog_main_reg_val |
+ CLEAR_STDERR_LOG), stderrlog_main);
+ break;
+ default:
+ /* Nothing to be handled here as of now */
+ break;
+ }
+ /* Error found so break the for loop */
+ break;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int __init omap_l3_init(void)
+{
+ int ret;
+
+ /* Static mapping. Never released map it for 1M*/
+ l3_base[0] = ioremap(L3_44XX_BASE_CLK1, SZ_1M);
+ l3_base[1] = ioremap(L3_44XX_BASE_CLK2, SZ_1M);
+ l3_base[2] = ioremap(L3_44XX_BASE_CLK3, SZ_1M);
+ l3_base[3] = ioremap(L3_44XX_BASE_FIREWALL, SZ_1M);
+ if ((!l3_base[0]) || (!l3_base[1]) || (!l3_base[2]) || (!l3_base[3])) {
+ pr_crit("Could not ioremap L3 address space\n");
+ return -ENOMEM;
+ }
+ ctrl_base = omap_ctrl_base_get();
+
+ /*
+ * Setup interrupt Handlers
+ */
+ ret = request_irq(OMAP44XX_IRQ_L3_DBG,
+ (irq_handler_t)l3_interrupt_handler,
+ IRQF_DISABLED, "l3_debug_error", NULL);
+ if (ret) {
+ pr_crit("L3: request_irq failed to register for 0x%x\n",
+ OMAP44XX_IRQ_L3_DBG);
+ return ret;
+ }
+ ret = request_irq(OMAP44XX_IRQ_L3_APP,
+ (irq_handler_t)l3_interrupt_handler,
+ IRQF_DISABLED, "l3_app_error", NULL);
+ if (ret) {
+ pr_crit("L3: request_irq failed to register for 0x%x\n",
+ OMAP44XX_IRQ_L3_APP);
+ return ret;
+ }
+ return 0;
+}
+early_initcall(omap_l3_init);
+#endif
+