Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8d502fd authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding
Browse files

memory: tegra: Squash tegra20-mc into common tegra-mc driver



Tegra30+ has some minor differences in registers / bits layout compared
to Tegra20. Let's squash Tegra20 driver into the common tegra-mc driver
in a preparation for the upcoming MC hot reset controls implementation,
avoiding code duplication.

Note that this currently doesn't report the value of MC_GART_ERROR_REQ
because it is located within the GART register area and cannot be safely
accessed from the MC driver (this happens to work only by accident). The
proper solution is to integrate the GART driver with the MC driver, much
like is done for the Tegra SMMU, but that is an invasive change and will
be part of a separate patch series.

Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 85dce891
Loading
Loading
Loading
Loading
+0 −10
Original line number Diff line number Diff line
@@ -104,16 +104,6 @@ config MVEBU_DEVBUS
	  Armada 370 and Armada XP. This controller allows to handle flash
	  devices such as NOR, NAND, SRAM, and FPGA.

config TEGRA20_MC
	bool "Tegra20 Memory Controller(MC) driver"
	default y
	depends on ARCH_TEGRA_2x_SOC
	help
	  This driver is for the Memory Controller(MC) module available
	  in Tegra20 SoCs, mainly for a address translation fault
	  analysis, especially for IOMMU/GART(Graphics Address
	  Relocation Table) module.

config FSL_CORENET_CF
	tristate "Freescale CoreNet Error Reporting"
	depends on FSL_SOC_BOOKE
+0 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@ obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
obj-$(CONFIG_FSL_CORENET_CF)	+= fsl-corenet-cf.o
obj-$(CONFIG_FSL_IFC)		+= fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS)	+= mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC)	+= tegra20-mc.o
obj-$(CONFIG_JZ4780_NEMC)	+= jz4780-nemc.o
obj-$(CONFIG_MTK_SMI)		+= mtk-smi.o
obj-$(CONFIG_DA8XX_DDRCTL)	+= da8xx-ddrctl.o
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
tegra-mc-y := mc.o

tegra-mc-$(CONFIG_ARCH_TEGRA_2x_SOC)  += tegra20.o
tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC)  += tegra30.o
tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
+100 −12
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@

#define MC_ERR_ADR 0x0c

#define MC_DECERR_EMEM_OTHERS_STATUS	0x58
#define MC_SECURITY_VIOLATION_STATUS	0x74

#define MC_EMEM_ARB_CFG 0x90
#define  MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x)	(((x) & 0x1ff) << 0)
#define  MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK	0x1ff
@@ -46,6 +49,9 @@
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)

static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
	{ .compatible = "nvidia,tegra20-mc", .data = &tegra20_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
	{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
#endif
@@ -221,6 +227,7 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
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",
@@ -334,11 +341,78 @@ 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:
			dev_err_ratelimited(mc->dev, "%s\n", error);
			continue;

		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;
}

static int tegra_mc_probe(struct platform_device *pdev)
{
	const struct of_device_id *match;
	struct resource *res;
	struct tegra_mc *mc;
	void *isr;
	int err;

	match = of_match_node(tegra_mc_of_match, pdev->dev.of_node);
@@ -361,6 +435,17 @@ static int tegra_mc_probe(struct platform_device *pdev)
	if (IS_ERR(mc->regs))
		return PTR_ERR(mc->regs);

#ifdef CONFIG_ARCH_TEGRA_2x_SOC
	if (mc->soc == &tegra20_mc_soc) {
		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
		mc->regs2 = devm_ioremap_resource(&pdev->dev, res);
		if (IS_ERR(mc->regs2))
			return PTR_ERR(mc->regs2);

		isr = tegra20_mc_irq;
	} else
#endif
	{
		mc->clk = devm_clk_get(&pdev->dev, "mc");
		if (IS_ERR(mc->clk)) {
			dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
@@ -375,6 +460,9 @@ static int tegra_mc_probe(struct platform_device *pdev)
			return err;
		}

		isr = tegra_mc_irq;
	}

	err = tegra_mc_setup_timings(mc);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to setup timings: %d\n", err);
@@ -400,7 +488,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, tegra_mc_irq, IRQF_SHARED,
	err = devm_request_irq(&pdev->dev, mc->irq, isr, IRQF_SHARED,
			       dev_name(&pdev->dev), mc);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
+11 −0
Original line number Diff line number Diff line
@@ -21,19 +21,30 @@
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
#define MC_INT_ARBITRATION_EMEM (1 << 9)
#define MC_INT_SECURITY_VIOLATION (1 << 8)
#define MC_INT_INVALID_GART_PAGE (1 << 7)
#define MC_INT_DECERR_EMEM (1 << 6)

static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
{
	if (mc->regs2 && offset >= 0x24)
		return readl(mc->regs2 + offset - 0x3c);

	return readl(mc->regs + offset);
}

static inline void mc_writel(struct tegra_mc *mc, u32 value,
			     unsigned long offset)
{
	if (mc->regs2 && offset >= 0x24)
		return writel(value, mc->regs2 + offset - 0x3c);

	writel(value, mc->regs + offset);
}

#ifdef CONFIG_ARCH_TEGRA_2x_SOC
extern const struct tegra_mc_soc tegra20_mc_soc;
#endif

#ifdef CONFIG_ARCH_TEGRA_3x_SOC
extern const struct tegra_mc_soc tegra30_mc_soc;
#endif
Loading