xref: /linux/drivers/cxl/core/ras_rch.c (revision e812928be2ee1c2744adf20ed04e0ce1e2fc5c13)
10ff60f2eSTerry Bowman // SPDX-License-Identifier: GPL-2.0-only
20ff60f2eSTerry Bowman /* Copyright(c) 2025 AMD Corporation. All rights reserved. */
30ff60f2eSTerry Bowman 
40ff60f2eSTerry Bowman #include <linux/types.h>
50ff60f2eSTerry Bowman #include <linux/aer.h>
60ff60f2eSTerry Bowman #include "cxl.h"
70ff60f2eSTerry Bowman #include "core.h"
80ff60f2eSTerry Bowman #include "cxlmem.h"
90ff60f2eSTerry Bowman 
100ff60f2eSTerry Bowman void cxl_dport_map_rch_aer(struct cxl_dport *dport)
110ff60f2eSTerry Bowman {
120ff60f2eSTerry Bowman 	resource_size_t aer_phys;
130ff60f2eSTerry Bowman 	struct device *host;
140ff60f2eSTerry Bowman 	u16 aer_cap;
150ff60f2eSTerry Bowman 
160ff60f2eSTerry Bowman 	aer_cap = cxl_rcrb_to_aer(dport->dport_dev, dport->rcrb.base);
170ff60f2eSTerry Bowman 	if (aer_cap) {
180ff60f2eSTerry Bowman 		host = dport->reg_map.host;
190ff60f2eSTerry Bowman 		aer_phys = aer_cap + dport->rcrb.base;
200ff60f2eSTerry Bowman 		dport->regs.dport_aer =
210ff60f2eSTerry Bowman 			devm_cxl_iomap_block(host, aer_phys,
220ff60f2eSTerry Bowman 					     sizeof(struct aer_capability_regs));
230ff60f2eSTerry Bowman 	}
240ff60f2eSTerry Bowman }
250ff60f2eSTerry Bowman 
260ff60f2eSTerry Bowman void cxl_disable_rch_root_ints(struct cxl_dport *dport)
270ff60f2eSTerry Bowman {
280ff60f2eSTerry Bowman 	void __iomem *aer_base = dport->regs.dport_aer;
290ff60f2eSTerry Bowman 	u32 aer_cmd_mask, aer_cmd;
300ff60f2eSTerry Bowman 
310ff60f2eSTerry Bowman 	if (!aer_base)
320ff60f2eSTerry Bowman 		return;
330ff60f2eSTerry Bowman 
340ff60f2eSTerry Bowman 	/*
350ff60f2eSTerry Bowman 	 * Disable RCH root port command interrupts.
360ff60f2eSTerry Bowman 	 * CXL 3.0 12.2.1.1 - RCH Downstream Port-detected Errors
370ff60f2eSTerry Bowman 	 *
380ff60f2eSTerry Bowman 	 * This sequence may not be necessary. CXL spec states disabling
390ff60f2eSTerry Bowman 	 * the root cmd register's interrupts is required. But, PCI spec
400ff60f2eSTerry Bowman 	 * shows these are disabled by default on reset.
410ff60f2eSTerry Bowman 	 */
420ff60f2eSTerry Bowman 	aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
430ff60f2eSTerry Bowman 			PCI_ERR_ROOT_CMD_NONFATAL_EN |
440ff60f2eSTerry Bowman 			PCI_ERR_ROOT_CMD_FATAL_EN);
450ff60f2eSTerry Bowman 	aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
460ff60f2eSTerry Bowman 	aer_cmd &= ~aer_cmd_mask;
470ff60f2eSTerry Bowman 	writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
480ff60f2eSTerry Bowman }
490ff60f2eSTerry Bowman 
500ff60f2eSTerry Bowman /*
510ff60f2eSTerry Bowman  * Copy the AER capability registers using 32 bit read accesses.
520ff60f2eSTerry Bowman  * This is necessary because RCRB AER capability is MMIO mapped. Clear the
530ff60f2eSTerry Bowman  * status after copying.
540ff60f2eSTerry Bowman  *
550ff60f2eSTerry Bowman  * @aer_base: base address of AER capability block in RCRB
560ff60f2eSTerry Bowman  * @aer_regs: destination for copying AER capability
570ff60f2eSTerry Bowman  */
580ff60f2eSTerry Bowman static bool cxl_rch_get_aer_info(void __iomem *aer_base,
590ff60f2eSTerry Bowman 				 struct aer_capability_regs *aer_regs)
600ff60f2eSTerry Bowman {
610ff60f2eSTerry Bowman 	int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32);
620ff60f2eSTerry Bowman 	u32 *aer_regs_buf = (u32 *)aer_regs;
630ff60f2eSTerry Bowman 	int n;
640ff60f2eSTerry Bowman 
650ff60f2eSTerry Bowman 	if (!aer_base)
660ff60f2eSTerry Bowman 		return false;
670ff60f2eSTerry Bowman 
680ff60f2eSTerry Bowman 	/* Use readl() to guarantee 32-bit accesses */
690ff60f2eSTerry Bowman 	for (n = 0; n < read_cnt; n++)
700ff60f2eSTerry Bowman 		aer_regs_buf[n] = readl(aer_base + n * sizeof(u32));
710ff60f2eSTerry Bowman 
720ff60f2eSTerry Bowman 	writel(aer_regs->uncor_status, aer_base + PCI_ERR_UNCOR_STATUS);
730ff60f2eSTerry Bowman 	writel(aer_regs->cor_status, aer_base + PCI_ERR_COR_STATUS);
740ff60f2eSTerry Bowman 
750ff60f2eSTerry Bowman 	return true;
760ff60f2eSTerry Bowman }
770ff60f2eSTerry Bowman 
780ff60f2eSTerry Bowman /* Get AER severity. Return false if there is no error. */
790ff60f2eSTerry Bowman static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
800ff60f2eSTerry Bowman 				     int *severity)
810ff60f2eSTerry Bowman {
820ff60f2eSTerry Bowman 	if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
830ff60f2eSTerry Bowman 		if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
840ff60f2eSTerry Bowman 			*severity = AER_FATAL;
850ff60f2eSTerry Bowman 		else
860ff60f2eSTerry Bowman 			*severity = AER_NONFATAL;
870ff60f2eSTerry Bowman 		return true;
880ff60f2eSTerry Bowman 	}
890ff60f2eSTerry Bowman 
900ff60f2eSTerry Bowman 	if (aer_regs->cor_status & ~aer_regs->cor_mask) {
910ff60f2eSTerry Bowman 		*severity = AER_CORRECTABLE;
920ff60f2eSTerry Bowman 		return true;
930ff60f2eSTerry Bowman 	}
940ff60f2eSTerry Bowman 
950ff60f2eSTerry Bowman 	return false;
960ff60f2eSTerry Bowman }
970ff60f2eSTerry Bowman 
980ff60f2eSTerry Bowman void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
990ff60f2eSTerry Bowman {
1000ff60f2eSTerry Bowman 	struct pci_dev *pdev = to_pci_dev(cxlds->dev);
1010ff60f2eSTerry Bowman 	struct aer_capability_regs aer_regs;
1020ff60f2eSTerry Bowman 	struct cxl_dport *dport;
1030ff60f2eSTerry Bowman 	int severity;
1040ff60f2eSTerry Bowman 
1050ff60f2eSTerry Bowman 	struct cxl_port *port __free(put_cxl_port) =
1060ff60f2eSTerry Bowman 		cxl_pci_find_port(pdev, &dport);
1070ff60f2eSTerry Bowman 	if (!port)
1080ff60f2eSTerry Bowman 		return;
1090ff60f2eSTerry Bowman 
1100ff60f2eSTerry Bowman 	if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
1110ff60f2eSTerry Bowman 		return;
1120ff60f2eSTerry Bowman 
1130ff60f2eSTerry Bowman 	if (!cxl_rch_get_aer_severity(&aer_regs, &severity))
1140ff60f2eSTerry Bowman 		return;
1150ff60f2eSTerry Bowman 
1160ff60f2eSTerry Bowman 	pci_print_aer(pdev, severity, &aer_regs);
1170ff60f2eSTerry Bowman 	if (severity == AER_CORRECTABLE)
118*9a8920caSTerry Bowman 		cxl_handle_cor_ras(&cxlds->cxlmd->dev, dport->regs.ras);
1190ff60f2eSTerry Bowman 	else
120*9a8920caSTerry Bowman 		cxl_handle_ras(&cxlds->cxlmd->dev, dport->regs.ras);
1210ff60f2eSTerry Bowman }
122