mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
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);
|
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)
|
static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
|
||||||
{
|
{
|
||||||
|
struct pci_dev *bridge = pci_upstream_bridge(dev);
|
||||||
int rc;
|
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);
|
rc = pci_dev_reset_slot_function(dev, probe);
|
||||||
if (rc != -ENOTTY)
|
if (rc != -ENOTTY)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
||||||
|
|
@ -1148,4 +1148,9 @@
|
||||||
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000
|
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000
|
||||||
#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000
|
#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 */
|
#endif /* LINUX_PCI_REGS_H */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user