1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2025 AMD Corporation. All rights reserved. */ 3 4 #include <linux/types.h> 5 #include <linux/aer.h> 6 #include "cxl.h" 7 #include "core.h" 8 #include "cxlmem.h" 9 10 void cxl_dport_map_rch_aer(struct cxl_dport *dport) 11 { 12 resource_size_t aer_phys; 13 struct device *host; 14 u16 aer_cap; 15 16 aer_cap = cxl_rcrb_to_aer(dport->dport_dev, dport->rcrb.base); 17 if (aer_cap) { 18 host = dport->reg_map.host; 19 aer_phys = aer_cap + dport->rcrb.base; 20 dport->regs.dport_aer = 21 devm_cxl_iomap_block(host, aer_phys, 22 sizeof(struct aer_capability_regs)); 23 } 24 } 25 26 void cxl_disable_rch_root_ints(struct cxl_dport *dport) 27 { 28 void __iomem *aer_base = dport->regs.dport_aer; 29 u32 aer_cmd_mask, aer_cmd; 30 31 if (!aer_base) 32 return; 33 34 /* 35 * Disable RCH root port command interrupts. 36 * CXL 3.0 12.2.1.1 - RCH Downstream Port-detected Errors 37 * 38 * This sequence may not be necessary. CXL spec states disabling 39 * the root cmd register's interrupts is required. But, PCI spec 40 * shows these are disabled by default on reset. 41 */ 42 aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN | 43 PCI_ERR_ROOT_CMD_NONFATAL_EN | 44 PCI_ERR_ROOT_CMD_FATAL_EN); 45 aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND); 46 aer_cmd &= ~aer_cmd_mask; 47 writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND); 48 } 49 50 /* 51 * Copy the AER capability registers using 32 bit read accesses. 52 * This is necessary because RCRB AER capability is MMIO mapped. Clear the 53 * status after copying. 54 * 55 * @aer_base: base address of AER capability block in RCRB 56 * @aer_regs: destination for copying AER capability 57 */ 58 static bool cxl_rch_get_aer_info(void __iomem *aer_base, 59 struct aer_capability_regs *aer_regs) 60 { 61 /* 62 * Bound the copy to the physically-defined AER registers (header 63 * through the 16-byte Header Log). struct aer_capability_regs is a 64 * software layout whose embedded struct pcie_tlp_log is larger than 65 * the on-wire AER capability; copying sizeof(*aer_regs) would 66 * over-read the RCRB-mapped MMIO block. 67 */ 68 int read_cnt = (PCI_ERR_HEADER_LOG + 16) / sizeof(u32); 69 u32 *aer_regs_buf = (u32 *)aer_regs; 70 int n; 71 72 if (!aer_base) 73 return false; 74 75 /* 76 * Zero the destination so the software-only tail fields 77 * (e.g. header_log.header_len) are deterministic rather than 78 * left as uninitialized stack, which could drive a bogus loop 79 * length in pcie_print_tlp_log(). 80 */ 81 memset(aer_regs, 0, sizeof(*aer_regs)); 82 83 /* Use readl() to guarantee 32-bit accesses */ 84 for (n = 0; n < read_cnt; n++) 85 aer_regs_buf[n] = readl(aer_base + n * sizeof(u32)); 86 87 writel(aer_regs->uncor_status, aer_base + PCI_ERR_UNCOR_STATUS); 88 writel(aer_regs->cor_status, aer_base + PCI_ERR_COR_STATUS); 89 90 return true; 91 } 92 93 /* Get AER severity. Return false if there is no error. */ 94 static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs, 95 int *severity) 96 { 97 u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask; 98 99 if (uncor_status) { 100 *severity = (uncor_status & aer_regs->uncor_severity) ? 101 AER_FATAL : AER_NONFATAL; 102 return true; 103 } 104 105 if (aer_regs->cor_status & ~aer_regs->cor_mask) { 106 *severity = AER_CORRECTABLE; 107 return true; 108 } 109 110 return false; 111 } 112 113 void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) 114 { 115 struct pci_dev *pdev = to_pci_dev(cxlds->dev); 116 struct aer_capability_regs aer_regs; 117 struct cxl_dport *dport; 118 int severity; 119 120 struct cxl_port *port __free(put_cxl_port) = 121 cxl_pci_find_port(pdev, &dport); 122 if (!port) 123 return; 124 125 if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs)) 126 return; 127 128 if (!cxl_rch_get_aer_severity(&aer_regs, &severity)) 129 return; 130 131 pci_print_aer(pdev, severity, &aer_regs); 132 if (severity == AER_CORRECTABLE) 133 cxl_handle_cor_ras(&cxlds->cxlmd->dev, dport->regs.ras); 134 else 135 cxl_handle_ras(&cxlds->cxlmd->dev, dport->regs.ras); 136 } 137