Commit 1079a66b authored by Thierry Reding's avatar Thierry Reding Committed by Krzysztof Kozlowski
Browse files

memory: tegra: Parameterize interrupt handler



Tegra20 requires a slightly different interrupt handler than Tegra30 and
later, so parameterize the handler, so that each SoC implementation can
provide its own.

Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
Link: https://lore.kernel.org/r/20210602163302.120041-8-thierry.reding@gmail.com


Signed-off-by: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
parent ddeceab0
Loading
Loading
Loading
Loading
+27 −109
Original line number Diff line number Diff line
@@ -492,32 +492,7 @@ int tegra30_mc_probe(struct tegra_mc *mc)
	return 0;
}

const struct tegra_mc_ops tegra30_mc_ops = {
	.probe = tegra30_mc_probe,
};
#endif

static const char *const status_names[32] = {
	[ 1] = "External interrupt",
	[ 6] = "EMEM address decode error",
	[ 7] = "GART page fault",
	[ 8] = "Security violation",
	[ 9] = "EMEM arbitration error",
	[10] = "Page fault",
	[11] = "Invalid APB ASID update",
	[12] = "VPR violation",
	[13] = "Secure carveout violation",
	[16] = "MTS carveout violation",
};

static const char *const error_names[8] = {
	[2] = "EMEM decode error",
	[3] = "TrustZone violation",
	[4] = "Carveout violation",
	[6] = "SMMU translation error",
};

static irqreturn_t tegra_mc_irq(int irq, void *data)
static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
{
	struct tegra_mc *mc = data;
	unsigned long status;
@@ -529,7 +504,7 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
		return IRQ_NONE;

	for_each_set_bit(bit, &status, 32) {
		const char *error = status_names[bit] ?: "unknown";
		const char *error = tegra_mc_status_names[bit] ?: "unknown";
		const char *client = "unknown", *desc;
		const char *direction, *secure;
		phys_addr_t addr = 0;
@@ -569,7 +544,7 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)

		type = (value & MC_ERR_STATUS_TYPE_MASK) >>
		       MC_ERR_STATUS_TYPE_SHIFT;
		desc = error_names[type];
		desc = tegra_mc_error_names[type];

		switch (value & MC_ERR_STATUS_TYPE_MASK) {
		case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
@@ -614,78 +589,31 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
	return IRQ_HANDLED;
}

static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
{
	struct tegra_mc *mc = data;
	unsigned long status;
	unsigned int bit;

	/* mask all interrupts to avoid flooding */
	status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
	if (!status)
		return IRQ_NONE;

	for_each_set_bit(bit, &status, 32) {
		const char *direction = "read", *secure = "";
		const char *error = status_names[bit];
		const char *client, *desc;
		phys_addr_t addr;
		u32 value, reg;
		u8 id, type;

		switch (BIT(bit)) {
		case MC_INT_DECERR_EMEM:
			reg = MC_DECERR_EMEM_OTHERS_STATUS;
			value = mc_readl(mc, reg);

			id = value & mc->soc->client_id_mask;
			desc = error_names[2];

			if (value & BIT(31))
				direction = "write";
			break;

		case MC_INT_INVALID_GART_PAGE:
			reg = MC_GART_ERROR_REQ;
			value = mc_readl(mc, reg);

			id = (value >> 1) & mc->soc->client_id_mask;
			desc = error_names[2];

			if (value & BIT(0))
				direction = "write";
			break;

		case MC_INT_SECURITY_VIOLATION:
			reg = MC_SECURITY_VIOLATION_STATUS;
			value = mc_readl(mc, reg);

			id = value & mc->soc->client_id_mask;
			type = (value & BIT(30)) ? 4 : 3;
			desc = error_names[type];
			secure = "secure ";

			if (value & BIT(31))
				direction = "write";
			break;

		default:
			continue;
		}

		client = mc->soc->clients[id].name;
		addr = mc_readl(mc, reg + sizeof(u32));

		dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
				    client, secure, direction, &addr, error,
				    desc);
	}
const struct tegra_mc_ops tegra30_mc_ops = {
	.probe = tegra30_mc_probe,
	.handle_irq = tegra30_mc_handle_irq,
};
#endif

	/* clear interrupts */
	mc_writel(mc, status, MC_INTSTATUS);
const char *const tegra_mc_status_names[32] = {
	[ 1] = "External interrupt",
	[ 6] = "EMEM address decode error",
	[ 7] = "GART page fault",
	[ 8] = "Security violation",
	[ 9] = "EMEM arbitration error",
	[10] = "Page fault",
	[11] = "Invalid APB ASID update",
	[12] = "VPR violation",
	[13] = "Secure carveout violation",
	[16] = "MTS carveout violation",
};

	return IRQ_HANDLED;
}
const char *const tegra_mc_error_names[8] = {
	[2] = "EMEM decode error",
	[3] = "TrustZone violation",
	[4] = "Carveout violation",
	[6] = "SMMU translation error",
};

