diff options
author | Thierry Reding <treding@nvidia.com> | 2021-06-02 19:32:57 +0300 |
---|---|---|
committer | Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> | 2021-06-03 22:49:41 +0300 |
commit | 1079a66bc32ff04eaab792152a9ed9c7585b5efc (patch) | |
tree | 82b09eafe619f0f9c2b8ba276ddfa449700d69da /drivers/memory/tegra/tegra20.c | |
parent | ddeceab0a959d199de776eaf5da977574b7c8f16 (diff) | |
download | linux-1079a66bc32ff04eaab792152a9ed9c7585b5efc.tar.xz |
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>
Diffstat (limited to 'drivers/memory/tegra/tegra20.c')
-rw-r--r-- | drivers/memory/tegra/tegra20.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index 2c86c0d70d59..fcd7738fcb53 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -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 = { |