xref: /linux/drivers/pci/pcie/aer_cxl_rch.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2023 AMD Corporation. All rights reserved. */
3 
4 #include <linux/pci.h>
5 #include <linux/aer.h>
6 #include <linux/bitfield.h>
7 #include "../pci.h"
8 #include "portdrv.h"
9 
10 static bool is_cxl_mem_dev(struct pci_dev *dev)
11 {
12 	/*
13 	 * The capability, status, and control fields in Device 0,
14 	 * Function 0 DVSEC control the CXL functionality of the
15 	 * entire device (CXL 3.0, 8.1.3).
16 	 */
17 	if (dev->devfn != PCI_DEVFN(0, 0))
18 		return false;
19 
20 	/*
21 	 * CXL Memory Devices must have the 502h class code set (CXL
22 	 * 3.0, 8.1.12.1).
23 	 */
24 	if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL)
25 		return false;
26 
27 	return true;
28 }
29 
30 static bool cxl_error_is_native(struct pci_dev *dev)
31 {
32 	struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
33 
34 	return (pcie_ports_native || host->native_aer);
35 }
36 
37 static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
38 {
39 	struct aer_err_info *info = (struct aer_err_info *)data;
40 	const struct pci_error_handlers *err_handler;
41 
42 	if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
43 		return 0;
44 
45 	guard(device)(&dev->dev);
46 
47 	err_handler = dev->driver ? dev->driver->err_handler : NULL;
48 	if (!err_handler)
49 		return 0;
50 
51 	if (info->severity == AER_CORRECTABLE) {
52 		if (err_handler->cor_error_detected)
53 			err_handler->cor_error_detected(dev);
54 	} else if (err_handler->error_detected) {
55 		if (info->severity == AER_NONFATAL)
56 			err_handler->error_detected(dev, pci_channel_io_normal);
57 		else if (info->severity == AER_FATAL)
58 			err_handler->error_detected(dev, pci_channel_io_frozen);
59 	}
60 	return 0;
61 }
62 
63 void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
64 {
65 	/*
66 	 * Internal errors of an RCEC indicate an AER error in an
67 	 * RCH's downstream port. Check and handle them in the CXL.mem
68 	 * device driver.
69 	 */
70 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
71 	    is_aer_internal_error(info))
72 		pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
73 }
74 
75 static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
76 {
77 	bool *handles_cxl = data;
78 
79 	if (!*handles_cxl)
80 		*handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
81 
82 	/* Non-zero terminates iteration */
83 	return *handles_cxl;
84 }
85 
86 static bool handles_cxl_errors(struct pci_dev *rcec)
87 {
88 	bool handles_cxl = false;
89 
90 	if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
91 	    pcie_aer_is_native(rcec))
92 		pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
93 
94 	return handles_cxl;
95 }
96 
97 void cxl_rch_enable_rcec(struct pci_dev *rcec)
98 {
99 	if (!handles_cxl_errors(rcec))
100 		return;
101 
102 	pci_aer_unmask_internal_errors(rcec);
103 	pci_info(rcec, "CXL: Internal errors unmasked");
104 }
105