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 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