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
cxl_dport_map_rch_aer(struct cxl_dport * dport)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
cxl_disable_rch_root_ints(struct cxl_dport * dport)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 */
cxl_rch_get_aer_info(void __iomem * aer_base,struct aer_capability_regs * aer_regs)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. */
cxl_rch_get_aer_severity(struct aer_capability_regs * aer_regs,int * severity)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
cxl_handle_rdport_errors(struct cxl_dev_state * cxlds)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