diff mbox

[3/4] ARM: tegra30: Add Tegra Memory Controller(MC) driver

Message ID 1336635764-30597-3-git-send-email-hdoyu@nvidia.com
State Not Applicable, archived
Headers show

Commit Message

Hiroshi Doyu May 10, 2012, 7:42 a.m. UTC
Tegra Memory Controller(MC) driver for Tegra30
Added to support MC General interrupts, mainly for IOMMU(SMMU).

Signed-off-by: Hiroshi DOYU <hdoyu@nvidia.com>
---
 .../bindings/arm/tegra/nvidia,tegra30-mc.txt       |   18 +
 arch/arm/mach-tegra/Kconfig                        |    2 +
 drivers/memory/Kconfig                             |    4 +
 drivers/memory/Makefile                            |    1 +
 drivers/memory/tegra30-mc.c                        |  391 ++++++++++++++++++++
 5 files changed, 416 insertions(+), 0 deletions(-)

Comments

gregkh@linuxfoundation.org May 10, 2012, 9:46 p.m. UTC | #1
On Thu, May 10, 2012 at 10:42:32AM +0300, Hiroshi DOYU wrote:
> Tegra Memory Controller(MC) driver for Tegra30
> Added to support MC General interrupts, mainly for IOMMU(SMMU).

I'll apply this, but:

> +static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
> +{
> +	if (offs < 0x10)
> +		return readl(mc->regs[0] + offs);
> +	BUG_ON(offs < 0x3c);
> +	if (offs < 0x1f0)
> +		return readl(mc->regs[1] + offs - 0x3c);
> +	BUG_ON(offs < 0x200);
> +	if (offs < 0x228)
> +		return readl(mc->regs[2] + offs - 0x200);
> +	BUG_ON(offs < 0x284);
> +	if (offs < 0x400)
> +		return readl(mc->regs[3] + offs - 0x284);
> +	BUG();
> +}

That's a lot of BUG* calls.  Same thing with the 1/4 patch in this
series.

We really should not have a BUG call in any driver, as you just stopped
the whole system.  I can understand this being there for debugging when
you create the code originally, but as these are things no one should
ever be able to hit now, you should remove them, right?

Care to write a follow-on patch removing all of these BUG_ON and BUG
calls in this and the drivers/memory/tegra20-mc.c driver?

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hiroshi Doyu May 11, 2012, 6:16 a.m. UTC | #2
Hi Greg,

From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: Re: [PATCH 3/4] ARM: tegra30: Add Tegra Memory Controller(MC) driver
Date: Thu, 10 May 2012 23:46:04 +0200
Message-ID: <20120510214604.GA3525@kroah.com>

> On Thu, May 10, 2012 at 10:42:32AM +0300, Hiroshi DOYU wrote:
> > Tegra Memory Controller(MC) driver for Tegra30
> > Added to support MC General interrupts, mainly for IOMMU(SMMU).
> 
> I'll apply this, but:

Thanks.

> > +static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
> > +{
> > +	if (offs < 0x10)
> > +		return readl(mc->regs[0] + offs);
> > +	BUG_ON(offs < 0x3c);
> > +	if (offs < 0x1f0)
> > +		return readl(mc->regs[1] + offs - 0x3c);
> > +	BUG_ON(offs < 0x200);
> > +	if (offs < 0x228)
> > +		return readl(mc->regs[2] + offs - 0x200);
> > +	BUG_ON(offs < 0x284);
> > +	if (offs < 0x400)
> > +		return readl(mc->regs[3] + offs - 0x284);
> > +	BUG();
> > +}
> 
> That's a lot of BUG* calls.  Same thing with the 1/4 patch in this
> series.
> 
> We really should not have a BUG call in any driver, as you just stopped
> the whole system.  I can understand this being there for debugging when
> you create the code originally, but as these are things no one should
> ever be able to hit now, you should remove them, right?
> 
> Care to write a follow-on patch removing all of these BUG_ON and BUG
> calls in this and the drivers/memory/tegra20-mc.c driver?

Ok, they are replying to this email.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt
new file mode 100644
index 0000000..e47e73f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt
@@ -0,0 +1,18 @@ 
+NVIDIA Tegra30 MC(Memory Controller)
+
+Required properties:
+- compatible : "nvidia,tegra30-mc"
+- reg : Should contain 4 register ranges(address and length); see the
+  example below. Note that the MC registers are interleaved with the
+  SMMU registers, and hence must be represented as multiple ranges.
+- interrupts : Should contain MC General interrupt.
+
+Example:
+	mc {
+		compatible = "nvidia,tegra30-mc";
+		reg = <0x7000f000 0x010
+		       0x7000f03c 0x1b4
+		       0x7000f200 0x028
+		       0x7000f284 0x17c>;
+		interrupts = <0 77 0x04>;
+	};
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index abaf5d0..16fe065 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -43,6 +43,8 @@  config ARCH_TEGRA_3x_SOC
 	select ARM_ERRATA_764369
 	select PL310_ERRATA_769419 if CACHE_L2X0
 	select CPU_FREQ_TABLE if CPU_FREQ
