/*
 * 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;
}
Example #2
0
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;
}
Example #5
0
File: mc.c Project: AK101111/linux
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;
}
Example #8
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;
}