PCI/CXL: Fail bus reset if upstream CXL Port has SBR masked
Per CXL spec r3.1, sec 8.1.5.2, the Secondary Bus Reset (SBR) bit in the Bridge Control register of a CXL port has no effect unless the "Unmask SBR" bit is set. Return -ENOTTY if we attempt a bus reset on a device below a CXL Port where "Unmask SBR" is 0. Otherwise, the bus reset would appear to have succeeded even though setting the bridge SBR bit had no effect. Link: https://lore.kernel.org/linux-cxl/20240220203956.GA1502351@bhelgaas/ Link: https://lore.kernel.org/r/20240502165851.1948523-4-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com> [bhelgaas: simplify commit log and comments] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> Reviewed-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
7e89efc6e9
commit
b1956e2d07
@ -4928,10 +4928,52 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)
|
||||
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
|
||||
}
|
||||
|
||||
static u16 cxl_port_dvsec(struct pci_dev *dev)
|
||||
{
|
||||
return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_CXL,
|
||||
PCI_DVSEC_CXL_PORT);
|
||||
}
|
||||
|
||||
static bool cxl_sbr_masked(struct pci_dev *dev)
|
||||
{
|
||||
u16 dvsec, reg;
|
||||
int rc;
|
||||
|
||||
dvsec = cxl_port_dvsec(dev);
|
||||
if (!dvsec)
|
||||
return false;
|
||||
|
||||
rc = pci_read_config_word(dev, dvsec + PCI_DVSEC_CXL_PORT_CTL, ®);
|
||||
if (rc || PCI_POSSIBLE_ERROR(reg))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Per CXL spec r3.1, sec 8.1.5.2, when "Unmask SBR" is 0, the SBR
|
||||
* bit in Bridge Control has no effect. When 1, the Port generates
|
||||
* hot reset when the SBR bit is set to 1.
|
||||
*/
|
||||
if (reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
|
||||
{
|
||||
struct pci_dev *bridge = pci_upstream_bridge(dev);
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* If "dev" is below a CXL port that has SBR control masked, SBR
|
||||
* won't do anything, so return error.
|
||||
*/
|
||||
if (bridge && cxl_sbr_masked(bridge)) {
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
rc = pci_dev_reset_slot_function(dev, probe);
|
||||
if (rc != -ENOTTY)
|
||||
return rc;
|
||||
|
@ -1148,4 +1148,9 @@
|
||||
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000
|
||||
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000
|
||||
|
||||
/* Compute Express Link (CXL r3.1, sec 8.1.5) */
|
||||
#define PCI_DVSEC_CXL_PORT 3
|
||||
#define PCI_DVSEC_CXL_PORT_CTL 0x0c
|
||||
#define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001
|
||||
|
||||
#endif /* LINUX_PCI_REGS_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user