On Tue, 2 Apr 2024 16:45:31 -0700 Dave Jiang <dave.jiang@xxxxxxxxx> wrote: > CXL spec r3.1 8.1.5.2 > By default Secondary Bus Reset (SBR) is masked for CXL ports. Introduce a > new PCI reset method "cxl_bus" to force SBR on CXL ports by setting > the unmask SBR bit in the CXL DVSEC port control register before performing > the bus reset and restore the original value of the bit post reset. The > new reset method allows the user to intentionally perform SBR on a CXL > device without needing to set the "Unmask SBR" bit via a user tool. > > Signed-off-by: Dave Jiang <dave.jiang@xxxxxxxxx> A few trivial things inline. Otherwise looks fine. FWIW Reviewed-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> > --- > v3: > - move cxl_port_dvsec() to previous patch. (Dan) > - add pci_cfg_access_lock() for the bridge. (Dan) > - Change cxl_bus_force method to cxl_bus. (Dan) > --- > drivers/pci/pci.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/pci.h | 2 +- > 2 files changed, 45 insertions(+), 1 deletion(-) > > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c > index 00eddb451102..3989c8888813 100644 > --- a/drivers/pci/pci.c > +++ b/drivers/pci/pci.c > @@ -4982,6 +4982,49 @@ 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; > + int dvsec; Lukas' comment on previous applies to this as well. > + int rc; > + u16 reg, val; Maybe combine lines as appropriate. > + > + bridge = pci_upstream_bridge(dev); > + if (!bridge) > + return -ENOTTY; > + > + dvsec = cxl_port_dvsec(bridge); > + if (!dvsec) > + return -ENOTTY; > + > + if (probe) > + return 0; > + > + pci_cfg_access_lock(bridge); > + rc = pci_read_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, ®); > + if (rc) { > + rc = -ENOTTY; > + goto out; > + } > + > + if (!(reg & PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR)) { > + val = reg | PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR; > + pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, > + val); > + } else { > + val = reg; > + } > + > + rc = pci_reset_bus_function(dev, probe); > + > + if (reg != val) > + pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, reg); > + > +out: > + pci_cfg_access_unlock(bridge); Maybe a guard() use case to allow early returns in error paths? > + return rc; > +} > + > void pci_dev_lock(struct pci_dev *dev) > { > /* block PM suspend, driver probe, etc. */ > @@ -5066,6 +5109,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, > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 16493426a04f..235f37715a43 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -51,7 +51,7 @@ > PCI_STATUS_PARITY) > > /* Number of reset methods used in pci_reset_fn_methods array in pci.c */ > -#define PCI_NUM_RESET_METHODS 7 > +#define PCI_NUM_RESET_METHODS 8 > > #define PCI_RESET_PROBE true > #define PCI_RESET_DO_RESET false