• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/err.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/ratelimit.h>
24 #include <linux/platform_device.h>
25 #include <linux/interrupt.h>
26 #include <linux/io.h>
27 
28 #define DRV_NAME "tegra20-mc"
29 
30 #define MC_INTSTATUS			0x0
31 #define MC_INTMASK			0x4
32 
33 #define MC_INT_ERR_SHIFT		6
34 #define MC_INT_ERR_MASK			(0x1f << MC_INT_ERR_SHIFT)
35 #define MC_INT_DECERR_EMEM		BIT(MC_INT_ERR_SHIFT)
36 #define MC_INT_INVALID_GART_PAGE	BIT(MC_INT_ERR_SHIFT + 1)
37 #define MC_INT_SECURITY_VIOLATION	BIT(MC_INT_ERR_SHIFT + 2)
38 #define MC_INT_ARBITRATION_EMEM		BIT(MC_INT_ERR_SHIFT + 3)
39 
40 #define MC_GART_ERROR_REQ		0x30
41 #define MC_DECERR_EMEM_OTHERS_STATUS	0x58
42 #define MC_SECURITY_VIOLATION_STATUS	0x74
43 
44 #define SECURITY_VIOLATION_TYPE		BIT(30)	/* 0=TRUSTZONE, 1=CARVEOUT */
45 
46 #define MC_CLIENT_ID_MASK		0x3f
47 
48 #define NUM_MC_REG_BANKS		2
49 
50 struct tegra20_mc {
51 	void __iomem *regs[NUM_MC_REG_BANKS];
52 	struct device *dev;
53 };
54 
mc_readl(struct tegra20_mc * mc,u32 offs)55 static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
56 {
57 	u32 val = 0;
58 
59 	if (offs < 0x24)
60 		val = readl(mc->regs[0] + offs);
61 	else if (offs < 0x400)
62 		val = readl(mc->regs[1] + offs - 0x3c);
63 
64 	return val;
65 }
66 
mc_writel(struct tegra20_mc * mc,u32 val,u32 offs)67 static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
68 {
69 	if (offs < 0x24)
70 		writel(val, mc->regs[0] + offs);
71 	else if (offs < 0x400)
72 		writel(val, mc->regs[1] + offs - 0x3c);
73 }
74 
75 static const char * const tegra20_mc_client[] = {
76 	"cbr_display0a",
77 	"cbr_display0ab",
78 	"cbr_display0b",
79 	"cbr_display0bb",
80 	"cbr_display0c",
81 	"cbr_display0cb",
82 	"cbr_display1b",
83 	"cbr_display1bb",
84 	"cbr_eppup",
85 	"cbr_g2pr",
86 	"cbr_g2sr",
87 	"cbr_mpeunifbr",
88 	"cbr_viruv",
89 	"csr_avpcarm7r",
90 	"csr_displayhc",
91 	"csr_displayhcb",
92 	"csr_fdcdrd",
93 	"csr_g2dr",
94 	"csr_host1xdmar",
95 	"csr_host1xr",
96 	"csr_idxsrd",
97 	"csr_mpcorer",
98 	"csr_mpe_ipred",
99 	"csr_mpeamemrd",
100 	"csr_mpecsrd",
101 	"csr_ppcsahbdmar",
102 	"csr_ppcsahbslvr",
103 	"csr_texsrd",
104 	"csr_vdebsevr",
105 	"csr_vdember",
106 	"csr_vdemcer",
107 	"csr_vdetper",
108 	"cbw_eppu",
109 	"cbw_eppv",
110 	"cbw_eppy",
111 	"cbw_mpeunifbw",
112 	"cbw_viwsb",
113 	"cbw_viwu",
114 	"cbw_viwv",
115 	"cbw_viwy",
116 	"ccw_g2dw",
117 	"csw_avpcarm7w",
118 	"csw_fdcdwr",
119 	"csw_host1xw",
120 	"csw_ispw",
121 	"csw_mpcorew",
122 	"csw_mpecswr",
123 	"csw_ppcsahbdmaw",
124 	"csw_ppcsahbslvw",
125 	"csw_vdebsevw",
126 	"csw_vdembew",
127 	"csw_vdetpmw",
128 };
129 
tegra20_mc_decode(struct tegra20_mc * mc,int n)130 static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
131 {
132 	u32 addr, req;
133 	const char *client = "Unknown";
134 	int idx, cid;
135 	const struct reg_info {
136 		u32 offset;
137 		u32 write_bit;	/* 0=READ, 1=WRITE */
138 		int cid_shift;
139 		char *message;
140 	} reg[] = {
141 		{
142 			.offset = MC_DECERR_EMEM_OTHERS_STATUS,
143 			.write_bit = 31,
144 			.message = "MC_DECERR",
145 		},
146 		{
147 			.offset	= MC_GART_ERROR_REQ,
148 			.cid_shift = 1,
149 			.message = "MC_GART_ERR",
150 
151 		},
152 		{
153 			.offset = MC_SECURITY_VIOLATION_STATUS,
154 			.write_bit = 31,
155 			.message = "MC_SECURITY_ERR",
156 		},
157 	};
158 
159 	idx = n - MC_INT_ERR_SHIFT;
160 	if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) {
161 		dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
162 				    BIT(n));
163 		return;
164 	}
165 
166 	req = mc_readl(mc, reg[idx].offset);
167 	cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK;
168 	if (cid < ARRAY_SIZE(tegra20_mc_client))
169 		client = tegra20_mc_client[cid];
170 
171 	addr = mc_readl(mc, reg[idx].offset + sizeof(u32));
172 
173 	dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n",
174 			   reg[idx].message, req, addr, client,
175 			   (req & BIT(reg[idx].write_bit)) ? "write" : "read",
176 			   (reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ?
177 			   ((req & SECURITY_VIOLATION_TYPE) ?
178 			    "carveout" : "trustzone") : "");
179 }
180 
181 static const struct of_device_id tegra20_mc_of_match[] = {
182 	{ .compatible = "nvidia,tegra20-mc", },
183 	{},
184 };
185 
tegra20_mc_isr(int irq,void * data)186 static irqreturn_t tegra20_mc_isr(int irq, void *data)
187 {
188 	u32 stat, mask, bit;
189 	struct tegra20_mc *mc = data;
190 
191 	stat = mc_readl(mc, MC_INTSTATUS);
192 	mask = mc_readl(mc, MC_INTMASK);
193 	mask &= stat;
194 	if (!mask)
195 		return IRQ_NONE;
196 	while ((bit = ffs(mask)) != 0) {
197 		tegra20_mc_decode(mc, bit - 1);
198 		mask &= ~BIT(bit - 1);
199 	}
200 
201 	mc_writel(mc, stat, MC_INTSTATUS);
202 	return IRQ_HANDLED;
203 }
204 
tegra20_mc_probe(struct platform_device * pdev)205 static int tegra20_mc_probe(struct platform_device *pdev)
206 {
207 	struct resource *irq;
208 	struct tegra20_mc *mc;
209 	int i, err;
210 	u32 intmask;
211 
212 	mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
213 	if (!mc)
214 		return -ENOMEM;
215 	mc->dev = &pdev->dev;
216 
217 	for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
218 		struct resource *res;
219 
220 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
221 		mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
222 		if (IS_ERR(mc->regs[i]))
223 			return PTR_ERR(mc->regs[i]);
224 	}
225 
226 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
227 	if (!irq)
228 		return -ENODEV;
229 	err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr,
230 			       IRQF_SHARED, dev_name(&pdev->dev), mc);
231 	if (err)
232 		return -ENODEV;
233 
234 	platform_set_drvdata(pdev, mc);
235 
236 	intmask = MC_INT_INVALID_GART_PAGE |
237 		MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
238 	mc_writel(mc, intmask, MC_INTMASK);
239 	return 0;
240 }
241 
242 static struct platform_driver tegra20_mc_driver = {
243 	.probe = tegra20_mc_probe,
244 	.driver = {
245 		.name = DRV_NAME,
246 		.owner = THIS_MODULE,
247 		.of_match_table = tegra20_mc_of_match,
248 	},
249 };
250 module_platform_driver(tegra20_mc_driver);
251 
252 MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
253 MODULE_DESCRIPTION("Tegra20 MC driver");
254 MODULE_LICENSE("GPL v2");
255 MODULE_ALIAS("platform:" DRV_NAME);
256