1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2025 AMD Corporation. All rights reserved. */ 3 4 #include <linux/pci.h> 5 #include <linux/aer.h> 6 #include <cxl/event.h> 7 #include <cxlmem.h> 8 #include "trace.h" 9 10 static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, 11 struct cxl_ras_capability_regs ras_cap) 12 { 13 u32 status = ras_cap.cor_status & ~ras_cap.cor_mask; 14 15 trace_cxl_port_aer_correctable_error(&pdev->dev, status); 16 } 17 18 static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, 19 struct cxl_ras_capability_regs ras_cap) 20 { 21 u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; 22 u32 fe; 23 24 if (hweight32(status) > 1) 25 fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, 26 ras_cap.cap_control)); 27 else 28 fe = status; 29 30 trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, 31 ras_cap.header_log); 32 } 33 34 static void cxl_cper_trace_corr_prot_err(struct pci_dev *pdev, 35 struct cxl_ras_capability_regs ras_cap) 36 { 37 u32 status = ras_cap.cor_status & ~ras_cap.cor_mask; 38 struct cxl_dev_state *cxlds; 39 40 cxlds = pci_get_drvdata(pdev); 41 if (!cxlds) 42 return; 43 44 trace_cxl_aer_correctable_error(cxlds->cxlmd, status); 45 } 46 47 static void cxl_cper_trace_uncorr_prot_err(struct pci_dev *pdev, 48 struct cxl_ras_capability_regs ras_cap) 49 { 50 u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; 51 struct cxl_dev_state *cxlds; 52 u32 fe; 53 54 cxlds = pci_get_drvdata(pdev); 55 if (!cxlds) 56 return; 57 58 if (hweight32(status) > 1) 59 fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, 60 ras_cap.cap_control)); 61 else 62 fe = status; 63 64 trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, 65 ras_cap.header_log); 66 } 67 68 static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) 69 { 70 unsigned int devfn = PCI_DEVFN(data->prot_err.agent_addr.device, 71 data->prot_err.agent_addr.function); 72 struct pci_dev *pdev __free(pci_dev_put) = 73 pci_get_domain_bus_and_slot(data->prot_err.agent_addr.segment, 74 data->prot_err.agent_addr.bus, 75 devfn); 76 int port_type; 77 78 if (!pdev) 79 return; 80 81 guard(device)(&pdev->dev); 82 83 port_type = pci_pcie_type(pdev); 84 if (port_type == PCI_EXP_TYPE_ROOT_PORT || 85 port_type == PCI_EXP_TYPE_DOWNSTREAM || 86 port_type == PCI_EXP_TYPE_UPSTREAM) { 87 if (data->severity == AER_CORRECTABLE) 88 cxl_cper_trace_corr_port_prot_err(pdev, data->ras_cap); 89 else 90 cxl_cper_trace_uncorr_port_prot_err(pdev, data->ras_cap); 91 92 return; 93 } 94 95 if (data->severity == AER_CORRECTABLE) 96 cxl_cper_trace_corr_prot_err(pdev, data->ras_cap); 97 else 98 cxl_cper_trace_uncorr_prot_err(pdev, data->ras_cap); 99 } 100 101 static void cxl_cper_prot_err_work_fn(struct work_struct *work) 102 { 103 struct cxl_cper_prot_err_work_data wd; 104 105 while (cxl_cper_prot_err_kfifo_get(&wd)) 106 cxl_cper_handle_prot_err(&wd); 107 } 108 static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn); 109 110 int cxl_ras_init(void) 111 { 112 return cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work); 113 } 114 115 void cxl_ras_exit(void) 116 { 117 cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work); 118 cancel_work_sync(&cxl_cper_prot_err_work); 119 } 120