/* * The actual error handling takes longer than is ideal so this must be * threaded. */ static irqreturn_t tegra_mc_error_hard_irq(int irq, void *data) { u32 intr; err_channel = 0; intr = mc_readl(MC_INT_STATUS); /* * Sometimes the MC seems to generate spurious interrupts - that * is interrupts with an interrupt status register equal to 0. * Not much we can do other than keep a count of them. */ if (!intr) { spurious_intrs++; return IRQ_NONE; } trace_printk("MCERR detected.\n"); /* * We have an interrupt; disable the rest until this one is handled. * This means we will potentially miss interrupts. We can live with * that. */ mc_writel(0, MC_INT_MASK); mc_readl(MC_INT_STATUS); mc_intr = intr & mc_int_mask; return IRQ_WAKE_THREAD; }
static void tegra30_mc_decode(struct tegra30_mc *mc, int n) { u32 err, addr; const char * const mc_int_err[] = { "MC_DECERR", "Unknown", "MC_SECURITY_ERR", "MC_ARBITRATION_EMEM", "MC_SMMU_ERR", }; const char * const err_type[] = { "Unknown", "Unknown", "DECERR_EMEM", "SECURITY_TRUSTZONE", "SECURITY_CARVEOUT", "Unknown", "INVALID_SMMU_PAGE", "Unknown", }; char attr[6]; int cid, perm, type, idx; const char *client = "Unknown"; idx = n - MC_INT_ERR_SHIFT; if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) { dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n", BIT(n)); return; } err = mc_readl(mc, MC_ERR_STATUS); type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT; perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >> MC_ERR_INVALID_SMMU_PAGE_SHIFT; if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE) sprintf(attr, "%c-%c-%c", (perm & BIT(2)) ? 'R' : '-', (perm & BIT(1)) ? 'W' : '-', (perm & BIT(0)) ? 'S' : '-'); else attr[0] = '\0'; cid = err & MC_CLIENT_ID_MASK; if (cid < ARRAY_SIZE(tegra30_mc_client)) client = tegra30_mc_client[cid]; addr = mc_readl(mc, MC_ERR_ADR); dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n", mc_int_err[idx], err, addr, client, (err & MC_ERR_SECURITY) ? "secure" : "non-secure", (err & MC_ERR_RW) ? "write" : "read", err_type[type], attr); }
static irqreturn_t tegra30_mc_isr(int irq, void *data) { u32 stat, mask, bit; struct tegra30_mc *mc = data; stat = mc_readl(mc, MC_INTSTATUS); mask = mc_readl(mc, MC_INTMASK); mask &= stat; if (!mask) return IRQ_NONE; while ((bit = ffs(mask)) != 0) tegra30_mc_decode(mc, bit - 1); mc_writel(mc, stat, MC_INTSTATUS); return IRQ_HANDLED; }
static int tegra30_mc_suspend(struct device *dev) { int i; struct tegra30_mc *mc = dev_get_drvdata(dev); for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]); return 0; }
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) { u8 dram_count; dram_count = mc_readl(mc, MC_EMEM_ADR_CFG); dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV; dram_count++; return dram_count; }
static inline void set_mc_arbiter_limits(void) { u32 reg = mc_readl(MC_EMEM_ARB_OUTSTANDING_REQ); u32 max_val = 0x50 << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT; if (!(reg & MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE) || ((reg & MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK) > max_val)) { reg = MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE | MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE | max_val; mc_writel(reg, MC_EMEM_ARB_OUTSTANDING_REQ); mc_writel(0x1, MC_TIMING_CONTROL); } }
static int tegra30_mc_resume(struct device *dev) { int i; struct tegra30_mc *mc = dev_get_drvdata(dev); for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++) mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]); mc_writel(mc, 1, MC_TIMING_CONTROL); /* Read-back to ensure that write reached */ mc_readl(mc, MC_TIMING_CONTROL); return 0; }
static inline void do_clock_change(u32 clk_setting) { int err; mc_readl(MC_EMEM_ADR_CFG); /* completes prev writes */ writel(clk_setting, clk_base + emc->reg); readl(clk_base + emc->reg);/* completes prev write */ err = wait_for_update(EMC_INTSTATUS, EMC_INTSTATUS_CLKCHANGE_COMPLETE, true); if (err) { pr_err("%s: clock change completion error: %d", __func__, err); BUG(); } }
/* * Common MC error handling code. */ static irqreturn_t tegra_mc_error_thread(int irq, void *data) { struct mc_client *client = NULL; const struct mc_error *fault; const char *smmu_info; unsigned long count; phys_addr_t addr; u32 status, intr = mc_intr; u32 write, secure; u32 client_id; cancel_delayed_work(&unthrottle_prints_work); if (intr & MC_INT_ARBITRATION_EMEM) { arb_intr(); if (intr == MC_INT_ARBITRATION_EMEM) goto out; intr &= ~MC_INT_ARBITRATION_EMEM; } count = atomic_inc_return(&error_count); fault = chip_specific.mcerr_info(intr & mc_int_mask); if (WARN(!fault, "Unknown error! intr sig: 0x%08x\n", intr & mc_int_mask)) goto out; if (fault->flags & E_NO_STATUS) { mcerr_pr("MC fault - no status: %s\n", fault->msg); goto out; } status = __mc_readl(err_channel, fault->stat_reg); addr = __mc_readl(err_channel, fault->addr_reg); secure = !!(status & MC_ERR_STATUS_SECURE); write = !!(status & MC_ERR_STATUS_WRITE); client_id = status & 0xff; client = &mc_clients[client_id <= mc_client_last ? client_id : mc_client_last]; /* * LPAE: make sure we get the extra 2 physical address bits available * and pass them down to the printing function. */ addr |= (((phys_addr_t)(status & MC_ERR_STATUS_ADR_HI)) << 12); if (fault->flags & E_SMMU) smmu_info = smmu_page_attrib[MC_ERR_SMMU_BITS(status)]; else smmu_info = NULL; mcerr_info_update(client, intr & mc_int_mask); if (mcerr_throttle_enabled && count >= MAX_PRINTS) { schedule_delayed_work(&unthrottle_prints_work, HZ/2); if (count == MAX_PRINTS) mcerr_pr("Too many MC errors; throttling prints\n"); goto out; } chip_specific.mcerr_print(fault, client, status, addr, secure, write, smmu_info); out: mc_writel(intr, MC_INT_STATUS); mc_readl(MC_INT_MASK); mc_writel(mc_int_mask, MC_INT_MASK); return IRQ_HANDLED; }