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: Thierry Reding <treding@nvidia.com> Link: https://lore.kernel.org/r/20210602163302.120041-8-thierry.reding@gmail.com Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
This commit is contained in:
parent
ddeceab0a9
commit
1079a66bc3
|
@ -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;
|
||||
const struct tegra_mc_ops tegra30_mc_ops = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.handle_irq = tegra30_mc_handle_irq,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* mask all interrupts to avoid flooding */
|
||||
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
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",
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* clear interrupts */
|
||||
mc_writel(mc, status, MC_INTSTATUS);
|
||||
|
||||
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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue