Commit | Line | Data |
---|---|---|
89184651 TR |
1 | /* |
2 | * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/clk.h> | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/slab.h> | |
3d9dd6fd MP |
16 | #include <linux/sort.h> |
17 | ||
18 | #include <soc/tegra/fuse.h> | |
89184651 TR |
19 | |
20 | #include "mc.h" | |
21 | ||
22 | #define MC_INTSTATUS 0x000 | |
23 | #define MC_INT_DECERR_MTS (1 << 16) | |
24 | #define MC_INT_SECERR_SEC (1 << 13) | |
25 | #define MC_INT_DECERR_VPR (1 << 12) | |
26 | #define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) | |
27 | #define MC_INT_INVALID_SMMU_PAGE (1 << 10) | |
28 | #define MC_INT_ARBITRATION_EMEM (1 << 9) | |
29 | #define MC_INT_SECURITY_VIOLATION (1 << 8) | |
30 | #define MC_INT_DECERR_EMEM (1 << 6) | |
31 | ||
32 | #define MC_INTMASK 0x004 | |
33 | ||
34 | #define MC_ERR_STATUS 0x08 | |
35 | #define MC_ERR_STATUS_TYPE_SHIFT 28 | |
36 | #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT) | |
37 | #define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT) | |
38 | #define MC_ERR_STATUS_READABLE (1 << 27) | |
39 | #define MC_ERR_STATUS_WRITABLE (1 << 26) | |
40 | #define MC_ERR_STATUS_NONSECURE (1 << 25) | |
41 | #define MC_ERR_STATUS_ADR_HI_SHIFT 20 | |
42 | #define MC_ERR_STATUS_ADR_HI_MASK 0x3 | |
43 | #define MC_ERR_STATUS_SECURITY (1 << 17) | |
44 | #define MC_ERR_STATUS_RW (1 << 16) | |
89184651 TR |
45 | |
46 | #define MC_ERR_ADR 0x0c | |
47 | ||
48 | #define MC_EMEM_ARB_CFG 0x90 | |
49 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0) | |
50 | #define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff | |
51 | #define MC_EMEM_ARB_MISC0 0xd8 | |
52 | ||
3d9dd6fd MP |
53 | #define MC_EMEM_ADR_CFG 0x54 |
54 | #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) | |
55 | ||
89184651 TR |
56 | static const struct of_device_id tegra_mc_of_match[] = { |
57 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC | |
58 | { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, | |
59 | #endif | |
60 | #ifdef CONFIG_ARCH_TEGRA_114_SOC | |
61 | { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, | |
62 | #endif | |
63 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | |
64 | { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, | |
242b1d71 TR |
65 | #endif |
66 | #ifdef CONFIG_ARCH_TEGRA_132_SOC | |
67 | { .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc }, | |
588c43a7 TR |
68 | #endif |
69 | #ifdef CONFIG_ARCH_TEGRA_210_SOC | |
70 | { .compatible = "nvidia,tegra210-mc", .data = &tegra210_mc_soc }, | |
89184651 TR |
71 | #endif |
72 | { } | |
73 | }; | |
74 | MODULE_DEVICE_TABLE(of, tegra_mc_of_match); | |
75 | ||
76 | static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) | |
77 | { | |
78 | unsigned long long tick; | |
79 | unsigned int i; | |
80 | u32 value; | |
81 | ||
82 | /* compute the number of MC clock cycles per tick */ | |
83 | tick = mc->tick * clk_get_rate(mc->clk); | |
84 | do_div(tick, NSEC_PER_SEC); | |
85 | ||
86 | value = readl(mc->regs + MC_EMEM_ARB_CFG); | |
87 | value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; | |
88 | value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); | |
89 | writel(value, mc->regs + MC_EMEM_ARB_CFG); | |
90 | ||
91 | /* write latency allowance defaults */ | |
92 | for (i = 0; i < mc->soc->num_clients; i++) { | |
93 | const struct tegra_mc_la *la = &mc->soc->clients[i].la; | |
94 | u32 value; | |
95 | ||
96 | value = readl(mc->regs + la->reg); | |
97 | value &= ~(la->mask << la->shift); | |
98 | value |= (la->def & la->mask) << la->shift; | |
99 | writel(value, mc->regs + la->reg); | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
3d9dd6fd MP |
105 | void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) |
106 | { | |
107 | unsigned int i; | |
108 | struct tegra_mc_timing *timing = NULL; | |
109 | ||
110 | for (i = 0; i < mc->num_timings; i++) { | |
111 | if (mc->timings[i].rate == rate) { | |
112 | timing = &mc->timings[i]; | |
113 | break; | |
114 | } | |
115 | } | |
116 | ||
117 | if (!timing) { | |
118 | dev_err(mc->dev, "no memory timing registered for rate %lu\n", | |
119 | rate); | |
120 | return; | |
121 | } | |
122 | ||
123 | for (i = 0; i < mc->soc->num_emem_regs; ++i) | |
124 | mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); | |
125 | } | |
126 | ||
127 | unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) | |
128 | { | |
129 | u8 dram_count; | |
130 | ||
131 | dram_count = mc_readl(mc, MC_EMEM_ADR_CFG); | |
132 | dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV; | |
133 | dram_count++; | |
134 | ||
135 | return dram_count; | |
136 | } | |
137 | ||
138 | static int load_one_timing(struct tegra_mc *mc, | |
139 | struct tegra_mc_timing *timing, | |
140 | struct device_node *node) | |
141 | { | |
142 | int err; | |
143 | u32 tmp; | |
144 | ||
145 | err = of_property_read_u32(node, "clock-frequency", &tmp); | |
146 | if (err) { | |
147 | dev_err(mc->dev, | |
148 | "timing %s: failed to read rate\n", node->name); | |
149 | return err; | |
150 | } | |
151 | ||
152 | timing->rate = tmp; | |
153 | timing->emem_data = devm_kcalloc(mc->dev, mc->soc->num_emem_regs, | |
154 | sizeof(u32), GFP_KERNEL); | |
155 | if (!timing->emem_data) | |
156 | return -ENOMEM; | |
157 | ||
158 | err = of_property_read_u32_array(node, "nvidia,emem-configuration", | |
159 | timing->emem_data, | |
160 | mc->soc->num_emem_regs); | |
161 | if (err) { | |
162 | dev_err(mc->dev, | |
163 | "timing %s: failed to read EMEM configuration\n", | |
164 | node->name); | |
165 | return err; | |
166 | } | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | static int load_timings(struct tegra_mc *mc, struct device_node *node) | |
172 | { | |
173 | struct device_node *child; | |
174 | struct tegra_mc_timing *timing; | |
175 | int child_count = of_get_child_count(node); | |
176 | int i = 0, err; | |
177 | ||
178 | mc->timings = devm_kcalloc(mc->dev, child_count, sizeof(*timing), | |
179 | GFP_KERNEL); | |
180 | if (!mc->timings) | |
181 | return -ENOMEM; | |
182 | ||
183 | mc->num_timings = child_count; | |
184 | ||
185 | for_each_child_of_node(node, child) { | |
186 | timing = &mc->timings[i++]; | |
187 | ||
188 | err = load_one_timing(mc, timing, child); | |
189 | if (err) | |
190 | return err; | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | static int tegra_mc_setup_timings(struct tegra_mc *mc) | |
197 | { | |
198 | struct device_node *node; | |
199 | u32 ram_code, node_ram_code; | |
200 | int err; | |
201 | ||
202 | ram_code = tegra_read_ram_code(); | |
203 | ||
204 | mc->num_timings = 0; | |
205 | ||
206 | for_each_child_of_node(mc->dev->of_node, node) { | |
207 | err = of_property_read_u32(node, "nvidia,ram-code", | |
208 | &node_ram_code); | |
209 | if (err || (node_ram_code != ram_code)) { | |
210 | of_node_put(node); | |
211 | continue; | |
212 | } | |
213 | ||
214 | err = load_timings(mc, node); | |
215 | if (err) | |
216 | return err; | |
217 | of_node_put(node); | |
218 | break; | |
219 | } | |
220 | ||
221 | if (mc->num_timings == 0) | |
222 | dev_warn(mc->dev, | |
223 | "no memory timings for RAM code %u registered\n", | |
224 | ram_code); | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
89184651 TR |
229 | static const char *const status_names[32] = { |
230 | [ 1] = "External interrupt", | |
231 | [ 6] = "EMEM address decode error", | |
232 | [ 8] = "Security violation", | |
233 | [ 9] = "EMEM arbitration error", | |
234 | [10] = "Page fault", | |
235 | [11] = "Invalid APB ASID update", | |
236 | [12] = "VPR violation", | |
237 | [13] = "Secure carveout violation", | |
238 | [16] = "MTS carveout violation", | |
239 | }; | |
240 | ||
241 | static const char *const error_names[8] = { | |
242 | [2] = "EMEM decode error", | |
243 | [3] = "TrustZone violation", | |
244 | [4] = "Carveout violation", | |
245 | [6] = "SMMU translation error", | |
246 | }; | |
247 | ||
248 | static irqreturn_t tegra_mc_irq(int irq, void *data) | |
249 | { | |
250 | struct tegra_mc *mc = data; | |
251 | unsigned long status, mask; | |
252 | unsigned int bit; | |
253 | ||
254 | /* mask all interrupts to avoid flooding */ | |
255 | status = mc_readl(mc, MC_INTSTATUS); | |
256 | mask = mc_readl(mc, MC_INTMASK); | |
257 | ||
258 | for_each_set_bit(bit, &status, 32) { | |
259 | const char *error = status_names[bit] ?: "unknown"; | |
260 | const char *client = "unknown", *desc; | |
261 | const char *direction, *secure; | |
262 | phys_addr_t addr = 0; | |
263 | unsigned int i; | |
264 | char perm[7]; | |
265 | u8 id, type; | |
266 | u32 value; | |
267 | ||
268 | value = mc_readl(mc, MC_ERR_STATUS); | |
269 | ||
270 | #ifdef CONFIG_PHYS_ADDR_T_64BIT | |
271 | if (mc->soc->num_address_bits > 32) { | |
272 | addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & | |
273 | MC_ERR_STATUS_ADR_HI_MASK); | |
274 | addr <<= 32; | |
275 | } | |
276 | #endif | |
277 | ||
278 | if (value & MC_ERR_STATUS_RW) | |
279 | direction = "write"; | |
280 | else | |
281 | direction = "read"; | |
282 | ||
283 | if (value & MC_ERR_STATUS_SECURITY) | |
284 | secure = "secure "; | |
285 | else | |
286 | secure = ""; | |
287 | ||
3c01cf3b | 288 | id = value & mc->soc->client_id_mask; |
89184651 TR |
289 | |
290 | for (i = 0; i < mc->soc->num_clients; i++) { | |
291 | if (mc->soc->clients[i].id == id) { | |
292 | client = mc->soc->clients[i].name; | |
293 | break; | |
294 | } | |
295 | } | |
296 | ||
297 | type = (value & MC_ERR_STATUS_TYPE_MASK) >> | |
298 | MC_ERR_STATUS_TYPE_SHIFT; | |
299 | desc = error_names[type]; | |
300 | ||
301 | switch (value & MC_ERR_STATUS_TYPE_MASK) { | |
302 | case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: | |
303 | perm[0] = ' '; | |
304 | perm[1] = '['; | |
305 | ||
306 | if (value & MC_ERR_STATUS_READABLE) | |
307 | perm[2] = 'R'; | |
308 | else | |
309 | perm[2] = '-'; | |
310 | ||
311 | if (value & MC_ERR_STATUS_WRITABLE) | |
312 | perm[3] = 'W'; | |
313 | else | |
314 | perm[3] = '-'; | |
315 | ||
316 | if (value & MC_ERR_STATUS_NONSECURE) | |
317 | perm[4] = '-'; | |
318 | else | |
319 | perm[4] = 'S'; | |
320 | ||
321 | perm[5] = ']'; | |
322 | perm[6] = '\0'; | |
323 | break; | |
324 | ||
325 | default: | |
326 | perm[0] = '\0'; | |
327 | break; | |
328 | } | |
329 | ||
330 | value = mc_readl(mc, MC_ERR_ADR); | |
331 | addr |= value; | |
332 | ||
333 | dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", | |
334 | client, secure, direction, &addr, error, | |
335 | desc, perm); | |
336 | } | |
337 | ||
338 | /* clear interrupts */ | |
339 | mc_writel(mc, status, MC_INTSTATUS); | |
340 | ||
341 | return IRQ_HANDLED; | |
342 | } | |
343 | ||
344 | static int tegra_mc_probe(struct platform_device *pdev) | |
345 | { | |
346 | const struct of_device_id *match; | |
347 | struct resource *res; | |
348 | struct tegra_mc *mc; | |
349 | u32 value; | |
350 | int err; | |
351 | ||
352 | match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); | |
353 | if (!match) | |
354 | return -ENODEV; | |
355 | ||
356 | mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); | |
357 | if (!mc) | |
358 | return -ENOMEM; | |
359 | ||
360 | platform_set_drvdata(pdev, mc); | |
361 | mc->soc = match->data; | |
362 | mc->dev = &pdev->dev; | |
363 | ||
364 | /* length of MC tick in nanoseconds */ | |
365 | mc->tick = 30; | |
366 | ||
367 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
368 | mc->regs = devm_ioremap_resource(&pdev->dev, res); | |
369 | if (IS_ERR(mc->regs)) | |
370 | return PTR_ERR(mc->regs); | |
371 | ||
372 | mc->clk = devm_clk_get(&pdev->dev, "mc"); | |
373 | if (IS_ERR(mc->clk)) { | |
374 | dev_err(&pdev->dev, "failed to get MC clock: %ld\n", | |
375 | PTR_ERR(mc->clk)); | |
376 | return PTR_ERR(mc->clk); | |
377 | } | |
378 | ||
379 | err = tegra_mc_setup_latency_allowance(mc); | |
380 | if (err < 0) { | |
381 | dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", | |
382 | err); | |
383 | return err; | |
384 | } | |
385 | ||
3d9dd6fd MP |
386 | err = tegra_mc_setup_timings(mc); |
387 | if (err < 0) { | |
388 | dev_err(&pdev->dev, "failed to setup timings: %d\n", err); | |
389 | return err; | |
390 | } | |
391 | ||
89184651 TR |
392 | if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) { |
393 | mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); | |
394 | if (IS_ERR(mc->smmu)) { | |
395 | dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", | |
396 | PTR_ERR(mc->smmu)); | |
397 | return PTR_ERR(mc->smmu); | |
398 | } | |
399 | } | |
400 | ||
401 | mc->irq = platform_get_irq(pdev, 0); | |
402 | if (mc->irq < 0) { | |
403 | dev_err(&pdev->dev, "interrupt not specified\n"); | |
404 | return mc->irq; | |
405 | } | |
406 | ||
407 | err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED, | |
408 | dev_name(&pdev->dev), mc); | |
409 | if (err < 0) { | |
410 | dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, | |
411 | err); | |
412 | return err; | |
413 | } | |
414 | ||
3c01cf3b PW |
415 | WARN(!mc->soc->client_id_mask, "Missing client ID mask for this SoC\n"); |
416 | ||
89184651 TR |
417 | value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | |
418 | MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | | |
6f0a4d0c TV |
419 | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM; |
420 | ||
89184651 TR |
421 | mc_writel(mc, value, MC_INTMASK); |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | static struct platform_driver tegra_mc_driver = { | |
427 | .driver = { | |
428 | .name = "tegra-mc", | |
429 | .of_match_table = tegra_mc_of_match, | |
430 | .suppress_bind_attrs = true, | |
431 | }, | |
432 | .prevent_deferred_probe = true, | |
433 | .probe = tegra_mc_probe, | |
434 | }; | |
435 | ||
436 | static int tegra_mc_init(void) | |
437 | { | |
438 | return platform_driver_register(&tegra_mc_driver); | |
439 | } | |
440 | arch_initcall(tegra_mc_init); | |
441 | ||
442 | MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); | |
443 | MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver"); | |
444 | MODULE_LICENSE("GPL v2"); |