/*
 * Memory Controller (MC) has few Memory Clients that are issuing memory
@@ -786,7 +714,6 @@ static int tegra_mc_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct tegra_mc *mc;
	void *isr;
	u64 mask;
	int err;

@@ -823,15 +750,6 @@ static int tegra_mc_probe(struct platform_device *pdev)
			return err;
	}

#ifdef CONFIG_ARCH_TEGRA_2x_SOC
	if (mc->soc == &tegra20_mc_soc) {
		isr = tegra20_mc_irq;
	} else
#endif
	{
		isr = tegra_mc_irq;
	}

	mc->irq = platform_get_irq(pdev, 0);
	if (mc->irq < 0)
		return mc->irq;
@@ -840,7 +758,7 @@ static int tegra_mc_probe(struct platform_device *pdev)

	mc_writel(mc, mc->soc->intmask, MC_INTMASK);

	err = devm_request_irq(&pdev->dev, mc->irq, isr, 0,
	err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0,
			       dev_name(&pdev->dev), mc);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
+3 −0
Original line number Diff line number Diff line
@@ -138,6 +138,9 @@ int tegra30_mc_probe(struct tegra_mc *mc);
extern const struct tegra_mc_ops tegra30_mc_ops;
#endif

extern const char * const tegra_mc_status_names[32];
extern const char * const tegra_mc_error_names[8];

/*
 * 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.
+74 −0
Original line number Diff line number Diff line
@@ -713,10 +713,84 @@ static int tegra20_mc_resume(struct tegra_mc *mc)
	return 0;
}

static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
{
	struct tegra_mc *mc = data;
	unsigned long status;
	unsigned int bit;

	/* mask all interrupts to avoid flooding */
	status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
	if (!status)
		return IRQ_NONE;

	for_each_set_bit(bit, &status, 32) {
		const char *error = tegra_mc_status_names[bit];
		const char *direction = "read", *secure = "";
		const char *client, *desc;
		phys_addr_t addr;
		u32 value, reg;
		u8 id, type;

		switch (BIT(bit)) {
		case MC_INT_DECERR_EMEM:
			reg = MC_DECERR_EMEM_OTHERS_STATUS;
			value = mc_readl(mc, reg);

			id = value & mc->soc->client_id_mask;
			desc = tegra_mc_error_names[2];

			if (value & BIT(31))
				direction = "write";
			break;

		case MC_INT_INVALID_GART_PAGE:
			reg = MC_GART_ERROR_REQ;
			value = mc_readl(mc, reg);

			id = (value >> 1) & mc->soc->client_id_mask;
			desc = tegra_mc_error_names[2];

			if (value & BIT(0))
				direction = "write";
			break;

		case MC_INT_SECURITY_VIOLATION:
			reg = MC_SECURITY_VIOLATION_STATUS;
			value = mc_readl(mc, reg);

			id = value & mc->soc->client_id_mask;
			type = (value & BIT(30)) ? 4 : 3;
			desc = tegra_mc_error_names[type];
			secure = "secure ";

			if (value & BIT(31))
				direction = "write";
			break;

		default:
			continue;
		}

		client = mc->soc->clients[id].name;
		addr = mc_readl(mc, reg + sizeof(u32));

		dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
				    client, secure, direction, &addr, error,
				    desc);
	}

	/* clear interrupts */
	mc_writel(mc, status, MC_INTSTATUS);

	return IRQ_HANDLED;
}

static const struct tegra_mc_ops tegra20_mc_ops = {
	.probe = tegra20_mc_probe,
	.suspend = tegra20_mc_suspend,
	.resume = tegra20_mc_resume,
	.handle_irq = tegra20_mc_handle_irq,
};

const struct tegra_mc_soc tegra20_mc_soc = {
+2 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/interconnect-provider.h>
#include <linux/irq.h>
#include <linux/reset-controller.h>
#include <linux/types.h>

@@ -177,6 +178,7 @@ struct tegra_mc_ops {
	int (*probe)(struct tegra_mc *mc);
	int (*suspend)(struct tegra_mc *mc);
	int (*resume)(struct tegra_mc *mc);
	irqreturn_t (*handle_irq)(int irq, void *data);
};

struct tegra_mc_soc {