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