159010029STerry Bowman // SPDX-License-Identifier: GPL-2.0-only 259010029STerry Bowman /* Copyright(c) 2023 AMD Corporation. All rights reserved. */ 359010029STerry Bowman 459010029STerry Bowman #include <linux/pci.h> 559010029STerry Bowman #include <linux/aer.h> 659010029STerry Bowman #include <linux/bitfield.h> 759010029STerry Bowman #include "../pci.h" 859010029STerry Bowman #include "portdrv.h" 959010029STerry Bowman 1059010029STerry Bowman static bool is_cxl_mem_dev(struct pci_dev *dev) 1159010029STerry Bowman { 1259010029STerry Bowman /* 1359010029STerry Bowman * The capability, status, and control fields in Device 0, 1459010029STerry Bowman * Function 0 DVSEC control the CXL functionality of the 1559010029STerry Bowman * entire device (CXL 3.0, 8.1.3). 1659010029STerry Bowman */ 1759010029STerry Bowman if (dev->devfn != PCI_DEVFN(0, 0)) 1859010029STerry Bowman return false; 1959010029STerry Bowman 2059010029STerry Bowman /* 2159010029STerry Bowman * CXL Memory Devices must have the 502h class code set (CXL 2259010029STerry Bowman * 3.0, 8.1.12.1). 2359010029STerry Bowman */ 2459010029STerry Bowman if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL) 2559010029STerry Bowman return false; 2659010029STerry Bowman 2759010029STerry Bowman return true; 2859010029STerry Bowman } 2959010029STerry Bowman 3059010029STerry Bowman static bool cxl_error_is_native(struct pci_dev *dev) 3159010029STerry Bowman { 3259010029STerry Bowman struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); 3359010029STerry Bowman 3459010029STerry Bowman return (pcie_ports_native || host->native_aer); 3559010029STerry Bowman } 3659010029STerry Bowman 3759010029STerry Bowman static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data) 3859010029STerry Bowman { 3959010029STerry Bowman struct aer_err_info *info = (struct aer_err_info *)data; 4059010029STerry Bowman const struct pci_error_handlers *err_handler; 4159010029STerry Bowman 4259010029STerry Bowman if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev)) 4359010029STerry Bowman return 0; 4459010029STerry Bowman 45*da71bd36STerry Bowman guard(device)(&dev->dev); 4659010029STerry Bowman 4759010029STerry Bowman err_handler = dev->driver ? dev->driver->err_handler : NULL; 4859010029STerry Bowman if (!err_handler) 49*da71bd36STerry Bowman return 0; 5059010029STerry Bowman 5159010029STerry Bowman if (info->severity == AER_CORRECTABLE) { 5259010029STerry Bowman if (err_handler->cor_error_detected) 5359010029STerry Bowman err_handler->cor_error_detected(dev); 5459010029STerry Bowman } else if (err_handler->error_detected) { 5559010029STerry Bowman if (info->severity == AER_NONFATAL) 5659010029STerry Bowman err_handler->error_detected(dev, pci_channel_io_normal); 5759010029STerry Bowman else if (info->severity == AER_FATAL) 5859010029STerry Bowman err_handler->error_detected(dev, pci_channel_io_frozen); 5959010029STerry Bowman } 6059010029STerry Bowman return 0; 6159010029STerry Bowman } 6259010029STerry Bowman 6359010029STerry Bowman void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info) 6459010029STerry Bowman { 6559010029STerry Bowman /* 6659010029STerry Bowman * Internal errors of an RCEC indicate an AER error in an 6759010029STerry Bowman * RCH's downstream port. Check and handle them in the CXL.mem 6859010029STerry Bowman * device driver. 6959010029STerry Bowman */ 7059010029STerry Bowman if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC && 7159010029STerry Bowman is_aer_internal_error(info)) 7259010029STerry Bowman pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info); 7359010029STerry Bowman } 7459010029STerry Bowman 7559010029STerry Bowman static int handles_cxl_error_iter(struct pci_dev *dev, void *data) 7659010029STerry Bowman { 7759010029STerry Bowman bool *handles_cxl = data; 7859010029STerry Bowman 7959010029STerry Bowman if (!*handles_cxl) 8059010029STerry Bowman *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev); 8159010029STerry Bowman 8259010029STerry Bowman /* Non-zero terminates iteration */ 8359010029STerry Bowman return *handles_cxl; 8459010029STerry Bowman } 8559010029STerry Bowman 8659010029STerry Bowman static bool handles_cxl_errors(struct pci_dev *rcec) 8759010029STerry Bowman { 8859010029STerry Bowman bool handles_cxl = false; 8959010029STerry Bowman 9059010029STerry Bowman if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC && 9159010029STerry Bowman pcie_aer_is_native(rcec)) 9259010029STerry Bowman pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl); 9359010029STerry Bowman 9459010029STerry Bowman return handles_cxl; 9559010029STerry Bowman } 9659010029STerry Bowman 9759010029STerry Bowman void cxl_rch_enable_rcec(struct pci_dev *rcec) 9859010029STerry Bowman { 9959010029STerry Bowman if (!handles_cxl_errors(rcec)) 10059010029STerry Bowman return; 10159010029STerry Bowman 10259010029STerry Bowman pci_aer_unmask_internal_errors(rcec); 10359010029STerry Bowman pci_info(rcec, "CXL: Internal errors unmasked"); 10459010029STerry Bowman } 105