+	select MEMORY
+	select TEGRA30_MC
 	help
 	  Support for NVIDIA Tegra T30 processor family, based on the
 	  ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index ebade16..42e6d66 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -24,4 +24,8 @@  config TEGRA20_MC
 	bool
 	depends on ARCH_TEGRA_2x_SOC
 
+config TEGRA30_MC
+	bool
+	depends on ARCH_TEGRA_3x_SOC
+
 endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 1f58518..42b3ce9 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -4,3 +4,4 @@ 
 
 obj-$(CONFIG_TI_EMIF)		+= emif.o
 obj-$(CONFIG_TEGRA20_MC)	+= tegra20-mc.o
+obj-$(CONFIG_TEGRA30_MC)	+= tegra30-mc.o
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c
new file mode 100644
index 0000000..c982125
--- /dev/null
+++ b/drivers/memory/tegra30-mc.c
@@ -0,0 +1,391 @@ 
+/*
+ * Tegra30 Memory Controller
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define DRV_NAME "tegra30-mc"
+
+#define MC_INTSTATUS			0x0
+#define MC_INTMASK			0x4
+
+#define MC_INT_ERR_SHIFT		6
+#define MC_INT_ERR_MASK			(0x1f << MC_INT_ERR_SHIFT)
+#define MC_INT_DECERR_EMEM		BIT(MC_INT_ERR_SHIFT)
+#define MC_INT_SECURITY_VIOLATION	BIT(MC_INT_ERR_SHIFT + 2)
+#define MC_INT_ARBITRATION_EMEM		BIT(MC_INT_ERR_SHIFT + 3)
+#define MC_INT_INVALID_SMMU_PAGE	BIT(MC_INT_ERR_SHIFT + 4)
+
+#define MC_ERR_STATUS			0x8
+#define MC_ERR_ADR			0xc
+
+#define MC_ERR_TYPE_SHIFT		28
+#define MC_ERR_TYPE_MASK		(7 << MC_ERR_TYPE_SHIFT)
+#define MC_ERR_TYPE_DECERR_EMEM		2
+#define MC_ERR_TYPE_SECURITY_TRUSTZONE	3
+#define MC_ERR_TYPE_SECURITY_CARVEOUT	4
+#define MC_ERR_TYPE_INVALID_SMMU_PAGE	6
+
+#define MC_ERR_INVALID_SMMU_PAGE_SHIFT	25
+#define MC_ERR_INVALID_SMMU_PAGE_MASK	(7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT)
+#define MC_ERR_RW_SHIFT			16
+#define MC_ERR_RW			BIT(MC_ERR_RW_SHIFT)
+#define MC_ERR_SECURITY			BIT(MC_ERR_RW_SHIFT + 1)
+
+#define SECURITY_VIOLATION_TYPE		BIT(30)	/* 0=TRUSTZONE, 1=CARVEOUT */
+
+#define MC_EMEM_ARB_CFG			0x90
+#define MC_EMEM_ARB_OUTSTANDING_REQ	0x94
+#define MC_EMEM_ARB_TIMING_RCD		0x98
+#define MC_EMEM_ARB_TIMING_RP		0x9c
+#define MC_EMEM_ARB_TIMING_RC		0xa0
+#define MC_EMEM_ARB_TIMING_RAS		0xa4
+#define MC_EMEM_ARB_TIMING_FAW		0xa8
+#define MC_EMEM_ARB_TIMING_RRD		0xac
+#define MC_EMEM_ARB_TIMING_RAP2PRE	0xb0
+#define MC_EMEM_ARB_TIMING_WAP2PRE	0xb4
+#define MC_EMEM_ARB_TIMING_R2R		0xb8
+#define MC_EMEM_ARB_TIMING_W2W		0xbc
+#define MC_EMEM_ARB_TIMING_R2W		0xc0
+#define MC_EMEM_ARB_TIMING_W2R		0xc4
+
+#define MC_EMEM_ARB_DA_TURNS		0xd0
+#define MC_EMEM_ARB_DA_COVERS		0xd4
+#define MC_EMEM_ARB_MISC0		0xd8
+#define MC_EMEM_ARB_MISC1		0xdc
+
+#define MC_EMEM_ARB_RING3_THROTTLE	0xe4
+#define MC_EMEM_ARB_OVERRIDE		0xe8
+
+#define MC_TIMING_CONTROL		0xfc
+
+#define MC_CLIENT_ID_MASK		0x7f
+
+#define NUM_MC_REG_BANKS		4
+
+struct tegra30_mc {
+	void __iomem *regs[NUM_MC_REG_BANKS];
+	struct device *dev;
+	u32 ctx[0];
+};
+
+static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
+{
+	if (offs < 0x10)
+		return readl(mc->regs[0] + offs);
+	BUG_ON(offs < 0x3c);
+	if (offs < 0x1f0)
+		return readl(mc->regs[1] + offs - 0x3c);
+	BUG_ON(offs < 0x200);
+	if (offs < 0x228)
+		return readl(mc->regs[2] + offs - 0x200);
+	BUG_ON(offs < 0x284);
+	if (offs < 0x400)
+		return readl(mc->regs[3] + offs - 0x284);
+	BUG();
+}
+
+static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
+{
+	if (offs < 0x10) {
+		writel(val, mc->regs[0] + offs);
+		return;
+	}
+	BUG_ON(offs < 0x3c);
+	if (offs < 0x1f0) {
+		writel(val, mc->regs[1] + offs - 0x3c);
+		return;
+	}
+	BUG_ON(offs < 0x200);
+	if (offs < 0x228) {
+		writel(val, mc->regs[2] + offs - 0x200);
+		return;
+	}
+	BUG_ON(offs < 0x284);
+	if (offs < 0x400) {
+		writel(val, mc->regs[3] + offs - 0x284);
+		return;
+	}
+	BUG();
+}
+
+static const char * const tegra30_mc_client[] = {
+	"csr_ptcr",
+	"cbr_display0a",
+	"cbr_display0ab",
+	"cbr_display0b",
+	"cbr_display0bb",
+	"cbr_display0c",
+	"cbr_display0cb",
+	"cbr_display1b",
+	"cbr_display1bb",
+	"cbr_eppup",
+	"cbr_g2pr",
+	"cbr_g2sr",
+	"cbr_mpeunifbr",
+	"cbr_viruv",
+	"csr_afir",
+	"csr_avpcarm7r",
+	"csr_displayhc",
+	"csr_displayhcb",
+	"csr_fdcdrd",
+	"csr_fdcdrd2",
+	"csr_g2dr",
+	"csr_hdar",
+	"csr_host1xdmar",
+	"csr_host1xr",
+	"csr_idxsrd",
+	"csr_idxsrd2",
+	"csr_mpe_ipred",
+	"csr_mpeamemrd",
+	"csr_mpecsrd",
+	"csr_ppcsahbdmar",
+	"csr_ppcsahbslvr",
+	"csr_satar",
+	"csr_texsrd",
+	"csr_texsrd2",
+	"csr_vdebsevr",
+	"csr_vdember",
+	"csr_vdemcer",
+	"csr_vdetper",
+	"csr_mpcorelpr",
+	"csr_mpcorer",
+	"cbw_eppu",
+	"cbw_eppv",
+	"cbw_eppy",
+	"cbw_mpeunifbw",
+	"cbw_viwsb",
+	"cbw_viwu",
+	"cbw_viwv",
+	"cbw_viwy",
+	"ccw_g2dw",
+	"csw_afiw",
+	"csw_avpcarm7w",
+	"csw_fdcdwr",
+	"csw_fdcdwr2",
+	"csw_hdaw",
+	"csw_host1xw",
+	"csw_ispw",
+	"csw_mpcorelpw",
+	"csw_mpcorew",
+	"csw_mpecswr",
+	"csw_ppcsahbdmaw",
+	"csw_ppcsahbslvw",
+	"csw_sataw",
+	"csw_vdebsevw",
+	"csw_vdedbgw",
+	"csw_vdembew",
+	"csw_vdetpmw",
+};
+
+static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
+{
+	u32 err, addr;
+	const char * const mc_int_err[] = {
+		"MC_DECERR",
+		"Unknown",
+		"MC_SECURITY_ERR",
+		"MC_ARBITRATION_EMEM",
+		"MC_SMMU_ERR",
+	};
+	const char * const err_type[] = {
+		"Unknown",
+		"Unknown",
+		"DECERR_EMEM",
+		"SECURITY_TRUSTZONE",
+		"SECURITY_CARVEOUT",
+		"Unknown",
+		"INVALID_SMMU_PAGE",
+		"Unknown",
+	};
+	char attr[6];
+	int cid, perm, type, idx;
+	const char *client = "Unknown";
+
+	idx = n - MC_INT_ERR_SHIFT;
+	if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) {
+		pr_err_ratelimited("Unknown interrupt status %08lx\n", BIT(n));
+		return;
+	}
+
+	err = readl(mc + MC_ERR_STATUS);
+
+	type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
+	perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
+		MC_ERR_INVALID_SMMU_PAGE_SHIFT;
+	if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE)
+		sprintf(attr, "%c-%c-%c",
+			(perm & BIT(2)) ? 'R' : '-',
+			(perm & BIT(1)) ? 'W' : '-',
+			(perm & BIT(0)) ? 'S' : '-');
+	else
+		attr[0] = '\0';
+
+	cid = err & MC_CLIENT_ID_MASK;
+	if (cid < ARRAY_SIZE(tegra30_mc_client))
+		client = tegra30_mc_client[cid];
+
+	addr = readl(mc + MC_ERR_ADR);
+
+	pr_err_ratelimited("%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
+			   mc_int_err[idx], err, addr, client,
+			   (err & MC_ERR_SECURITY) ? "secure" : "non-secure",
+			   (err & MC_ERR_RW) ? "write" : "read",
+			   err_type[type], attr);
+}
+
+static const u32 tegra30_mc_ctx[] = {
+	MC_EMEM_ARB_CFG,
+	MC_EMEM_ARB_OUTSTANDING_REQ,
+	MC_EMEM_ARB_TIMING_RCD,
+	MC_EMEM_ARB_TIMING_RP,
+	MC_EMEM_ARB_TIMING_RC,
+	MC_EMEM_ARB_TIMING_RAS,
+	MC_EMEM_ARB_TIMING_FAW,
+	MC_EMEM_ARB_TIMING_RRD,
+	MC_EMEM_ARB_TIMING_RAP2PRE,
+	MC_EMEM_ARB_TIMING_WAP2PRE,
+	MC_EMEM_ARB_TIMING_R2R,
+	MC_EMEM_ARB_TIMING_W2W,
+	MC_EMEM_ARB_TIMING_R2W,
+	MC_EMEM_ARB_TIMING_W2R,
+	MC_EMEM_ARB_DA_TURNS,
+	MC_EMEM_ARB_DA_COVERS,
+	MC_EMEM_ARB_MISC0,
+	MC_EMEM_ARB_MISC1,
+	MC_EMEM_ARB_RING3_THROTTLE,
+	MC_EMEM_ARB_OVERRIDE,
+	MC_INTMASK,
+};
+
+static int tegra30_mc_suspend(struct device *dev)
+{
+	int i;
+	struct tegra30_mc *mc = dev_get_drvdata(dev);
+
+	for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
+		mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]);
+	return 0;
+}
+
+static int tegra30_mc_resume(struct device *dev)
+{
+	int i;
+	struct tegra30_mc *mc = dev_get_drvdata(dev);
+
+	for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
+		mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]);
+
+	mc_writel(mc, 1, MC_TIMING_CONTROL);
+	/* Read-back to ensure that write reached */
+	mc_readl(mc, MC_TIMING_CONTROL);
+	return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
+			    tegra30_mc_suspend,
+			    tegra30_mc_resume, NULL);
+
+static const struct of_device_id tegra30_mc_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra30-mc", },
+	{},
+};
+
+static irqreturn_t tegra30_mc_isr(int irq, void *data)
+{
+	u32 stat, mask, bit;
+	struct tegra30_mc *mc = data;
+
+	stat = mc_readl(mc, MC_INTSTATUS);
+	mask = mc_readl(mc, MC_INTMASK);
+	mask &= stat;
+	if (!mask)
+		return IRQ_NONE;
+	while ((bit = ffs(mask)) != 0)
+		tegra30_mc_decode(mc, bit - 1);
+	mc_writel(mc, stat, MC_INTSTATUS);
+	return IRQ_HANDLED;
+}
+
+static int __devinit tegra30_mc_probe(struct platform_device *pdev)
+{
+	struct resource *irq;
+	struct tegra30_mc *mc;
+	size_t bytes;
+	int err, i;
+	u32 intmask;
+
+	bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx);
+	mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
+	if (!mc)
+		return -ENOMEM;
+	mc->dev = &pdev->dev;
+
+	for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
+		struct resource *res;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		if (!res)
+			return -ENODEV;
+		mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
+		if (!mc->regs[i])
+			return -EBUSY;
+	}
+
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq)
+		return -ENODEV;
+	err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr,
+			       IRQF_SHARED, dev_name(&pdev->dev), mc);
+	if (err)
+		return -ENODEV;
+
+	platform_set_drvdata(pdev, mc);
+
+	intmask = MC_INT_INVALID_SMMU_PAGE |
+		MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
+	mc_writel(mc, intmask, MC_INTMASK);
+	return 0;
+}
+
+static int __devexit tegra30_mc_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver tegra30_mc_driver = {
+	.probe = tegra30_mc_probe,
+	.remove = __devexit_p(tegra30_mc_remove),
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra30_mc_of_match,
+		.pm = &tegra30_mc_pm,
+	},
+};
+module_platform_driver(tegra30_mc_driver);
+
+MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 MC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);