aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHiroshi DOYU <hdoyu@nvidia.com>2012-05-10 03:42:30 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-10 17:43:53 -0400
commitc542fb79fba4c63aa6e2a27f90373b0516614eca (patch)
tree19847b29ef6202717dfc8132a575b04214efdbd0
parent649e6ee33f73ba1c4f2492c6de9aff2254b540cb (diff)
ARM: tegra20: Add Tegra Memory Controller(MC) driver
Tegra Memory Controller(MC) driver for Tegra20 Added to support MC General interrupts, mainly for IOMMU(GART). Signed-off-by: Hiroshi DOYU <hdoyu@nvidia.com> Acked-by: Stephen Warren <swarren@wwwdotorg.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt16
-rw-r--r--arch/arm/mach-tegra/Kconfig2
-rw-r--r--drivers/memory/Kconfig4
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/tegra20-mc.c262
5 files changed, 285 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt
new file mode 100644
index 000000000000..c25a0a55151d
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt
@@ -0,0 +1,16 @@
1NVIDIA Tegra20 MC(Memory Controller)
2
3Required properties:
4- compatible : "nvidia,tegra20-mc"
5- reg : Should contain 2 register ranges(address and length); see the
6 example below. Note that the MC registers are interleaved with the
7 GART registers, and hence must be represented as multiple ranges.
8- interrupts : Should contain MC General interrupt.
9
10Example:
11 mc {
12 compatible = "nvidia,tegra20-mc";
13 reg = <0x7000f000 0x024
14 0x7000f03c 0x3c4>;
15 interrupts = <0 77 0x04>;
16 };
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index d0f2546706ca..0e4e502ad448 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -20,6 +20,8 @@ config ARCH_TEGRA_2x_SOC
20 select PL310_ERRATA_727915 if CACHE_L2X0 20 select PL310_ERRATA_727915 if CACHE_L2X0
21 select PL310_ERRATA_769419 if CACHE_L2X0 21 select PL310_ERRATA_769419 if CACHE_L2X0
22 select CPU_FREQ_TABLE if CPU_FREQ 22 select CPU_FREQ_TABLE if CPU_FREQ
23 select MEMORY
24 select TEGRA20_MC
23 help 25 help
24 Support for NVIDIA Tegra AP20 and T20 processors, based on the 26 Support for NVIDIA Tegra AP20 and T20 processors, based on the
25 ARM CortexA9MP CPU and the ARM PL310 L2 cache controller 27 ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index e0b3156d65b5..ebade1647f9e 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -20,4 +20,8 @@ config TI_EMIF
20 parameters and other settings during frequency, voltage and 20 parameters and other settings during frequency, voltage and
21 temperature changes 21 temperature changes
22 22
23config TEGRA20_MC
24 bool
25 depends on ARCH_TEGRA_2x_SOC
26
23endif 27endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index e27f80b28859..1f585184d88b 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -3,3 +3,4 @@
3# 3#
4 4
5obj-$(CONFIG_TI_EMIF) += emif.o 5obj-$(CONFIG_TI_EMIF) += emif.o
6obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c
new file mode 100644
index 000000000000..c0bfffacdc64
--- /dev/null
+++ b/drivers/memory/tegra20-mc.c
@@ -0,0 +1,262 @@
1/*
2 * Tegra20 Memory Controller
3 *
4 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/ratelimit.h>
23#include <linux/platform_device.h>
24#include <linux/interrupt.h>
25#include <linux/io.h>
26
27#define DRV_NAME "tegra20-mc"
28
29#define MC_INTSTATUS 0x0
30#define MC_INTMASK 0x4
31
32#define MC_INT_ERR_SHIFT 6
33#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
34#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
35#define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1)
36#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
37#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
38
39#define MC_GART_ERROR_REQ 0x30
40#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
41#define MC_SECURITY_VIOLATION_STATUS 0x74
42
43#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
44
45#define MC_CLIENT_ID_MASK 0x3f
46
47#define NUM_MC_REG_BANKS 2
48
49struct tegra20_mc {
50 void __iomem *regs[NUM_MC_REG_BANKS];
51 struct device *dev;
52};
53
54static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
55{
56 if (offs < 0x24)
57 return readl(mc->regs[0] + offs);
58 BUG_ON(offs < 0x3c);
59 if (offs < 0x400)
60 return readl(mc->regs[1] + offs - 0x3c);
61 BUG();
62}
63
64static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
65{
66 if (offs < 0x24) {
67 writel(val, mc->regs[0] + offs);
68 return;
69 }
70 BUG_ON(offs < 0x3c);
71 if (offs < 0x400) {
72 writel(val, mc->regs[1] + offs - 0x3c);
73 return;
74 }
75 BUG();
76}
77
78static const char * const tegra20_mc_client[] = {
79 "cbr_display0a",
80 "cbr_display0ab",
81 "cbr_display0b",
82 "cbr_display0bb",
83 "cbr_display0c",
84 "cbr_display0cb",
85 "cbr_display1b",
86 "cbr_display1bb",
87 "cbr_eppup",
88 "cbr_g2pr",
89 "cbr_g2sr",
90 "cbr_mpeunifbr",
91 "cbr_viruv",
92 "csr_avpcarm7r",
93 "csr_displayhc",
94 "csr_displayhcb",
95 "csr_fdcdrd",
96 "csr_g2dr",
97 "csr_host1xdmar",
98 "csr_host1xr",
99 "csr_idxsrd",
100 "csr_mpcorer",
101 "csr_mpe_ipred",
102 "csr_mpeamemrd",
103 "csr_mpecsrd",
104 "csr_ppcsahbdmar",
105 "csr_ppcsahbslvr",
106 "csr_texsrd",
107 "csr_vdebsevr",
108 "csr_vdember",
109 "csr_vdemcer",
110 "csr_vdetper",
111 "cbw_eppu",
112 "cbw_eppv",
113 "cbw_eppy",
114 "cbw_mpeunifbw",
115 "cbw_viwsb",
116 "cbw_viwu",
117 "cbw_viwv",
118 "cbw_viwy",
119 "ccw_g2dw",
120 "csw_avpcarm7w",
121 "csw_fdcdwr",
122 "csw_host1xw",
123 "csw_ispw",
124 "csw_mpcorew",
125 "csw_mpecswr",
126 "csw_ppcsahbdmaw",
127 "csw_ppcsahbslvw",
128 "csw_vdebsevw",
129 "csw_vdembew",
130 "csw_vdetpmw",
131};
132
133static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
134{
135 u32 addr, req;
136 const char *client = "Unknown";
137 int idx, cid;
138 const struct reg_info {
139 u32 offset;
140 u32 write_bit; /* 0=READ, 1=WRITE */
141 int cid_shift;
142 char *message;
143 } reg[] = {
144 {
145 .offset = MC_DECERR_EMEM_OTHERS_STATUS,
146 .write_bit = 31,
147 .message = "MC_DECERR",
148 },
149 {
150 .offset = MC_GART_ERROR_REQ,
151 .cid_shift = 1,
152 .message = "MC_GART_ERR",
153
154 },
155 {
156 .offset = MC_SECURITY_VIOLATION_STATUS,
157 .write_bit = 31,
158 .message = "MC_SECURITY_ERR",
159 },
160 };
161
162 idx = n - MC_INT_ERR_SHIFT;
163 if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) {
164 pr_err_ratelimited("Unknown interrupt status %08lx\n", BIT(n));
165 return;
166 }
167
168 req = mc_readl(mc, reg[idx].offset);
169 cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK;
170 if (cid < ARRAY_SIZE(tegra20_mc_client))
171 client = tegra20_mc_client[cid];
172
173 addr = mc_readl(mc, reg[idx].offset + sizeof(u32));
174
175 pr_err_ratelimited("%s (0x%08x): 0x%08x %s (%s %s)\n",
176 reg[idx].message, req, addr, client,
177 (req & BIT(reg[idx].write_bit)) ? "write" : "read",
178 (reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ?
179 ((req & SECURITY_VIOLATION_TYPE) ?
180 "carveout" : "trustzone") : "");
181}
182
183static const struct of_device_id tegra20_mc_of_match[] __devinitconst = {
184 { .compatible = "nvidia,tegra20-mc", },
185 {},
186};
187
188static irqreturn_t tegra20_mc_isr(int irq, void *data)
189{
190 u32 stat, mask, bit;
191 struct tegra20_mc *mc = data;
192
193 stat = mc_readl(mc, MC_INTSTATUS);
194 mask = mc_readl(mc, MC_INTMASK);
195 mask &= stat;
196 if (!mask)
197 return IRQ_NONE;
198 while ((bit = ffs(mask)) != 0)
199 tegra20_mc_decode(mc, bit - 1);
200 mc_writel(mc, stat, MC_INTSTATUS);
201 return IRQ_HANDLED;
202}
203
204static int __devinit tegra20_mc_probe(struct platform_device *pdev)
205{
206 struct resource *irq;
207 struct tegra20_mc *mc;
208 int i, err;
209 u32 intmask;
210
211 mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
212 if (!mc)
213 return -ENOMEM;
214 mc->dev = &pdev->dev;
215
216 for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
217 struct resource *res;
218
219 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
220 if (!res)
221 return -ENODEV;
222 mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
223 if (!mc->regs[i])
224 return -EBUSY;
225 }
226
227 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
228 if (!irq)
229 return -ENODEV;
230 err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr,
231 IRQF_SHARED, dev_name(&pdev->dev), mc);
232 if (err)
233 return -ENODEV;
234
235 platform_set_drvdata(pdev, mc);
236
237 intmask = MC_INT_INVALID_GART_PAGE |
238 MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
239 mc_writel(mc, intmask, MC_INTMASK);
240 return 0;
241}
242
243static int __devexit tegra20_mc_remove(struct platform_device *pdev)
244{
245 return 0;
246}
247
248static struct platform_driver tegra20_mc_driver = {
249 .probe = tegra20_mc_probe,
250 .remove = __devexit_p(tegra20_mc_remove),
251 .driver = {
252 .name = DRV_NAME,
253 .owner = THIS_MODULE,
254 .of_match_table = tegra20_mc_of_match,
255 },
256};
257module_platform_driver(tegra20_mc_driver);
258
259MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
260MODULE_DESCRIPTION("Tegra20 MC driver");
261MODULE_LICENSE("GPL v2");
262MODULE_ALIAS("platform:" DRV_NAME);