1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved |
4 | * |
5 | * The driver handles Error's from Control Backbone(CBB) version 2.0. |
6 | * generated due to illegal accesses. The driver prints debug information |
7 | * about failed transaction on receiving interrupt from Error Notifier. |
8 | * Error types supported by CBB2.0 are: |
9 | * UNSUPPORTED_ERR, PWRDOWN_ERR, TIMEOUT_ERR, FIREWALL_ERR, DECODE_ERR, |
10 | * SLAVE_ERR |
11 | */ |
12 | |
13 | #include <linux/acpi.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/cpufeature.h> |
16 | #include <linux/debugfs.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/device.h> |
21 | #include <linux/io.h> |
22 | #include <linux/interrupt.h> |
23 | #include <linux/ioport.h> |
24 | #include <soc/tegra/fuse.h> |
25 | #include <soc/tegra/tegra-cbb.h> |
26 | |
27 | #define FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0 0x0 |
28 | #define FABRIC_EN_CFG_STATUS_0_0 0x40 |
29 | #define FABRIC_EN_CFG_ADDR_INDEX_0_0 0x60 |
30 | #define FABRIC_EN_CFG_ADDR_LOW_0 0x80 |
31 | #define FABRIC_EN_CFG_ADDR_HI_0 0x84 |
32 | |
33 | #define FABRIC_MN_MASTER_ERR_EN_0 0x200 |
34 | #define FABRIC_MN_MASTER_ERR_FORCE_0 0x204 |
35 | #define FABRIC_MN_MASTER_ERR_STATUS_0 0x208 |
36 | #define FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0 0x20c |
37 | |
38 | #define FABRIC_MN_MASTER_LOG_ERR_STATUS_0 0x300 |
39 | #define FABRIC_MN_MASTER_LOG_ADDR_LOW_0 0x304 |
40 | #define FABRIC_MN_MASTER_LOG_ADDR_HIGH_0 0x308 |
41 | #define FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0 0x30c |
42 | #define FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0 0x310 |
43 | #define FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0 0x314 |
44 | #define FABRIC_MN_MASTER_LOG_USER_BITS0_0 0x318 |
45 | |
46 | #define AXI_SLV_TIMEOUT_STATUS_0_0 0x8 |
47 | #define APB_BLOCK_TMO_STATUS_0 0xc00 |
48 | #define APB_BLOCK_NUM_TMO_OFFSET 0x20 |
49 | |
50 | #define FAB_EM_EL_MSTRID GENMASK(29, 24) |
51 | #define FAB_EM_EL_VQC GENMASK(17, 16) |
52 | #define FAB_EM_EL_GRPSEC GENMASK(14, 8) |
53 | #define FAB_EM_EL_FALCONSEC GENMASK(1, 0) |
54 | |
55 | #define FAB_EM_EL_FABID GENMASK(20, 16) |
56 | #define FAB_EM_EL_SLAVEID GENMASK(7, 0) |
57 | |
58 | #define FAB_EM_EL_ACCESSID GENMASK(7, 0) |
59 | |
60 | #define FAB_EM_EL_AXCACHE GENMASK(27, 24) |
61 | #define FAB_EM_EL_AXPROT GENMASK(22, 20) |
62 | #define FAB_EM_EL_BURSTLENGTH GENMASK(19, 12) |
63 | #define FAB_EM_EL_BURSTTYPE GENMASK(9, 8) |
64 | #define FAB_EM_EL_BEATSIZE GENMASK(6, 4) |
65 | #define FAB_EM_EL_ACCESSTYPE GENMASK(0, 0) |
66 | |
67 | #define USRBITS_MSTR_ID GENMASK(29, 24) |
68 | |
69 | #define REQ_SOCKET_ID GENMASK(27, 24) |
70 | |
71 | #define CCPLEX_MSTRID 0x1 |
72 | #define FIREWALL_APERTURE_SZ 0x10000 |
73 | /* Write firewall check enable */ |
74 | #define WEN 0x20000 |
75 | |
76 | enum tegra234_cbb_fabric_ids { |
77 | CBB_FAB_ID, |
78 | SCE_FAB_ID, |
79 | RCE_FAB_ID, |
80 | DCE_FAB_ID, |
81 | AON_FAB_ID, |
82 | PSC_FAB_ID, |
83 | BPMP_FAB_ID, |
84 | FSI_FAB_ID, |
85 | MAX_FAB_ID, |
86 | }; |
87 | |
88 | struct tegra234_slave_lookup { |
89 | const char *name; |
90 | unsigned int offset; |
91 | }; |
92 | |
93 | struct tegra234_cbb_fabric { |
94 | const char *name; |
95 | phys_addr_t off_mask_erd; |
96 | phys_addr_t firewall_base; |
97 | unsigned int firewall_ctl; |
98 | unsigned int firewall_wr_ctl; |
99 | const char * const *master_id; |
100 | unsigned int notifier_offset; |
101 | const struct tegra_cbb_error *errors; |
102 | const int max_errors; |
103 | const struct tegra234_slave_lookup *slave_map; |
104 | const int max_slaves; |
105 | }; |
106 | |
107 | struct tegra234_cbb { |
108 | struct tegra_cbb base; |
109 | |
110 | const struct tegra234_cbb_fabric *fabric; |
111 | struct resource *res; |
112 | void __iomem *regs; |
113 | |
114 | int num_intr; |
115 | int sec_irq; |
116 | |
117 | /* record */ |
118 | void __iomem *mon; |
119 | unsigned int type; |
120 | u32 mask; |
121 | u64 access; |
122 | u32 mn_attr0; |
123 | u32 mn_attr1; |
124 | u32 mn_attr2; |
125 | u32 mn_user_bits; |
126 | }; |
127 | |
128 | static inline struct tegra234_cbb *to_tegra234_cbb(struct tegra_cbb *cbb) |
129 | { |
130 | return container_of(cbb, struct tegra234_cbb, base); |
131 | } |
132 | |
133 | static LIST_HEAD(cbb_list); |
134 | static DEFINE_SPINLOCK(cbb_lock); |
135 | |
136 | static bool |
137 | tegra234_cbb_write_access_allowed(struct platform_device *pdev, struct tegra234_cbb *cbb) |
138 | { |
139 | u32 val; |
140 | |
141 | if (!cbb->fabric->firewall_base || |
142 | !cbb->fabric->firewall_ctl || |
143 | !cbb->fabric->firewall_wr_ctl) { |
144 | dev_info(&pdev->dev, "SoC data missing for firewall\n"); |
145 | return false; |
146 | } |
147 | |
148 | if ((cbb->fabric->firewall_ctl > FIREWALL_APERTURE_SZ) || |
149 | (cbb->fabric->firewall_wr_ctl > FIREWALL_APERTURE_SZ)) { |
150 | dev_err(&pdev->dev, "wrong firewall offset value\n"); |
151 | return false; |
152 | } |
153 | |
154 | val = readl(addr: cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_ctl); |
155 | /* |
156 | * If the firewall check feature for allowing or blocking the |
157 | * write accesses through the firewall of a fabric is disabled |
158 | * then CCPLEX can write to the registers of that fabric. |
159 | */ |
160 | if (!(val & WEN)) |
161 | return true; |
162 | |
163 | /* |
164 | * If the firewall check is enabled then check whether CCPLEX |
165 | * has write access to the fabric's error notifier registers |
166 | */ |
167 | val = readl(addr: cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_wr_ctl); |
168 | if (val & (BIT(CCPLEX_MSTRID))) |
169 | return true; |
170 | |
171 | return false; |
172 | } |
173 | |
174 | static void tegra234_cbb_fault_enable(struct tegra_cbb *cbb) |
175 | { |
176 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
177 | void __iomem *addr; |
178 | |
179 | addr = priv->regs + priv->fabric->notifier_offset; |
180 | writel(val: 0x1ff, addr: addr + FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0); |
181 | dsb(sy); |
182 | } |
183 | |
184 | static void tegra234_cbb_error_clear(struct tegra_cbb *cbb) |
185 | { |
186 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
187 | |
188 | writel(val: 0x3f, addr: priv->mon + FABRIC_MN_MASTER_ERR_STATUS_0); |
189 | dsb(sy); |
190 | } |
191 | |
192 | static u32 tegra234_cbb_get_status(struct tegra_cbb *cbb) |
193 | { |
194 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
195 | void __iomem *addr; |
196 | u32 value; |
197 | |
198 | addr = priv->regs + priv->fabric->notifier_offset; |
199 | value = readl(addr: addr + FABRIC_EN_CFG_STATUS_0_0); |
200 | dsb(sy); |
201 | |
202 | return value; |
203 | } |
204 | |
205 | static void tegra234_cbb_mask_serror(struct tegra234_cbb *cbb) |
206 | { |
207 | writel(val: 0x1, addr: cbb->regs + cbb->fabric->off_mask_erd); |
208 | dsb(sy); |
209 | } |
210 | |
211 | static u32 tegra234_cbb_get_tmo_slv(void __iomem *addr) |
212 | { |
213 | u32 timeout; |
214 | |
215 | timeout = readl(addr); |
216 | return timeout; |
217 | } |
218 | |
219 | static void tegra234_cbb_tmo_slv(struct seq_file *file, const char *slave, void __iomem *addr, |
220 | u32 status) |
221 | { |
222 | tegra_cbb_print_err(file, fmt: "\t %s : %#x\n", slave, status); |
223 | } |
224 | |
225 | static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave, |
226 | void __iomem *base) |
227 | { |
228 | unsigned int block = 0; |
229 | void __iomem *addr; |
230 | char name[64]; |
231 | u32 status; |
232 | |
233 | status = tegra234_cbb_get_tmo_slv(addr: base); |
234 | if (status) |
235 | tegra_cbb_print_err(file, fmt: "\t %s_BLOCK_TMO_STATUS : %#x\n", slave, status); |
236 | |
237 | while (status) { |
238 | if (status & BIT(0)) { |
239 | u32 timeout, clients, client = 0; |
240 | |
241 | addr = base + APB_BLOCK_NUM_TMO_OFFSET + (block * 4); |
242 | timeout = tegra234_cbb_get_tmo_slv(addr); |
243 | clients = timeout; |
244 | |
245 | while (timeout) { |
246 | if (timeout & BIT(0)) { |
247 | if (clients != 0xffffffff) |
248 | clients &= BIT(client); |
249 | |
250 | sprintf(buf: name, fmt: "%s_BLOCK%d_TMO", slave, block); |
251 | |
252 | tegra234_cbb_tmo_slv(file, slave: name, addr, status: clients); |
253 | } |
254 | |
255 | timeout >>= 1; |
256 | client++; |
257 | } |
258 | } |
259 | |
260 | status >>= 1; |
261 | block++; |
262 | } |
263 | } |
264 | |
265 | static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234_cbb *cbb, |
266 | u8 slave_id, u8 fab_id) |
267 | { |
268 | const struct tegra234_slave_lookup *map = cbb->fabric->slave_map; |
269 | void __iomem *addr; |
270 | |
271 | /* |
272 | * 1) Get slave node name and address mapping using slave_id. |
273 | * 2) Check if the timed out slave node is APB or AXI. |
274 | * 3) If AXI, then print timeout register and reset axi slave |
275 | * using <FABRIC>_SN_<>_SLV_TIMEOUT_STATUS_0_0 register. |
276 | * 4) If APB, then perform an additional lookup to find the client |
277 | * which timed out. |
278 | * a) Get block number from the index of set bit in |
279 | * <FABRIC>_SN_AXI2APB_<>_BLOCK_TMO_STATUS_0 register. |
280 | * b) Get address of register respective to block number i.e. |
281 | * <FABRIC>_SN_AXI2APB_<>_BLOCK<index-set-bit>_TMO_0. |
282 | * c) Read the register in above step to get client_id which |
283 | * timed out as per the set bits. |
284 | * d) Reset the timedout client and print details. |
285 | * e) Goto step-a till all bits are set. |
286 | */ |
287 | |
288 | addr = cbb->regs + map[slave_id].offset; |
289 | |
290 | if (strstr(map[slave_id].name, "AXI2APB")) { |
291 | addr += APB_BLOCK_TMO_STATUS_0; |
292 | |
293 | tegra234_cbb_lookup_apbslv(file, slave: map[slave_id].name, base: addr); |
294 | } else { |
295 | char name[64]; |
296 | u32 status; |
297 | |
298 | addr += AXI_SLV_TIMEOUT_STATUS_0_0; |
299 | |
300 | status = tegra234_cbb_get_tmo_slv(addr); |
301 | if (status) { |
302 | sprintf(buf: name, fmt: "%s_SLV_TIMEOUT_STATUS", map[slave_id].name); |
303 | tegra234_cbb_tmo_slv(file, slave: name, addr, status); |
304 | } |
305 | } |
306 | } |
307 | |
308 | static void tegra234_cbb_print_error(struct seq_file *file, struct tegra234_cbb *cbb, u32 status, |
309 | u32 overflow) |
310 | { |
311 | unsigned int type = 0; |
312 | |
313 | if (status & (status - 1)) |
314 | tegra_cbb_print_err(file, fmt: "\t Multiple type of errors reported\n"); |
315 | |
316 | while (status) { |
317 | if (type >= cbb->fabric->max_errors) { |
318 | tegra_cbb_print_err(file, fmt: "\t Wrong type index:%u, status:%u\n", |
319 | type, status); |
320 | return; |
321 | } |
322 | |
323 | if (status & 0x1) |
324 | tegra_cbb_print_err(file, fmt: "\t Error Code\t\t: %s\n", |
325 | cbb->fabric->errors[type].code); |
326 | |
327 | status >>= 1; |
328 | type++; |
329 | } |
330 | |
331 | type = 0; |
332 | |
333 | while (overflow) { |
334 | if (type >= cbb->fabric->max_errors) { |
335 | tegra_cbb_print_err(file, fmt: "\t Wrong type index:%u, overflow:%u\n", |
336 | type, overflow); |
337 | return; |
338 | } |
339 | |
340 | if (overflow & 0x1) |
341 | tegra_cbb_print_err(file, fmt: "\t Overflow\t\t: Multiple %s\n", |
342 | cbb->fabric->errors[type].code); |
343 | |
344 | overflow >>= 1; |
345 | type++; |
346 | } |
347 | } |
348 | |
349 | static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb) |
350 | { |
351 | u8 cache_type, prot_type, burst_length, mstr_id, grpsec, vqc, falconsec, beat_size; |
352 | u8 access_type, access_id, requester_socket_id, local_socket_id, slave_id, fab_id; |
353 | char fabric_name[20]; |
354 | bool is_numa = false; |
355 | u8 burst_type; |
356 | |
357 | if (num_possible_nodes() > 1) |
358 | is_numa = true; |
359 | |
360 | mstr_id = FIELD_GET(FAB_EM_EL_MSTRID, cbb->mn_user_bits); |
361 | vqc = FIELD_GET(FAB_EM_EL_VQC, cbb->mn_user_bits); |
362 | grpsec = FIELD_GET(FAB_EM_EL_GRPSEC, cbb->mn_user_bits); |
363 | falconsec = FIELD_GET(FAB_EM_EL_FALCONSEC, cbb->mn_user_bits); |
364 | |
365 | /* |
366 | * For SOC with multiple NUMA nodes, print cross socket access |
367 | * errors only if initiator/master_id is CCPLEX, CPMU or GPU. |
368 | */ |
369 | if (is_numa) { |
370 | local_socket_id = numa_node_id(); |
371 | requester_socket_id = FIELD_GET(REQ_SOCKET_ID, cbb->mn_attr2); |
372 | |
373 | if (requester_socket_id != local_socket_id) { |
374 | if ((mstr_id != 0x1) && (mstr_id != 0x2) && (mstr_id != 0xB)) |
375 | return; |
376 | } |
377 | } |
378 | |
379 | fab_id = FIELD_GET(FAB_EM_EL_FABID, cbb->mn_attr2); |
380 | slave_id = FIELD_GET(FAB_EM_EL_SLAVEID, cbb->mn_attr2); |
381 | |
382 | access_id = FIELD_GET(FAB_EM_EL_ACCESSID, cbb->mn_attr1); |
383 | |
384 | cache_type = FIELD_GET(FAB_EM_EL_AXCACHE, cbb->mn_attr0); |
385 | prot_type = FIELD_GET(FAB_EM_EL_AXPROT, cbb->mn_attr0); |
386 | burst_length = FIELD_GET(FAB_EM_EL_BURSTLENGTH, cbb->mn_attr0); |
387 | burst_type = FIELD_GET(FAB_EM_EL_BURSTTYPE, cbb->mn_attr0); |
388 | beat_size = FIELD_GET(FAB_EM_EL_BEATSIZE, cbb->mn_attr0); |
389 | access_type = FIELD_GET(FAB_EM_EL_ACCESSTYPE, cbb->mn_attr0); |
390 | |
391 | tegra_cbb_print_err(file, fmt: "\n"); |
392 | if (cbb->type < cbb->fabric->max_errors) |
393 | tegra_cbb_print_err(file, fmt: "\t Error Code\t\t: %s\n", |
394 | cbb->fabric->errors[cbb->type].code); |
395 | else |
396 | tegra_cbb_print_err(file, fmt: "\t Wrong type index:%u\n", cbb->type); |
397 | |
398 | tegra_cbb_print_err(file, fmt: "\t MASTER_ID\t\t: %s\n", cbb->fabric->master_id[mstr_id]); |
399 | tegra_cbb_print_err(file, fmt: "\t Address\t\t: %#llx\n", cbb->access); |
400 | |
401 | tegra_cbb_print_cache(file, cache: cache_type); |
402 | tegra_cbb_print_prot(file, prot: prot_type); |
403 | |
404 | tegra_cbb_print_err(file, fmt: "\t Access_Type\t\t: %s", (access_type) ? "Write\n": "Read\n"); |
405 | tegra_cbb_print_err(file, fmt: "\t Access_ID\t\t: %#x", access_id); |
406 | |
407 | if (fab_id == PSC_FAB_ID) |
408 | strcpy(p: fabric_name, q: "psc-fabric"); |
409 | else if (fab_id == FSI_FAB_ID) |
410 | strcpy(p: fabric_name, q: "fsi-fabric"); |
411 | else |
412 | strcpy(p: fabric_name, q: cbb->fabric->name); |
413 | |
414 | if (is_numa) { |
415 | tegra_cbb_print_err(file, fmt: "\t Requester_Socket_Id\t: %#x\n", |
416 | requester_socket_id); |
417 | tegra_cbb_print_err(file, fmt: "\t Local_Socket_Id\t: %#x\n", |
418 | local_socket_id); |
419 | tegra_cbb_print_err(file, fmt: "\t No. of NUMA_NODES\t: %#x\n", |
420 | num_possible_nodes()); |
421 | } |
422 | |
423 | tegra_cbb_print_err(file, fmt: "\t Fabric\t\t: %s\n", fabric_name); |
424 | tegra_cbb_print_err(file, fmt: "\t Slave_Id\t\t: %#x\n", slave_id); |
425 | tegra_cbb_print_err(file, fmt: "\t Burst_length\t\t: %#x\n", burst_length); |
426 | tegra_cbb_print_err(file, fmt: "\t Burst_type\t\t: %#x\n", burst_type); |
427 | tegra_cbb_print_err(file, fmt: "\t Beat_size\t\t: %#x\n", beat_size); |
428 | tegra_cbb_print_err(file, fmt: "\t VQC\t\t\t: %#x\n", vqc); |
429 | tegra_cbb_print_err(file, fmt: "\t GRPSEC\t\t: %#x\n", grpsec); |
430 | tegra_cbb_print_err(file, fmt: "\t FALCONSEC\t\t: %#x\n", falconsec); |
431 | |
432 | if ((fab_id == PSC_FAB_ID) || (fab_id == FSI_FAB_ID)) |
433 | return; |
434 | |
435 | if (slave_id >= cbb->fabric->max_slaves) { |
436 | tegra_cbb_print_err(file, fmt: "\t Invalid slave_id:%d\n", slave_id); |
437 | return; |
438 | } |
439 | |
440 | if (!strcmp(cbb->fabric->errors[cbb->type].code, "TIMEOUT_ERR")) { |
441 | tegra234_lookup_slave_timeout(file, cbb, slave_id, fab_id); |
442 | return; |
443 | } |
444 | |
445 | tegra_cbb_print_err(file, fmt: "\t Slave\t\t\t: %s\n", cbb->fabric->slave_map[slave_id].name); |
446 | } |
447 | |
448 | static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb) |
449 | { |
450 | u32 overflow, status, error; |
451 | |
452 | status = readl(addr: cbb->mon + FABRIC_MN_MASTER_ERR_STATUS_0); |
453 | if (!status) { |
454 | pr_err("Error Notifier received a spurious notification\n"); |
455 | return -ENODATA; |
456 | } |
457 | |
458 | if (status == 0xffffffff) { |
459 | pr_err("CBB registers returning all 1's which is invalid\n"); |
460 | return -EINVAL; |
461 | } |
462 | |
463 | overflow = readl(addr: cbb->mon + FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0); |
464 | |
465 | tegra234_cbb_print_error(file, cbb, status, overflow); |
466 | |
467 | error = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ERR_STATUS_0); |
468 | if (!error) { |
469 | pr_info("Error Monitor doesn't have Error Logger\n"); |
470 | return -EINVAL; |
471 | } |
472 | |
473 | cbb->type = 0; |
474 | |
475 | while (error) { |
476 | if (error & BIT(0)) { |
477 | u32 hi, lo; |
478 | |
479 | hi = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_HIGH_0); |
480 | lo = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_LOW_0); |
481 | |
482 | cbb->access = (u64)hi << 32 | lo; |
483 | |
484 | cbb->mn_attr0 = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0); |
485 | cbb->mn_attr1 = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0); |
486 | cbb->mn_attr2 = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0); |
487 | cbb->mn_user_bits = readl(addr: cbb->mon + FABRIC_MN_MASTER_LOG_USER_BITS0_0); |
488 | |
489 | print_errlog_err(file, cbb); |
490 | } |
491 | |
492 | cbb->type++; |
493 | error >>= 1; |
494 | } |
495 | |
496 | return 0; |
497 | } |
498 | |
499 | static int print_err_notifier(struct seq_file *file, struct tegra234_cbb *cbb, u32 status) |
500 | { |
501 | unsigned int index = 0; |
502 | int err; |
503 | |
504 | pr_crit("**************************************\n"); |
505 | pr_crit("CPU:%d, Error:%s, Errmon:%d\n", smp_processor_id(), |
506 | cbb->fabric->name, status); |
507 | |
508 | while (status) { |
509 | if (status & BIT(0)) { |
510 | unsigned int notifier = cbb->fabric->notifier_offset; |
511 | u32 hi, lo, mask = BIT(index); |
512 | phys_addr_t addr; |
513 | u64 offset; |
514 | |
515 | writel(val: mask, addr: cbb->regs + notifier + FABRIC_EN_CFG_ADDR_INDEX_0_0); |
516 | hi = readl(addr: cbb->regs + notifier + FABRIC_EN_CFG_ADDR_HI_0); |
517 | lo = readl(addr: cbb->regs + notifier + FABRIC_EN_CFG_ADDR_LOW_0); |
518 | |
519 | addr = (u64)hi << 32 | lo; |
520 | |
521 | offset = addr - cbb->res->start; |
522 | cbb->mon = cbb->regs + offset; |
523 | cbb->mask = BIT(index); |
524 | |
525 | err = print_errmonX_info(file, cbb); |
526 | tegra234_cbb_error_clear(cbb: &cbb->base); |
527 | if (err) |
528 | return err; |
529 | } |
530 | |
531 | status >>= 1; |
532 | index++; |
533 | } |
534 | |
535 | tegra_cbb_print_err(file, fmt: "\t**************************************\n"); |
536 | return 0; |
537 | } |
538 | |
539 | #ifdef CONFIG_DEBUG_FS |
540 | static DEFINE_MUTEX(cbb_debugfs_mutex); |
541 | |
542 | static int tegra234_cbb_debugfs_show(struct tegra_cbb *cbb, struct seq_file *file, void *data) |
543 | { |
544 | int err = 0; |
545 | |
546 | mutex_lock(&cbb_debugfs_mutex); |
547 | |
548 | list_for_each_entry(cbb, &cbb_list, node) { |
549 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
550 | u32 status; |
551 | |
552 | status = tegra_cbb_get_status(cbb: &priv->base); |
553 | if (status) { |
554 | err = print_err_notifier(file, cbb: priv, status); |
555 | if (err) |
556 | break; |
557 | } |
558 | } |
559 | |
560 | mutex_unlock(lock: &cbb_debugfs_mutex); |
561 | return err; |
562 | } |
563 | #endif |
564 | |
565 | /* |
566 | * Handler for CBB errors |
567 | */ |
568 | static irqreturn_t tegra234_cbb_isr(int irq, void *data) |
569 | { |
570 | bool is_inband_err = false; |
571 | struct tegra_cbb *cbb; |
572 | unsigned long flags; |
573 | u8 mstr_id; |
574 | int err; |
575 | |
576 | spin_lock_irqsave(&cbb_lock, flags); |
577 | |
578 | list_for_each_entry(cbb, &cbb_list, node) { |
579 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
580 | u32 status = tegra_cbb_get_status(cbb); |
581 | |
582 | if (status && (irq == priv->sec_irq)) { |
583 | tegra_cbb_print_err(NULL, fmt: "CPU:%d, Error: %s@0x%llx, irq=%d\n", |
584 | smp_processor_id(), priv->fabric->name, |
585 | priv->res->start, irq); |
586 | |
587 | err = print_err_notifier(NULL, cbb: priv, status); |
588 | if (err) |
589 | goto unlock; |
590 | |
591 | /* |
592 | * If illegal request is from CCPLEX(id:0x1) master then call WARN() |
593 | */ |
594 | if (priv->fabric->off_mask_erd) { |
595 | mstr_id = FIELD_GET(USRBITS_MSTR_ID, priv->mn_user_bits); |
596 | if (mstr_id == CCPLEX_MSTRID) |
597 | is_inband_err = 1; |
598 | } |
599 | } |
600 | } |
601 | |
602 | unlock: |
603 | spin_unlock_irqrestore(lock: &cbb_lock, flags); |
604 | WARN_ON(is_inband_err); |
605 | return IRQ_HANDLED; |
606 | } |
607 | |
608 | /* |
609 | * Register handler for CBB_SECURE interrupt for reporting errors |
610 | */ |
611 | static int tegra234_cbb_interrupt_enable(struct tegra_cbb *cbb) |
612 | { |
613 | struct tegra234_cbb *priv = to_tegra234_cbb(cbb); |
614 | |
615 | if (priv->sec_irq) { |
616 | int err = devm_request_irq(dev: cbb->dev, irq: priv->sec_irq, handler: tegra234_cbb_isr, irqflags: 0, |
617 | devname: dev_name(dev: cbb->dev), dev_id: priv); |
618 | if (err) { |
619 | dev_err(cbb->dev, "failed to register interrupt %u: %d\n", priv->sec_irq, |
620 | err); |
621 | return err; |
622 | } |
623 | } |
624 | |
625 | return 0; |
626 | } |
627 | |
628 | static void tegra234_cbb_error_enable(struct tegra_cbb *cbb) |
629 | { |
630 | tegra_cbb_fault_enable(cbb); |
631 | } |
632 | |
633 | static const struct tegra_cbb_ops tegra234_cbb_ops = { |
634 | .get_status = tegra234_cbb_get_status, |
635 | .error_clear = tegra234_cbb_error_clear, |
636 | .fault_enable = tegra234_cbb_fault_enable, |
637 | .error_enable = tegra234_cbb_error_enable, |
638 | .interrupt_enable = tegra234_cbb_interrupt_enable, |
639 | #ifdef CONFIG_DEBUG_FS |
640 | .debugfs_show = tegra234_cbb_debugfs_show, |
641 | #endif |
642 | }; |
643 | |
644 | static const char * const tegra234_master_id[] = { |
645 | [0x00] = "TZ", |
646 | [0x01] = "CCPLEX", |
647 | [0x02] = "CCPMU", |
648 | [0x03] = "BPMP_FW", |
649 | [0x04] = "AON", |
650 | [0x05] = "SCE", |
651 | [0x06] = "GPCDMA_P", |
652 | [0x07] = "TSECA_NONSECURE", |
653 | [0x08] = "TSECA_LIGHTSECURE", |
654 | [0x09] = "TSECA_HEAVYSECURE", |
655 | [0x0a] = "CORESIGHT", |
656 | [0x0b] = "APE", |
657 | [0x0c] = "PEATRANS", |
658 | [0x0d] = "JTAGM_DFT", |
659 | [0x0e] = "RCE", |
660 | [0x0f] = "DCE", |
661 | [0x10] = "PSC_FW_USER", |
662 | [0x11] = "PSC_FW_SUPERVISOR", |
663 | [0x12] = "PSC_FW_MACHINE", |
664 | [0x13] = "PSC_BOOT", |
665 | [0x14] = "BPMP_BOOT", |
666 | [0x15] = "NVDEC_NONSECURE", |
667 | [0x16] = "NVDEC_LIGHTSECURE", |
668 | [0x17] = "NVDEC_HEAVYSECURE", |
669 | [0x18] = "CBB_INTERNAL", |
670 | [0x19] = "RSVD" |
671 | }; |
672 | |
673 | static const struct tegra_cbb_error tegra234_cbb_errors[] = { |
674 | { |
675 | .code = "SLAVE_ERR", |
676 | .desc = "Slave being accessed responded with an error" |
677 | }, { |
678 | .code = "DECODE_ERR", |
679 | .desc = "Attempt to access an address hole" |
680 | }, { |
681 | .code = "FIREWALL_ERR", |
682 | .desc = "Attempt to access a region which is firewall protected" |
683 | }, { |
684 | .code = "TIMEOUT_ERR", |
685 | .desc = "No response returned by slave" |
686 | }, { |
687 | .code = "PWRDOWN_ERR", |
688 | .desc = "Attempt to access a portion of fabric that is powered down" |
689 | }, { |
690 | .code = "UNSUPPORTED_ERR", |
691 | .desc = "Attempt to access a slave through an unsupported access" |
692 | } |
693 | }; |
694 | |
695 | static const struct tegra234_slave_lookup tegra234_aon_slave_map[] = { |
696 | { "AXI2APB", 0x00000 }, |
697 | { "AST", 0x14000 }, |
698 | { "CBB", 0x15000 }, |
699 | { "CPU", 0x16000 }, |
700 | }; |
701 | |
702 | static const struct tegra234_cbb_fabric tegra234_aon_fabric = { |
703 | .name = "aon-fabric", |
704 | .master_id = tegra234_master_id, |
705 | .slave_map = tegra234_aon_slave_map, |
706 | .max_slaves = ARRAY_SIZE(tegra234_aon_slave_map), |
707 | .errors = tegra234_cbb_errors, |
708 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
709 | .notifier_offset = 0x17000, |
710 | .firewall_base = 0x30000, |
711 | .firewall_ctl = 0x8d0, |
712 | .firewall_wr_ctl = 0x8c8, |
713 | }; |
714 | |
715 | static const struct tegra234_slave_lookup tegra234_bpmp_slave_map[] = { |
716 | { "AXI2APB", 0x00000 }, |
717 | { "AST0", 0x15000 }, |
718 | { "AST1", 0x16000 }, |
719 | { "CBB", 0x17000 }, |
720 | { "CPU", 0x18000 }, |
721 | }; |
722 | |
723 | static const struct tegra234_cbb_fabric tegra234_bpmp_fabric = { |
724 | .name = "bpmp-fabric", |
725 | .master_id = tegra234_master_id, |
726 | .slave_map = tegra234_bpmp_slave_map, |
727 | .max_slaves = ARRAY_SIZE(tegra234_bpmp_slave_map), |
728 | .errors = tegra234_cbb_errors, |
729 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
730 | .notifier_offset = 0x19000, |
731 | .firewall_base = 0x30000, |
732 | .firewall_ctl = 0x8f0, |
733 | .firewall_wr_ctl = 0x8e8, |
734 | }; |
735 | |
736 | static const struct tegra234_slave_lookup tegra234_cbb_slave_map[] = { |
737 | { "AON", 0x40000 }, |
738 | { "BPMP", 0x41000 }, |
739 | { "CBB", 0x42000 }, |
740 | { "HOST1X", 0x43000 }, |
741 | { "STM", 0x44000 }, |
742 | { "FSI", 0x45000 }, |
743 | { "PSC", 0x46000 }, |
744 | { "PCIE_C1", 0x47000 }, |
745 | { "PCIE_C2", 0x48000 }, |
746 | { "PCIE_C3", 0x49000 }, |
747 | { "PCIE_C0", 0x4a000 }, |
748 | { "PCIE_C4", 0x4b000 }, |
749 | { "GPU", 0x4c000 }, |
750 | { "SMMU0", 0x4d000 }, |
751 | { "SMMU1", 0x4e000 }, |
752 | { "SMMU2", 0x4f000 }, |
753 | { "SMMU3", 0x50000 }, |
754 | { "SMMU4", 0x51000 }, |
755 | { "PCIE_C10", 0x52000 }, |
756 | { "PCIE_C7", 0x53000 }, |
757 | { "PCIE_C8", 0x54000 }, |
758 | { "PCIE_C9", 0x55000 }, |
759 | { "PCIE_C5", 0x56000 }, |
760 | { "PCIE_C6", 0x57000 }, |
761 | { "DCE", 0x58000 }, |
762 | { "RCE", 0x59000 }, |
763 | { "SCE", 0x5a000 }, |
764 | { "AXI2APB_1", 0x70000 }, |
765 | { "AXI2APB_10", 0x71000 }, |
766 | { "AXI2APB_11", 0x72000 }, |
767 | { "AXI2APB_12", 0x73000 }, |
768 | { "AXI2APB_13", 0x74000 }, |
769 | { "AXI2APB_14", 0x75000 }, |
770 | { "AXI2APB_15", 0x76000 }, |
771 | { "AXI2APB_16", 0x77000 }, |
772 | { "AXI2APB_17", 0x78000 }, |
773 | { "AXI2APB_18", 0x79000 }, |
774 | { "AXI2APB_19", 0x7a000 }, |
775 | { "AXI2APB_2", 0x7b000 }, |
776 | { "AXI2APB_20", 0x7c000 }, |
777 | { "AXI2APB_21", 0x7d000 }, |
778 | { "AXI2APB_22", 0x7e000 }, |
779 | { "AXI2APB_23", 0x7f000 }, |
780 | { "AXI2APB_25", 0x80000 }, |
781 | { "AXI2APB_26", 0x81000 }, |
782 | { "AXI2APB_27", 0x82000 }, |
783 | { "AXI2APB_28", 0x83000 }, |
784 | { "AXI2APB_29", 0x84000 }, |
785 | { "AXI2APB_30", 0x85000 }, |
786 | { "AXI2APB_31", 0x86000 }, |
787 | { "AXI2APB_32", 0x87000 }, |
788 | { "AXI2APB_33", 0x88000 }, |
789 | { "AXI2APB_34", 0x89000 }, |
790 | { "AXI2APB_35", 0x92000 }, |
791 | { "AXI2APB_4", 0x8b000 }, |
792 | { "AXI2APB_5", 0x8c000 }, |
793 | { "AXI2APB_6", 0x8d000 }, |
794 | { "AXI2APB_7", 0x8e000 }, |
795 | { "AXI2APB_8", 0x8f000 }, |
796 | { "AXI2APB_9", 0x90000 }, |
797 | { "AXI2APB_3", 0x91000 }, |
798 | }; |
799 | |
800 | static const struct tegra234_cbb_fabric tegra234_cbb_fabric = { |
801 | .name = "cbb-fabric", |
802 | .master_id = tegra234_master_id, |
803 | .slave_map = tegra234_cbb_slave_map, |
804 | .max_slaves = ARRAY_SIZE(tegra234_cbb_slave_map), |
805 | .errors = tegra234_cbb_errors, |
806 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
807 | .notifier_offset = 0x60000, |
808 | .off_mask_erd = 0x3a004, |
809 | .firewall_base = 0x10000, |
810 | .firewall_ctl = 0x23f0, |
811 | .firewall_wr_ctl = 0x23e8, |
812 | }; |
813 | |
814 | static const struct tegra234_slave_lookup tegra234_common_slave_map[] = { |
815 | { "AXI2APB", 0x00000 }, |
816 | { "AST0", 0x15000 }, |
817 | { "AST1", 0x16000 }, |
818 | { "CBB", 0x17000 }, |
819 | { "RSVD", 0x00000 }, |
820 | { "CPU", 0x18000 }, |
821 | }; |
822 | |
823 | static const struct tegra234_cbb_fabric tegra234_dce_fabric = { |
824 | .name = "dce-fabric", |
825 | .master_id = tegra234_master_id, |
826 | .slave_map = tegra234_common_slave_map, |
827 | .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), |
828 | .errors = tegra234_cbb_errors, |
829 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
830 | .notifier_offset = 0x19000, |
831 | .firewall_base = 0x30000, |
832 | .firewall_ctl = 0x290, |
833 | .firewall_wr_ctl = 0x288, |
834 | }; |
835 | |
836 | static const struct tegra234_cbb_fabric tegra234_rce_fabric = { |
837 | .name = "rce-fabric", |
838 | .master_id = tegra234_master_id, |
839 | .slave_map = tegra234_common_slave_map, |
840 | .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), |
841 | .errors = tegra234_cbb_errors, |
842 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
843 | .notifier_offset = 0x19000, |
844 | .firewall_base = 0x30000, |
845 | .firewall_ctl = 0x290, |
846 | .firewall_wr_ctl = 0x288, |
847 | }; |
848 | |
849 | static const struct tegra234_cbb_fabric tegra234_sce_fabric = { |
850 | .name = "sce-fabric", |
851 | .master_id = tegra234_master_id, |
852 | .slave_map = tegra234_common_slave_map, |
853 | .max_slaves = ARRAY_SIZE(tegra234_common_slave_map), |
854 | .errors = tegra234_cbb_errors, |
855 | .max_errors = ARRAY_SIZE(tegra234_cbb_errors), |
856 | .notifier_offset = 0x19000, |
857 | .firewall_base = 0x30000, |
858 | .firewall_ctl = 0x290, |
859 | .firewall_wr_ctl = 0x288, |
860 | }; |
861 | |
862 | static const char * const tegra241_master_id[] = { |
863 | [0x0] = "TZ", |
864 | [0x1] = "CCPLEX", |
865 | [0x2] = "CCPMU", |
866 | [0x3] = "BPMP_FW", |
867 | [0x4] = "PSC_FW_USER", |
868 | [0x5] = "PSC_FW_SUPERVISOR", |
869 | [0x6] = "PSC_FW_MACHINE", |
870 | [0x7] = "PSC_BOOT", |
871 | [0x8] = "BPMP_BOOT", |
872 | [0x9] = "JTAGM_DFT", |
873 | [0xa] = "CORESIGHT", |
874 | [0xb] = "GPU", |
875 | [0xc] = "PEATRANS", |
876 | [0xd ... 0x3f] = "RSVD" |
877 | }; |
878 | |
879 | /* |
880 | * Possible causes for Slave and Timeout errors. |
881 | * SLAVE_ERR: |
882 | * Slave being accessed responded with an error. Slave could return |
883 | * an error for various cases : |
884 | * Unsupported access, clamp setting when power gated, register |
885 | * level firewall(SCR), address hole within the slave, etc |
886 | * |
887 | * TIMEOUT_ERR: |
888 | * No response returned by slave. Can be due to slave being clock |
889 | * gated, under reset, powered down or slave inability to respond |
890 | * for an internal slave issue |
891 | */ |
892 | static const struct tegra_cbb_error tegra241_cbb_errors[] = { |
893 | { |
894 | .code = "SLAVE_ERR", |
895 | .desc = "Slave being accessed responded with an error." |
896 | }, { |
897 | .code = "DECODE_ERR", |
898 | .desc = "Attempt to access an address hole or Reserved region of memory." |
899 | }, { |
900 | .code = "FIREWALL_ERR", |
901 | .desc = "Attempt to access a region which is firewalled." |
902 | }, { |
903 | .code = "TIMEOUT_ERR", |
904 | .desc = "No response returned by slave." |
905 | }, { |
906 | .code = "PWRDOWN_ERR", |
907 | .desc = "Attempt to access a portion of the fabric that is powered down." |
908 | }, { |
909 | .code = "UNSUPPORTED_ERR", |
910 | .desc = "Attempt to access a slave through an unsupported access." |
911 | }, { |
912 | .code = "POISON_ERR", |
913 | .desc = "Slave responds with poison error to indicate error in data." |
914 | }, { |
915 | .code = "RSVD" |
916 | }, { |
917 | .code = "RSVD" |
918 | }, { |
919 | .code = "RSVD" |
920 | }, { |
921 | .code = "RSVD" |
922 | }, { |
923 | .code = "RSVD" |
924 | }, { |
925 | .code = "RSVD" |
926 | }, { |
927 | .code = "RSVD" |
928 | }, { |
929 | .code = "RSVD" |
930 | }, { |
931 | .code = "RSVD" |
932 | }, { |
933 | .code = "NO_SUCH_ADDRESS_ERR", |
934 | .desc = "The address belongs to the pri_target range but there is no register " |
935 | "implemented at the address." |
936 | }, { |
937 | .code = "TASK_ERR", |
938 | .desc = "Attempt to update a PRI task when the current task has still not " |
939 | "completed." |
940 | }, { |
941 | .code = "EXTERNAL_ERR", |
942 | .desc = "Indicates that an external PRI register access met with an error due to " |
943 | "any issue in the unit." |
944 | }, { |
945 | .code = "INDEX_ERR", |
946 | .desc = "Applicable to PRI index aperture pair, when the programmed index is " |
947 | "outside the range defined in the manual." |
948 | }, { |
949 | .code = "RESET_ERR", |
950 | .desc = "Target in Reset Error: Attempt to access a SubPri or external PRI " |
951 | "register but they are in reset." |
952 | }, { |
953 | .code = "REGISTER_RST_ERR", |
954 | .desc = "Attempt to access a PRI register but the register is partial or " |
955 | "completely in reset." |
956 | }, { |
957 | .code = "POWER_GATED_ERR", |
958 | .desc = "Returned by external PRI client when the external access goes to a power " |
959 | "gated domain." |
960 | }, { |
961 | .code = "SUBPRI_FS_ERR", |
962 | .desc = "Subpri is floorswept: Attempt to access a subpri through the main pri " |
963 | "target but subPri logic is floorswept." |
964 | }, { |
965 | .code = "SUBPRI_CLK_OFF_ERR", |
966 | .desc = "Subpri clock is off: Attempt to access a subpri through the main pri " |
967 | "target but subPris clock is gated/off." |
968 | }, |
969 | }; |
970 | |
971 | static const struct tegra234_slave_lookup tegra241_cbb_slave_map[] = { |
972 | { "RSVD", 0x00000 }, |
973 | { "PCIE_C8", 0x51000 }, |
974 | { "PCIE_C9", 0x52000 }, |
975 | { "RSVD", 0x00000 }, |
976 | { "RSVD", 0x00000 }, |
977 | { "RSVD", 0x00000 }, |
978 | { "RSVD", 0x00000 }, |
979 | { "RSVD", 0x00000 }, |
980 | { "RSVD", 0x00000 }, |
981 | { "RSVD", 0x00000 }, |
982 | { "RSVD", 0x00000 }, |
983 | { "AON", 0x5b000 }, |
984 | { "BPMP", 0x5c000 }, |
985 | { "RSVD", 0x00000 }, |
986 | { "RSVD", 0x00000 }, |
987 | { "PSC", 0x5d000 }, |
988 | { "STM", 0x5e000 }, |
989 | { "AXI2APB_1", 0x70000 }, |
990 | { "AXI2APB_10", 0x71000 }, |
991 | { "AXI2APB_11", 0x72000 }, |
992 | { "AXI2APB_12", 0x73000 }, |
993 | { "AXI2APB_13", 0x74000 }, |
994 | { "AXI2APB_14", 0x75000 }, |
995 | { "AXI2APB_15", 0x76000 }, |
996 | { "AXI2APB_16", 0x77000 }, |
997 | { "AXI2APB_17", 0x78000 }, |
998 | { "AXI2APB_18", 0x79000 }, |
999 | { "AXI2APB_19", 0x7a000 }, |
1000 | { "AXI2APB_2", 0x7b000 }, |
1001 | { "AXI2APB_20", 0x7c000 }, |
1002 | { "AXI2APB_4", 0x87000 }, |
1003 | { "AXI2APB_5", 0x88000 }, |
1004 | { "AXI2APB_6", 0x89000 }, |
1005 | { "AXI2APB_7", 0x8a000 }, |
1006 | { "AXI2APB_8", 0x8b000 }, |
1007 | { "AXI2APB_9", 0x8c000 }, |
1008 | { "AXI2APB_3", 0x8d000 }, |
1009 | { "AXI2APB_21", 0x7d000 }, |
1010 | { "AXI2APB_22", 0x7e000 }, |
1011 | { "AXI2APB_23", 0x7f000 }, |
1012 | { "AXI2APB_24", 0x80000 }, |
1013 | { "AXI2APB_25", 0x81000 }, |
1014 | { "AXI2APB_26", 0x82000 }, |
1015 | { "AXI2APB_27", 0x83000 }, |
1016 | { "AXI2APB_28", 0x84000 }, |
1017 | { "PCIE_C4", 0x53000 }, |
1018 | { "PCIE_C5", 0x54000 }, |
1019 | { "PCIE_C6", 0x55000 }, |
1020 | { "PCIE_C7", 0x56000 }, |
1021 | { "PCIE_C2", 0x57000 }, |
1022 | { "PCIE_C3", 0x58000 }, |
1023 | { "PCIE_C0", 0x59000 }, |
1024 | { "PCIE_C1", 0x5a000 }, |
1025 | { "CCPLEX", 0x50000 }, |
1026 | { "AXI2APB_29", 0x85000 }, |
1027 | { "AXI2APB_30", 0x86000 }, |
1028 | { "CBB_CENTRAL", 0x00000 }, |
1029 | { "AXI2APB_31", 0x8E000 }, |
1030 | { "AXI2APB_32", 0x8F000 }, |
1031 | }; |
1032 | |
1033 | static const struct tegra234_cbb_fabric tegra241_cbb_fabric = { |
1034 | .name = "cbb-fabric", |
1035 | .master_id = tegra241_master_id, |
1036 | .slave_map = tegra241_cbb_slave_map, |
1037 | .max_slaves = ARRAY_SIZE(tegra241_cbb_slave_map), |
1038 | .errors = tegra241_cbb_errors, |
1039 | .max_errors = ARRAY_SIZE(tegra241_cbb_errors), |
1040 | .notifier_offset = 0x60000, |
1041 | .off_mask_erd = 0x40004, |
1042 | .firewall_base = 0x20000, |
1043 | .firewall_ctl = 0x2370, |
1044 | .firewall_wr_ctl = 0x2368, |
1045 | }; |
1046 | |
1047 | static const struct tegra234_slave_lookup tegra241_bpmp_slave_map[] = { |
1048 | { "RSVD", 0x00000 }, |
1049 | { "RSVD", 0x00000 }, |
1050 | { "RSVD", 0x00000 }, |
1051 | { "CBB", 0x15000 }, |
1052 | { "CPU", 0x16000 }, |
1053 | { "AXI2APB", 0x00000 }, |
1054 | { "DBB0", 0x17000 }, |
1055 | { "DBB1", 0x18000 }, |
1056 | }; |
1057 | |
1058 | static const struct tegra234_cbb_fabric tegra241_bpmp_fabric = { |
1059 | .name = "bpmp-fabric", |
1060 | .master_id = tegra241_master_id, |
1061 | .slave_map = tegra241_bpmp_slave_map, |
1062 | .max_slaves = ARRAY_SIZE(tegra241_bpmp_slave_map), |
1063 | .errors = tegra241_cbb_errors, |
1064 | .max_errors = ARRAY_SIZE(tegra241_cbb_errors), |
1065 | .notifier_offset = 0x19000, |
1066 | .firewall_base = 0x30000, |
1067 | .firewall_ctl = 0x8f0, |
1068 | .firewall_wr_ctl = 0x8e8, |
1069 | }; |
1070 | |
1071 | static const struct of_device_id tegra234_cbb_dt_ids[] = { |
1072 | { .compatible = "nvidia,tegra234-cbb-fabric", .data = &tegra234_cbb_fabric }, |
1073 | { .compatible = "nvidia,tegra234-aon-fabric", .data = &tegra234_aon_fabric }, |
1074 | { .compatible = "nvidia,tegra234-bpmp-fabric", .data = &tegra234_bpmp_fabric }, |
1075 | { .compatible = "nvidia,tegra234-dce-fabric", .data = &tegra234_dce_fabric }, |
1076 | { .compatible = "nvidia,tegra234-rce-fabric", .data = &tegra234_rce_fabric }, |
1077 | { .compatible = "nvidia,tegra234-sce-fabric", .data = &tegra234_sce_fabric }, |
1078 | { /* sentinel */ }, |
1079 | }; |
1080 | MODULE_DEVICE_TABLE(of, tegra234_cbb_dt_ids); |
1081 | |
1082 | struct tegra234_cbb_acpi_uid { |
1083 | const char *hid; |
1084 | const char *uid; |
1085 | const struct tegra234_cbb_fabric *fabric; |
1086 | }; |
1087 | |
1088 | static const struct tegra234_cbb_acpi_uid tegra234_cbb_acpi_uids[] = { |
1089 | { "NVDA1070", "1", &tegra241_cbb_fabric }, |
1090 | { "NVDA1070", "2", &tegra241_bpmp_fabric }, |
1091 | { }, |
1092 | }; |
1093 | |
1094 | static const struct |
1095 | tegra234_cbb_fabric *tegra234_cbb_acpi_get_fabric(struct acpi_device *adev) |
1096 | { |
1097 | const struct tegra234_cbb_acpi_uid *entry; |
1098 | |
1099 | for (entry = tegra234_cbb_acpi_uids; entry->hid; entry++) { |
1100 | if (acpi_dev_hid_uid_match(adev, entry->hid, entry->uid)) |
1101 | return entry->fabric; |
1102 | } |
1103 | |
1104 | return NULL; |
1105 | } |
1106 | |
1107 | static const struct acpi_device_id tegra241_cbb_acpi_ids[] = { |
1108 | { "NVDA1070"}, |
1109 | { }, |
1110 | }; |
1111 | MODULE_DEVICE_TABLE(acpi, tegra241_cbb_acpi_ids); |
1112 | |
1113 | static int tegra234_cbb_probe(struct platform_device *pdev) |
1114 | { |
1115 | const struct tegra234_cbb_fabric *fabric; |
1116 | struct tegra234_cbb *cbb; |
1117 | unsigned long flags = 0; |
1118 | int err; |
1119 | |
1120 | if (pdev->dev.of_node) { |
1121 | fabric = of_device_get_match_data(dev: &pdev->dev); |
1122 | } else { |
1123 | struct acpi_device *device = ACPI_COMPANION(&pdev->dev); |
1124 | if (!device) |
1125 | return -ENODEV; |
1126 | |
1127 | fabric = tegra234_cbb_acpi_get_fabric(adev: device); |
1128 | if (!fabric) { |
1129 | dev_err(&pdev->dev, "no device match found\n"); |
1130 | return -ENODEV; |
1131 | } |
1132 | } |
1133 | |
1134 | cbb = devm_kzalloc(dev: &pdev->dev, size: sizeof(*cbb), GFP_KERNEL); |
1135 | if (!cbb) |
1136 | return -ENOMEM; |
1137 | |
1138 | INIT_LIST_HEAD(list: &cbb->base.node); |
1139 | cbb->base.ops = &tegra234_cbb_ops; |
1140 | cbb->base.dev = &pdev->dev; |
1141 | cbb->fabric = fabric; |
1142 | |
1143 | cbb->regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &cbb->res); |
1144 | if (IS_ERR(ptr: cbb->regs)) |
1145 | return PTR_ERR(ptr: cbb->regs); |
1146 | |
1147 | err = tegra_cbb_get_irq(pdev, NULL, sec_irq: &cbb->sec_irq); |
1148 | if (err) |
1149 | return err; |
1150 | |
1151 | platform_set_drvdata(pdev, data: cbb); |
1152 | |
1153 | /* |
1154 | * Don't enable error reporting for a Fabric if write to it's registers |
1155 | * is blocked by CBB firewall. |
1156 | */ |
1157 | if (!tegra234_cbb_write_access_allowed(pdev, cbb)) { |
1158 | dev_info(&pdev->dev, "error reporting not enabled due to firewall\n"); |
1159 | return 0; |
1160 | } |
1161 | |
1162 | spin_lock_irqsave(&cbb_lock, flags); |
1163 | list_add(new: &cbb->base.node, head: &cbb_list); |
1164 | spin_unlock_irqrestore(lock: &cbb_lock, flags); |
1165 | |
1166 | /* set ERD bit to mask SError and generate interrupt to report error */ |
1167 | if (cbb->fabric->off_mask_erd) |
1168 | tegra234_cbb_mask_serror(cbb); |
1169 | |
1170 | return tegra_cbb_register(cbb: &cbb->base); |
1171 | } |
1172 | |
1173 | static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev) |
1174 | { |
1175 | struct tegra234_cbb *cbb = dev_get_drvdata(dev); |
1176 | |
1177 | tegra234_cbb_error_enable(cbb: &cbb->base); |
1178 | |
1179 | dev_dbg(dev, "%s resumed\n", cbb->fabric->name); |
1180 | |
1181 | return 0; |
1182 | } |
1183 | |
1184 | static const struct dev_pm_ops tegra234_cbb_pm = { |
1185 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, tegra234_cbb_resume_noirq) |
1186 | }; |
1187 | |
1188 | static struct platform_driver tegra234_cbb_driver = { |
1189 | .probe = tegra234_cbb_probe, |
1190 | .driver = { |
1191 | .name = "tegra234-cbb", |
1192 | .of_match_table = tegra234_cbb_dt_ids, |
1193 | .acpi_match_table = tegra241_cbb_acpi_ids, |
1194 | .pm = &tegra234_cbb_pm, |
1195 | }, |
1196 | }; |
1197 | |
1198 | static int __init tegra234_cbb_init(void) |
1199 | { |
1200 | return platform_driver_register(&tegra234_cbb_driver); |
1201 | } |
1202 | pure_initcall(tegra234_cbb_init); |
1203 | |
1204 | static void __exit tegra234_cbb_exit(void) |
1205 | { |
1206 | platform_driver_unregister(&tegra234_cbb_driver); |
1207 | } |
1208 | module_exit(tegra234_cbb_exit); |
1209 | |
1210 | MODULE_DESCRIPTION("Control Backbone 2.0 error handling driver for Tegra234"); |
1211 |
Definitions
- tegra234_cbb_fabric_ids
- tegra234_slave_lookup
- tegra234_cbb_fabric
- tegra234_cbb
- to_tegra234_cbb
- cbb_list
- cbb_lock
- tegra234_cbb_write_access_allowed
- tegra234_cbb_fault_enable
- tegra234_cbb_error_clear
- tegra234_cbb_get_status
- tegra234_cbb_mask_serror
- tegra234_cbb_get_tmo_slv
- tegra234_cbb_tmo_slv
- tegra234_cbb_lookup_apbslv
- tegra234_lookup_slave_timeout
- tegra234_cbb_print_error
- print_errlog_err
- print_errmonX_info
- print_err_notifier
- cbb_debugfs_mutex
- tegra234_cbb_debugfs_show
- tegra234_cbb_isr
- tegra234_cbb_interrupt_enable
- tegra234_cbb_error_enable
- tegra234_cbb_ops
- tegra234_master_id
- tegra234_cbb_errors
- tegra234_aon_slave_map
- tegra234_aon_fabric
- tegra234_bpmp_slave_map
- tegra234_bpmp_fabric
- tegra234_cbb_slave_map
- tegra234_cbb_fabric
- tegra234_common_slave_map
- tegra234_dce_fabric
- tegra234_rce_fabric
- tegra234_sce_fabric
- tegra241_master_id
- tegra241_cbb_errors
- tegra241_cbb_slave_map
- tegra241_cbb_fabric
- tegra241_bpmp_slave_map
- tegra241_bpmp_fabric
- tegra234_cbb_dt_ids
- tegra234_cbb_acpi_uid
- tegra234_cbb_acpi_uids
- tegra234_cbb_acpi_get_fabric
- tegra241_cbb_acpi_ids
- tegra234_cbb_probe
- tegra234_cbb_resume_noirq
- tegra234_cbb_pm
- tegra234_cbb_driver
- tegra234_cbb_init
Improve your Profiling and Debugging skills
Find out more