Unverified Commit 6e6962ff authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'memory-controller-drv-tegra-5.19' of...

Merge tag 'memory-controller-drv-tegra-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.19 - Tegra SoC

Add support for Tegra234 memory controller and for logging memory
controller errors on Tegra186, Tegra194 and Tegra234.

* tag 'memory-controller-drv-tegra-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl:
  memory: tegra: Add MC error logging on Tegra186 onward
  memory: tegra: Add memory controller channels support
  memory: tegra: Add APE memory clients for Tegra234
  memory: tegra: Add Tegra234 support

Link: https://lore.kernel.org/r/20220509160807.154187-1-krzysztof.kozlowski@linaro.org


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 2b6866d7 54a85e09
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o
tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o

obj-$(CONFIG_TEGRA_MC) += tegra-mc.o

@@ -19,5 +20,6 @@ obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o

tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
+121 −16
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ static const struct of_device_id tegra_mc_of_match[] = {
#endif
#ifdef CONFIG_ARCH_TEGRA_194_SOC
	{ .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_234_SOC
	{ .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc },
#endif
	{ /* sentinel */ }
};
@@ -505,14 +508,54 @@ int tegra30_mc_probe(struct tegra_mc *mc)
	return 0;
}

static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
const struct tegra_mc_ops tegra30_mc_ops = {
	.probe = tegra30_mc_probe,
	.handle_irq = tegra30_mc_handle_irq,
};
#endif

static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status,
					  unsigned int *mc_channel)
{
	if ((status & mc->soc->ch_intmask) == 0)
		return -EINVAL;

	*mc_channel = __ffs((status & mc->soc->ch_intmask) >>
			    mc->soc->global_intstatus_channel_shift);

	return 0;
}

static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc,
					  unsigned int channel)
{
	return BIT(channel) << mc->soc->global_intstatus_channel_shift;
}

irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
{
	struct tegra_mc *mc = data;
	unsigned int bit, channel;
	unsigned long status;
	unsigned int bit;

	if (mc->soc->num_channels) {
		u32 global_status;
		int err;

		global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS);
		err = mc_global_intstatus_to_channel(mc, global_status, &channel);
		if (err < 0) {
			dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n",
					    global_status);
			return IRQ_NONE;
		}

		/* mask all interrupts to avoid flooding */
		status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask;
	} else {
		status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
	}

	if (!status)
		return IRQ_NONE;

@@ -520,18 +563,70 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
		const char *error = tegra_mc_status_names[bit] ?: "unknown";
		const char *client = "unknown", *desc;
		const char *direction, *secure;
		u32 status_reg, addr_reg;
		u32 intmask = BIT(bit);
		phys_addr_t addr = 0;
#ifdef CONFIG_PHYS_ADDR_T_64BIT
		u32 addr_hi_reg = 0;
#endif
		unsigned int i;
		char perm[7];
		u8 id, type;
		u32 value;

		value = mc_readl(mc, MC_ERR_STATUS);
		switch (intmask) {
		case MC_INT_DECERR_VPR:
			status_reg = MC_ERR_VPR_STATUS;
			addr_reg = MC_ERR_VPR_ADR;
			break;

		case MC_INT_SECERR_SEC:
			status_reg = MC_ERR_SEC_STATUS;
			addr_reg = MC_ERR_SEC_ADR;
			break;

		case MC_INT_DECERR_MTS:
			status_reg = MC_ERR_MTS_STATUS;
			addr_reg = MC_ERR_MTS_ADR;
			break;

		case MC_INT_DECERR_GENERALIZED_CARVEOUT:
			status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS;
			addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR;
			break;

		case MC_INT_DECERR_ROUTE_SANITY:
			status_reg = MC_ERR_ROUTE_SANITY_STATUS;
			addr_reg = MC_ERR_ROUTE_SANITY_ADR;
			break;

		default:
			status_reg = MC_ERR_STATUS;
			addr_reg = MC_ERR_ADR;

#ifdef CONFIG_PHYS_ADDR_T_64BIT
			if (mc->soc->has_addr_hi_reg)
				addr_hi_reg = MC_ERR_ADR_HI;
#endif
			break;
		}

		if (mc->soc->num_channels)
			value = mc_ch_readl(mc, channel, status_reg);
		else
			value = mc_readl(mc, status_reg);

