xref: /linux/drivers/cxl/core/ras_rch.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
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