xref: /freebsd/sys/contrib/dev/iwlwifi/pcie/utils.c (revision 6b627f88584ce13118e0a24951b503c0b1f2d5a7)
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 #if defined(__linux__)
51 	print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
52 #elif defined(__FreeBSD__)
53 	iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
54 #endif
55 
56 	IWL_ERR(trans, "iwlwifi device memory mapped registers:\n");
57 	for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++)
58 		*ptr = iwl_read32(trans, i);
59 #if defined(__linux__)
60 	print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
61 #elif defined(__FreeBSD__)
62 	iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
63 #endif
64 
65 	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
66 	if (pos) {
67 		IWL_ERR(trans, "iwlwifi device AER capability structure:\n");
68 		for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++)
69 			if (pci_read_config_dword(pdev, pos + i, ptr))
70 				goto err_read;
71 #if defined(__linux__)
72 		print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
73 			       32, 4, buf, i, 0);
74 #elif defined(__FreeBSD__)
75 		iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
76 #endif
77 	}
78 
79 	/* Print parent device registers next */
80 	if (!pdev->bus->self)
81 		goto out;
82 
83 	pdev = pdev->bus->self;
84 	sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
85 
86 	IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n",
87 		pci_name(pdev));
88 	for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++)
89 		if (pci_read_config_dword(pdev, i, ptr))
90 			goto err_read;
91 #if defined(__linux__)
92 	print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
93 #elif defined(__FreeBSD__)
94 	iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
95 #endif
96 
97 	/* Print root port AER registers */
98 	pos = 0;
99 	pdev = pcie_find_root_port(pdev);
100 	if (pdev)
101 		pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
102 	if (pos) {
103 		IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n",
104 			pci_name(pdev));
105 		sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
106 		for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++)
107 			if (pci_read_config_dword(pdev, pos + i, ptr))
108 				goto err_read;
109 #if defined(__linux__)
110 		print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32,
111 			       4, buf, i, 0);
112 #elif defined(__FreeBSD__)
113 		iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
114 #endif
115 	}
116 	goto out;
117 
118 err_read:
119 #if defined(__linux__)
120 	print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
121 #elif defined(__FreeBSD__)
122 	iwl_print_hex_dump(NULL, IWL_DL_ANY, prefix, (u8 *)buf, i);
123 #endif
124 	IWL_ERR(trans, "Read failed at 0x%X\n", i);
125 out:
126 	pcie_dbg_dumped_once = 1;
127 	kfree(buf);
128 }
129