#ifdef CONFIG_PHYS_ADDR_T_64BIT
		if (mc->soc->num_address_bits > 32) {
			if (addr_hi_reg) {
				if (mc->soc->num_channels)
					addr = mc_ch_readl(mc, channel, addr_hi_reg);
				else
					addr = mc_readl(mc, addr_hi_reg);
			} else {
				addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
					MC_ERR_STATUS_ADR_HI_MASK);
			}
			addr <<= 32;
		}
#endif
@@ -588,7 +683,10 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
			break;
		}

		value = mc_readl(mc, MC_ERR_ADR);
		if (mc->soc->num_channels)
			value = mc_ch_readl(mc, channel, addr_reg);
		else
			value = mc_readl(mc, addr_reg);
		addr |= value;

		dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
@@ -597,17 +695,18 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data)
	}

	/* clear interrupts */
	if (mc->soc->num_channels) {
		mc_ch_writel(mc, channel, status, MC_INTSTATUS);
		mc_ch_writel(mc, MC_BROADCAST_CHANNEL,
			     mc_channel_to_global_intstatus(mc, channel),
			     MC_GLOBAL_INTSTATUS);
	} else {
		mc_writel(mc, status, MC_INTSTATUS);
	}

	return IRQ_HANDLED;
}

const struct tegra_mc_ops tegra30_mc_ops = {
	.probe = tegra30_mc_probe,
	.handle_irq = tegra30_mc_handle_irq,
};
#endif

const char *const tegra_mc_status_names[32] = {
	[ 1] = "External interrupt",
	[ 6] = "EMEM address decode error",
@@ -619,6 +718,8 @@ const char *const tegra_mc_status_names[32] = {
	[12] = "VPR violation",
	[13] = "Secure carveout violation",
	[16] = "MTS carveout violation",
	[17] = "Generalized carveout violation",
	[20] = "Route Sanity error",
};

const char *const tegra_mc_error_names[8] = {
@@ -759,6 +860,10 @@ static int tegra_mc_probe(struct platform_device *pdev)

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

		err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0,
+48 −2
Original line number Diff line number Diff line
@@ -43,7 +43,21 @@
#define MC_EMEM_ARB_OVERRIDE				0xe8
#define MC_TIMING_CONTROL_DBG				0xf8
#define MC_TIMING_CONTROL				0xfc

#define MC_ERR_VPR_STATUS				0x654
#define MC_ERR_VPR_ADR					0x658
#define MC_ERR_SEC_STATUS				0x67c
#define MC_ERR_SEC_ADR					0x680
#define MC_ERR_MTS_STATUS				0x9b0
#define MC_ERR_MTS_ADR					0x9b4
#define MC_ERR_ROUTE_SANITY_STATUS			0x9c0
#define MC_ERR_ROUTE_SANITY_ADR				0x9c4
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS		0xc00
#define MC_ERR_GENERALIZED_CARVEOUT_ADR			0xc04
#define MC_GLOBAL_INTSTATUS				0xf24
#define MC_ERR_ADR_HI					0x11fc

#define MC_INT_DECERR_ROUTE_SANITY			BIT(20)
#define MC_INT_DECERR_GENERALIZED_CARVEOUT		BIT(17)
#define MC_INT_DECERR_MTS				BIT(16)
#define MC_INT_SECERR_SEC				BIT(13)
#define MC_INT_DECERR_VPR				BIT(12)
@@ -78,6 +92,8 @@

#define MC_TIMING_UPDATE				BIT(0)

#define MC_BROADCAST_CHANNEL				~0

static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
{
	val = val * percents;
@@ -92,6 +108,30 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)
	return container_of(provider, struct tegra_mc, provider);
}

static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch,
			      unsigned long offset)
{
	if (!mc->bcast_ch_regs)
		return 0;

	if (ch == MC_BROADCAST_CHANNEL)
		return readl_relaxed(mc->bcast_ch_regs + offset);

	return readl_relaxed(mc->ch_regs[ch] + offset);
}

