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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct tegra_mc_ops tegra30_mc_ops = {
|
static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
|
||||||
.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)
|
|
||||||
{
|
{
|
||||||
struct tegra_mc *mc = data;
|
struct tegra_mc *mc = data;
|
||||||
unsigned long status;
|
unsigned long status;
|
||||||
@ -529,7 +504,7 @@ static irqreturn_t tegra_mc_irq(int irq, void *data)
|
|||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
for_each_set_bit(bit, &status, 32) {
|
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 *client = "unknown", *desc;
|
||||||
const char *direction, *secure;
|
const char *direction, *secure;
|
||||||
phys_addr_t addr = 0;
|
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) >>
|
type = (value & MC_ERR_STATUS_TYPE_MASK) >>
|
||||||
MC_ERR_STATUS_TYPE_SHIFT;
|
MC_ERR_STATUS_TYPE_SHIFT;
|
||||||
desc = error_names[type];
|
desc = tegra_mc_error_names[type];
|
||||||
|
|
||||||
switch (value & MC_ERR_STATUS_TYPE_MASK) {
|
switch (value & MC_ERR_STATUS_TYPE_MASK) {
|
||||||
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
|
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;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
|
const struct tegra_mc_ops tegra30_mc_ops = {
|
||||||
{
|
.probe = tegra30_mc_probe,
|
||||||
struct tegra_mc *mc = data;
|
.handle_irq = tegra30_mc_handle_irq,
|
||||||
unsigned long status;
|
};
|
||||||
unsigned int bit;
|
#endif
|
||||||
|
|
||||||
/* mask all interrupts to avoid flooding */
|
const char *const tegra_mc_status_names[32] = {
|
||||||
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
|
[ 1] = "External interrupt",
|
||||||
if (!status)
|
[ 6] = "EMEM address decode error",
|
||||||
return IRQ_NONE;
|
[ 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 *const tegra_mc_error_names[8] = {
|
||||||
const char *direction = "read", *secure = "";
|
[2] = "EMEM decode error",
|
||||||
const char *error = status_names[bit];
|
[3] = "TrustZone violation",
|
||||||
const char *client, *desc;
|
[4] = "Carveout violation",
|
||||||
phys_addr_t addr;
|
[6] = "SMMU translation error",
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Memory Controller (MC) has few Memory Clients that are issuing memory
|
* 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 resource *res;
|
||||||
struct tegra_mc *mc;
|
struct tegra_mc *mc;
|
||||||
void *isr;
|
|
||||||
u64 mask;
|
u64 mask;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -823,15 +750,6 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
|||||||
return err;
|
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);
|
mc->irq = platform_get_irq(pdev, 0);
|
||||||
if (mc->irq < 0)
|
if (mc->irq < 0)
|
||||||
return mc->irq;
|
return mc->irq;
|
||||||
@ -840,7 +758,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
|
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);
|
dev_name(&pdev->dev), mc);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
|
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;
|
extern const struct tegra_mc_ops tegra30_mc_ops;
|
||||||
#endif
|
#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
|
* 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.
|
* 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;
|
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 = {
|
static const struct tegra_mc_ops tegra20_mc_ops = {
|
||||||
.probe = tegra20_mc_probe,
|
.probe = tegra20_mc_probe,
|
||||||
.suspend = tegra20_mc_suspend,
|
.suspend = tegra20_mc_suspend,
|
||||||
.resume = tegra20_mc_resume,
|
.resume = tegra20_mc_resume,
|
||||||
|
.handle_irq = tegra20_mc_handle_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct tegra_mc_soc tegra20_mc_soc = {
|
const struct tegra_mc_soc tegra20_mc_soc = {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/interconnect-provider.h>
|
#include <linux/interconnect-provider.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/reset-controller.h>
|
#include <linux/reset-controller.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
@ -177,6 +178,7 @@ struct tegra_mc_ops {
|
|||||||
int (*probe)(struct tegra_mc *mc);
|
int (*probe)(struct tegra_mc *mc);
|
||||||
int (*suspend)(struct tegra_mc *mc);
|
int (*suspend)(struct tegra_mc *mc);
|
||||||
int (*resume)(struct tegra_mc *mc);
|
int (*resume)(struct tegra_mc *mc);
|
||||||
|
irqreturn_t (*handle_irq)(int irq, void *data);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tegra_mc_soc {
|
struct tegra_mc_soc {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user