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