static inline void mc_ch_writel(const struct tegra_mc *mc, int ch,
				u32 value, unsigned long offset)
{
	if (!mc->bcast_ch_regs)
		return;

	if (ch == MC_BROADCAST_CHANNEL)
		writel_relaxed(value, mc->bcast_ch_regs + offset);
	else
		writel_relaxed(value, mc->ch_regs[ch] + offset);
}

static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset)
{
	return readl_relaxed(mc->regs + offset);
@@ -137,6 +177,10 @@ extern const struct tegra_mc_soc tegra186_mc_soc;
extern const struct tegra_mc_soc tegra194_mc_soc;
#endif

#ifdef CONFIG_ARCH_TEGRA_234_SOC
extern const struct tegra_mc_soc tegra234_mc_soc;
#endif

#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
    defined(CONFIG_ARCH_TEGRA_114_SOC) || \
    defined(CONFIG_ARCH_TEGRA_124_SOC) || \
@@ -147,10 +191,12 @@ extern const struct tegra_mc_ops tegra30_mc_ops;
#endif

#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
    defined(CONFIG_ARCH_TEGRA_194_SOC)
    defined(CONFIG_ARCH_TEGRA_194_SOC) || \
    defined(CONFIG_ARCH_TEGRA_234_SOC)
extern const struct tegra_mc_ops tegra186_mc_ops;
#endif

irqreturn_t tegra30_mc_handle_irq(int irq, void *data);
extern const char * const tegra_mc_status_names[32];
extern const char * const tegra_mc_error_names[8];

+3 −0
Original line number Diff line number Diff line
@@ -272,6 +272,9 @@ static const struct of_device_id tegra186_emc_of_match[] = {
#endif
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
	{ .compatible = "nvidia,tegra194-emc" },
#endif
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
	{ .compatible = "nvidia,tegra234-emc" },
#endif
	{ /* sentinel */ }
};
+39 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include <dt-bindings/memory/tegra186-mc.h>
#endif

#include "mc.h"

#define MC_SID_STREAMID_OVERRIDE_MASK GENMASK(7, 0)
#define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16)
#define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8)
@@ -48,8 +50,37 @@ static void tegra186_mc_program_sid(struct tegra_mc *mc)

static int tegra186_mc_probe(struct tegra_mc *mc)
{
	struct platform_device *pdev = to_platform_device(mc->dev);
	unsigned int i;
	char name[8];
	int err;

	mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast");
	if (IS_ERR(mc->bcast_ch_regs)) {
		if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) {
			dev_warn(&pdev->dev,
				 "Broadcast channel is missing, please update your device-tree\n");
			mc->bcast_ch_regs = NULL;
			goto populate;
		}

		return PTR_ERR(mc->bcast_ch_regs);
	}

	mc->ch_regs = devm_kcalloc(mc->dev, mc->soc->num_channels, sizeof(*mc->ch_regs),
				   GFP_KERNEL);
	if (!mc->ch_regs)
		return -ENOMEM;

	for (i = 0; i < mc->soc->num_channels; i++) {
		snprintf(name, sizeof(name), "ch%u", i);

		mc->ch_regs[i] = devm_platform_ioremap_resource_byname(pdev, name);
		if (IS_ERR(mc->ch_regs[i]))
			return PTR_ERR(mc->ch_regs[i]);
	}

populate:
	err = of_platform_populate(mc->dev->of_node, NULL, NULL, mc->dev);
	if (err < 0)
		return err;
@@ -144,6 +175,7 @@ const struct tegra_mc_ops tegra186_mc_ops = {
	.remove = tegra186_mc_remove,
	.resume = tegra186_mc_resume,
	.probe_device = tegra186_mc_probe_device,
	.handle_irq = tegra30_mc_handle_irq,
};

#if defined(CONFIG_ARCH_TEGRA_186_SOC)
@@ -875,6 +907,13 @@ const struct tegra_mc_soc tegra186_mc_soc = {
	.num_clients = ARRAY_SIZE(tegra186_mc_clients),
	.clients = tegra186_mc_clients,
	.num_address_bits = 40,
	.num_channels = 4,
	.client_id_mask = 0xff,
	.intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
		   MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
		   MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
	.ops = &tegra186_mc_ops,
	.ch_intmask = 0x0000000f,
	.global_intstatus_channel_shift = 0,
};
#endif
Loading