Add all mc-errors supported by T186. Implement mc interrupt handling routine for T186. Signed-off-by: Ashish Mhetre <amhetre@xxxxxxxxxx> --- drivers/memory/tegra/mc.h | 17 +++++++ drivers/memory/tegra/tegra186.c | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 2d4f495..7817492 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -44,6 +44,15 @@ #define MC_TIMING_CONTROL_DBG 0xf8 #define MC_TIMING_CONTROL 0xfc +#define MC_ERR_VPR_STATUS 0x654 +#define MC_ERR_VPR_ADR 0x658 +#define MC_ERR_SEC_STATUS 0x67c +#define MC_ERR_SEC_ADR 0x680 +#define MC_ERR_MTS_STATUS 0x9b0 +#define MC_ERR_MTS_ADR 0x9b4 +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 +#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 + #define MC_INT_DECERR_ROUTE_SANITY BIT(20) #define MC_INT_WCAM_ERR BIT(19) #define MC_INT_SCRUB_ECC_WR_ACK BIT(18) @@ -159,6 +168,14 @@ extern const struct tegra_mc_ops tegra186_mc_ops; extern const char * const tegra_mc_status_names[32]; extern const char * const tegra_mc_error_names[8]; +struct tegra_mc_error { + u32 int_bit; + const char *msg; + u32 status_reg; + u32 addr_reg; + u32 addr_reg_hi; +}; + /* * These IDs are for internal use of Tegra ICC drivers. The ID numbers are * chosen such that they don't conflict with the device-tree ICC node IDs. diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 6766cc4..4f3ae71 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -146,8 +146,107 @@ static void tegra186_mc_clear_interrupt(struct tegra_mc *mc) mc_writel(mc, MC_INTSTATUS_CLEAR, MC_INTSTATUS); } +static const struct tegra_mc_error int_mc_errors[] = { + { + .int_bit = MC_INT_DECERR_EMEM, + .msg = "EMEM address decode error", + .status_reg = MC_ERR_STATUS, + .addr_reg = MC_ERR_ADR, + }, + { + .int_bit = MC_INT_SECURITY_VIOLATION, + .msg = "non secure access to secure region", + .status_reg = MC_ERR_STATUS, + .addr_reg = MC_ERR_ADR, + }, + { + .int_bit = MC_INT_DECERR_VPR, + .msg = "MC request violates VPR requirements", + .status_reg = MC_ERR_VPR_STATUS, + .addr_reg = MC_ERR_VPR_ADR, + }, + { + .int_bit = MC_INT_SECERR_SEC, + .msg = "MC request violated SEC carveout requirements", + .status_reg = MC_ERR_SEC_STATUS, + .addr_reg = MC_ERR_SEC_ADR, + }, + { + .int_bit = MC_INT_DECERR_MTS, + .msg = "MTS carveout access violation", + .status_reg = MC_ERR_MTS_STATUS, + .addr_reg = MC_ERR_MTS_ADR, + }, + { + .int_bit = MC_INT_DECERR_GENERALIZED_CARVEOUT, + .msg = "GSC access violation", + .status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS, + .addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR, + }, +}; + +static irqreturn_t tegra186_mc_handle_irq(int irq, void *data) +{ + struct tegra_mc *mc = data; + unsigned long status; + unsigned int bit; + + status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + if (!status) + return IRQ_NONE; + + for_each_set_bit(bit, &status, 32) { + const char *error = int_mc_errors[bit].msg ?: "unknown"; + const char *client = "unknown"; + const char *direction, *secure; + phys_addr_t addr = 0; + unsigned int i; + u8 id; + u32 value; + + value = mc_readl(mc, int_mc_errors[bit].status_reg); + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + if (mc->soc->num_address_bits > 32) { + addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & + MC_ERR_STATUS_ADR_HI_MASK); + addr <<= 32; + } +#endif + addr |= mc_readl(mc, int_mc_errors[bit].addr_reg); + + if (value & MC_ERR_STATUS_RW) + direction = "write"; + else + direction = "read"; + + if (value & MC_ERR_STATUS_SECURITY) + secure = "secure "; + else + secure = ""; + + id = value & mc->soc->client_id_mask; + + for (i = 0; i < mc->soc->num_clients; i++) { + if (mc->soc->clients[i].id == id) { + client = mc->soc->clients[i].name; + break; + } + } + + dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s\n", + client, secure, direction, &addr, error); + } + + /* clear interrupts */ + mc_writel(mc, status, MC_INTSTATUS); + + return IRQ_HANDLED; +} + const struct tegra_mc_interrupt_ops tegra186_mc_interrupt_ops = { .clear_interrupt = tegra186_mc_clear_interrupt, + .handle_irq = tegra186_mc_handle_irq, }; const struct tegra_mc_ops tegra186_mc_ops = { @@ -886,6 +985,7 @@ const struct tegra_mc_soc tegra186_mc_soc = { .num_clients = ARRAY_SIZE(tegra186_mc_clients), .clients = tegra186_mc_clients, .num_address_bits = 40, + .client_id_mask = 0xff, .intmask = MC_INT_WCAM_ERR | MC_INT_SCRUB_ECC_WR_ACK | MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | -- 2.7.4