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
cxl_cper_trace_corr_port_prot_err(struct pci_dev * pdev,struct cxl_ras_capability_regs ras_cap)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
cxl_cper_trace_uncorr_port_prot_err(struct pci_dev * pdev,struct cxl_ras_capability_regs ras_cap)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
cxl_cper_trace_corr_prot_err(struct pci_dev * pdev,struct cxl_ras_capability_regs ras_cap)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
cxl_cper_trace_uncorr_prot_err(struct pci_dev * pdev,struct cxl_ras_capability_regs ras_cap)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
cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data * data)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
cxl_cper_prot_err_work_fn(struct work_struct * work)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
cxl_ras_init(void)110 int cxl_ras_init(void)
111 {
112 return cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
113 }
114
cxl_ras_exit(void)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