diff options
Diffstat (limited to 'drivers/memory/tegra/mc.c')
| -rw-r--r-- | drivers/memory/tegra/mc.c | 135 |
1 files changed, 78 insertions, 57 deletions
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 6edb210287dc..d620660da331 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #include <linux/clk.h> @@ -56,6 +56,23 @@ static const struct of_device_id tegra_mc_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_mc_of_match); +const struct tegra_mc_regs tegra20_mc_regs = { + .cfg_channel_enable = 0xdf8, + .err_status = 0x08, + .err_add = 0x0c, + .err_add_hi = 0x11fc, + .err_vpr_status = 0x654, + .err_vpr_add = 0x658, + .err_sec_status = 0x67c, + .err_sec_add = 0x680, + .err_mts_status = 0x9b0, + .err_mts_add = 0x9b4, + .err_gen_co_status = 0xc00, + .err_gen_co_add = 0xc04, + .err_route_status = 0x9c0, + .err_route_add = 0x9c4, +}; + static void tegra_mc_devm_action_put_device(void *data) { struct tegra_mc *mc = data; @@ -381,12 +398,16 @@ unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) } EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count); +const irq_handler_t tegra30_mc_irq_handlers[] = { + tegra30_mc_handle_irq +}; + #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ defined(CONFIG_ARCH_TEGRA_114_SOC) || \ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ defined(CONFIG_ARCH_TEGRA_132_SOC) || \ defined(CONFIG_ARCH_TEGRA_210_SOC) -static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) +static void tegra_mc_setup_latency_allowance(struct tegra_mc *mc) { unsigned long long tick; unsigned int i; @@ -414,8 +435,6 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) /* latch new values */ mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); - - return 0; } static int load_one_timing(struct tegra_mc *mc, @@ -509,32 +528,24 @@ int tegra30_mc_probe(struct tegra_mc *mc) int err; mc->clk = devm_clk_get_optional(mc->dev, "mc"); - if (IS_ERR(mc->clk)) { - dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk)); - return PTR_ERR(mc->clk); - } + if (IS_ERR(mc->clk)) + return dev_err_probe(mc->dev, PTR_ERR(mc->clk), + "failed to get MC clock\n"); /* ensure that debug features are disabled */ mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); - err = tegra_mc_setup_latency_allowance(mc); - if (err < 0) { - dev_err(mc->dev, "failed to setup latency allowance: %d\n", err); - return err; - } + tegra_mc_setup_latency_allowance(mc); err = tegra_mc_setup_timings(mc); - if (err < 0) { - dev_err(mc->dev, "failed to setup timings: %d\n", err); - return err; - } + if (err < 0) + return dev_err_probe(mc->dev, err, "failed to setup timings\n"); return 0; } const struct tegra_mc_ops tegra30_mc_ops = { .probe = tegra30_mc_probe, - .handle_irq = tegra30_mc_handle_irq, }; #endif @@ -575,9 +586,9 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) } /* mask all interrupts to avoid flooding */ - status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask; + status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmasks[0].mask; } else { - status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask; } if (!status) @@ -600,37 +611,37 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) switch (intmask) { case MC_INT_DECERR_VPR: - status_reg = MC_ERR_VPR_STATUS; - addr_reg = MC_ERR_VPR_ADR; + status_reg = mc->soc->regs->err_vpr_status; + addr_reg = mc->soc->regs->err_vpr_add; break; case MC_INT_SECERR_SEC: - status_reg = MC_ERR_SEC_STATUS; - addr_reg = MC_ERR_SEC_ADR; + status_reg = mc->soc->regs->err_sec_status; + addr_reg = mc->soc->regs->err_sec_add; break; case MC_INT_DECERR_MTS: - status_reg = MC_ERR_MTS_STATUS; - addr_reg = MC_ERR_MTS_ADR; + status_reg = mc->soc->regs->err_mts_status; + addr_reg = mc->soc->regs->err_mts_add; break; case MC_INT_DECERR_GENERALIZED_CARVEOUT: - status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS; - addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR; + status_reg = mc->soc->regs->err_gen_co_status; + addr_reg = mc->soc->regs->err_gen_co_add; break; case MC_INT_DECERR_ROUTE_SANITY: - status_reg = MC_ERR_ROUTE_SANITY_STATUS; - addr_reg = MC_ERR_ROUTE_SANITY_ADR; + status_reg = mc->soc->regs->err_route_status; + addr_reg = mc->soc->regs->err_route_add; break; default: - status_reg = MC_ERR_STATUS; - addr_reg = MC_ERR_ADR; + status_reg = mc->soc->regs->err_status; + addr_reg = mc->soc->regs->err_add; #ifdef CONFIG_PHYS_ADDR_T_64BIT if (mc->soc->has_addr_hi_reg) - addr_hi_reg = MC_ERR_ADR_HI; + addr_hi_reg = mc->soc->regs->err_add_hi; #endif break; } @@ -647,9 +658,12 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) addr = mc_ch_readl(mc, channel, addr_hi_reg); else addr = mc_readl(mc, addr_hi_reg); - } else { + } else if (mc->soc->mc_addr_hi_mask) { addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & - MC_ERR_STATUS_ADR_HI_MASK); + mc->soc->mc_addr_hi_mask); + } else { + dev_err_ratelimited(mc->dev, "Unable to determine high address!"); + return IRQ_NONE; } addr <<= 32; } @@ -674,11 +688,11 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) } } - type = (value & MC_ERR_STATUS_TYPE_MASK) >> + type = (value & mc->soc->mc_err_status_type_mask) >> MC_ERR_STATUS_TYPE_SHIFT; - desc = tegra_mc_error_names[type]; + desc = tegra20_mc_error_names[type]; - switch (value & MC_ERR_STATUS_TYPE_MASK) { + switch (value & mc->soc->mc_err_status_type_mask) { case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: perm[0] = ' '; perm[1] = '['; @@ -744,9 +758,10 @@ const char *const tegra_mc_status_names[32] = { [16] = "MTS carveout violation", [17] = "Generalized carveout violation", [20] = "Route Sanity error", + [21] = "GIC_MSI error", }; -const char *const tegra_mc_error_names[8] = { +const char *const tegra20_mc_error_names[8] = { [2] = "EMEM decode error", [3] = "TrustZone violation", [4] = "Carveout violation", @@ -883,7 +898,7 @@ static void tegra_mc_num_channel_enabled(struct tegra_mc *mc) unsigned int i; u32 value; - value = mc_ch_readl(mc, 0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE); + value = mc_ch_readl(mc, 0, mc->soc->regs->cfg_channel_enable); if (value <= 0) { mc->num_channels = mc->soc->num_channels; return; @@ -935,25 +950,32 @@ static int tegra_mc_probe(struct platform_device *pdev) tegra_mc_num_channel_enabled(mc); - if (mc->soc->ops && mc->soc->ops->handle_irq) { - mc->irq = platform_get_irq(pdev, 0); - if (mc->irq < 0) - return mc->irq; + if (mc->soc->handle_irq) { + unsigned int i; WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); - if (mc->soc->num_channels) - mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask, - MC_INTMASK); - else - mc_writel(mc, mc->soc->intmask, MC_INTMASK); + for (i = 0; i < mc->soc->num_interrupts; i++) { + int irq; - 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, - err); - return err; + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + + err = devm_request_irq(&pdev->dev, irq, mc->soc->handle_irq[i], 0, + dev_name(&pdev->dev), mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", irq, err); + return err; + } + } + + for (i = 0; i < mc->soc->num_intmasks; i++) { + if (mc->soc->num_channels) + mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmasks[i].mask, + mc->soc->intmasks[i].reg); + else + mc_writel(mc, mc->soc->intmasks[i].mask, mc->soc->intmasks[i].reg); } } @@ -971,8 +993,7 @@ static int tegra_mc_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); if (IS_ERR(mc->smmu)) { - dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", - PTR_ERR(mc->smmu)); + dev_err(&pdev->dev, "failed to probe SMMU: %pe\n", mc->smmu); mc->smmu = NULL; } } |
