1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2025 Intel Corporation
4 */
5
6 #include <linux/pci.h>
7 #include <linux/gfp.h>
8
9 #include "iwl-io.h"
10 #include "pcie/utils.h"
11
iwl_trans_pcie_dump_regs(struct iwl_trans * trans,struct pci_dev * pdev)12 void iwl_trans_pcie_dump_regs(struct iwl_trans *trans, struct pci_dev *pdev)
13 {
14 #define PCI_DUMP_SIZE 352
15 #define PCI_MEM_DUMP_SIZE 64
16 #define PCI_PARENT_DUMP_SIZE 524
17 #define PREFIX_LEN 32
18
19 static bool pcie_dbg_dumped_once = 0;
20 u32 i, pos, alloc_size, *ptr, *buf;
21 char *prefix;
22
23 if (pcie_dbg_dumped_once)
24 return;
25
26 /* Should be a multiple of 4 */
27 BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3);
28 BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3);
29 BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3);
30
31 /* Alloc a max size buffer */
32 alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN;
33 alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN);
34 alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN);
35 alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN);
36
37 buf = kmalloc(alloc_size, GFP_ATOMIC);
38 if (!buf)
39 return;
40 prefix = (char *)buf + alloc_size - PREFIX_LEN;
41
42 IWL_ERR(trans, "iwlwifi transaction failed, dumping registers\n");
43
44 /* Print wifi device registers */
45 sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
46 IWL_ERR(trans, "iwlwifi device config registers:\n");
47 for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++)
48 if (pci_read_config_dword(pdev, i, ptr))
49 goto err_read;
50 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
51
52 IWL_ERR(trans, "iwlwifi device memory mapped registers:\n");
53 for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++)
54 *ptr = iwl_read32(trans, i);
55 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
56
57 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
58 if (pos) {
59 IWL_ERR(trans, "iwlwifi device AER capability structure:\n");
60 for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++)
61 if (pci_read_config_dword(pdev, pos + i, ptr))
62 goto err_read;
63 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
64 32, 4, buf, i, 0);
65 }
66
67 /* Print parent device registers next */
68 if (!pdev->bus->self)
69 goto out;
70
71 pdev = pdev->bus->self;
72 sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
73
74 IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n",
75 pci_name(pdev));
76 for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++)
77 if (pci_read_config_dword(pdev, i, ptr))
78 goto err_read;
79 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
80
81 /* Print root port AER registers */
82 pos = 0;
83 pdev = pcie_find_root_port(pdev);
84 if (pdev)
85 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
86 if (pos) {
87 IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n",
88 pci_name(pdev));
89 sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
90 for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++)
91 if (pci_read_config_dword(pdev, pos + i, ptr))
92 goto err_read;
93 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32,
94 4, buf, i, 0);
95 }
96 goto out;
97
98 err_read:
99 print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
100 IWL_ERR(trans, "Read failed at 0x%X\n", i);
101 out:
102 pcie_dbg_dumped_once = 1;
103 kfree(buf);
104 }
105