summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorDave Jiang <dave.jiang@intel.com>2024-05-02 19:57:33 +0300
committerBjorn Helgaas <bhelgaas@google.com>2024-05-08 21:25:43 +0300
commit53c49b6e6dd2ebc1d3257ae838e067699229bc8d (patch)
tree38fef0ebcfe00b0a7043689b7798b5f2d44478ba /drivers/pci
parentb1956e2d0713e210a56ae65ad3488ae36f833e76 (diff)
downloadlinux-53c49b6e6dd2ebc1d3257ae838e067699229bc8d.tar.xz
PCI/CXL: Add 'cxl_bus' reset method for devices below CXL Ports
By default Secondary Bus Reset (SBR) is masked for CXL Ports (see CXL r3.1, sec 8.1.5.2). Add cxl_reset_bus_function() (method "cxl_bus") to set the "Unmask SBR" bit in the upstream CXL Port before performing the bus reset and restore the original value afterwards. This method allows the user to perform a bus reset on a CXL device without needing to set the "Unmask SBR" bit via a user tool. Link: https://lore.kernel.org/r/20240502165851.1948523-5-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com> [bhelgaas: simplify commit log, invert condition to avoid negation] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c39
1 files changed, 39 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 225cec964f25..13605261a881 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4980,6 +4980,44 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
return pci_parent_bus_reset(dev, probe);
}
+static int cxl_reset_bus_function(struct pci_dev *dev, bool probe)
+{
+ struct pci_dev *bridge;
+ u16 dvsec, reg, val;
+ int rc;
+
+ bridge = pci_upstream_bridge(dev);
+ if (!bridge)
+ return -ENOTTY;
+
+ dvsec = cxl_port_dvsec(bridge);
+ if (!dvsec)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ rc = pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, &reg);
+ if (rc)
+ return -ENOTTY;
+
+ if (reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR) {
+ val = reg;
+ } else {
+ val = reg | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR;
+ pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+ val);
+ }
+
+ rc = pci_reset_bus_function(dev, probe);
+
+ if (reg != val)
+ pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL,
+ reg);
+
+ return rc;
+}
+
void pci_dev_lock(struct pci_dev *dev)
{
/* block PM suspend, driver probe, etc. */
@@ -5064,6 +5102,7 @@ static const struct pci_reset_fn_method pci_reset_fn_methods[] = {
{ pci_af_flr, .name = "af_flr" },
{ pci_pm_reset, .name = "pm" },
{ pci_reset_bus_function, .name = "bus" },
+ { cxl_reset_bus_function, .name = "cxl_bus" },
};
static ssize_t reset_method_show(struct device *dev,