Message ID | 20220105150239.9628-9-pali@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | PCI: mvebu: subsystem ids, AER and INTx | expand |
On Wed, Jan 5, 2022 at 9:03 AM Pali Rohár <pali@kernel.org> wrote: > > Split struct pci_ops between ops and child_ops. Member ops is used for > accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for > accessing real PCIe cards. > > There is no need to mix these two struct pci_ops into one as PCI core code > already provides separate callbacks via bridge->ops and bridge->child_ops. I had similar patches including mvebu that I never got around to sending out. I pushed the branch out now at least[1]. > Signed-off-by: Pali Rohár <pali@kernel.org> > --- > drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- > 1 file changed, 44 insertions(+), 38 deletions(-) > > diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c > index 9ea2f6a7c2b0..1e90ab888075 100644 > --- a/drivers/pci/controller/pci-mvebu.c > +++ b/drivers/pci/controller/pci-mvebu.c > @@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) > mvebu_writel(port, mask, PCIE_MASK_OFF); > } > > -static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, > - struct pci_bus *bus, > - u32 devfn, int where, int size, u32 *val) > +static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, > + struct pci_bus *bus, > + int devfn); > + > +static int mvebu_pcie_child_rd_conf(struct pci_bus *bus, u32 devfn, int where, > + int size, u32 *val) > { > - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; > + struct mvebu_pcie *pcie = bus->sysdata; > + struct mvebu_pcie_port *port; > + void __iomem *conf_data; > + > + port = mvebu_pcie_find_port(pcie, bus, devfn); > + if (!port) { > + *val = 0xffffffff; > + return PCIBIOS_DEVICE_NOT_FOUND; > + } > + > + if (!mvebu_pcie_link_up(port)) { > + *val = 0xffffffff; > + return PCIBIOS_DEVICE_NOT_FOUND; > + } > + > + conf_data = port->base + PCIE_CONF_DATA_OFF; > > mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), > PCIE_CONF_ADDR_OFF); > @@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, > return PCIBIOS_SUCCESSFUL; > } > > -static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, > - struct pci_bus *bus, > - u32 devfn, int where, int size, u32 val) > +static int mvebu_pcie_child_wr_conf(struct pci_bus *bus, u32 devfn, > + int where, int size, u32 val) > { > - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; > + struct mvebu_pcie *pcie = bus->sysdata; > + struct mvebu_pcie_port *port; > + void __iomem *conf_data; > + > + port = mvebu_pcie_find_port(pcie, bus, devfn); > + if (!port) > + return PCIBIOS_DEVICE_NOT_FOUND; > + > + if (!mvebu_pcie_link_up(port)) > + return PCIBIOS_DEVICE_NOT_FOUND; > + > + conf_data = port->base + PCIE_CONF_DATA_OFF; The same code in read and write is a hint to use .map_bus(). > > mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), > PCIE_CONF_ADDR_OFF); > @@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, > return PCIBIOS_SUCCESSFUL; > } > > +static struct pci_ops mvebu_pcie_child_ops = { > + .read = mvebu_pcie_child_rd_conf, > + .write = mvebu_pcie_child_wr_conf, > +}; > + > /* > * Remove windows, starting from the largest ones to the smallest > * ones. > @@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, > { > struct mvebu_pcie *pcie = bus->sysdata; > struct mvebu_pcie_port *port; > - int ret; > > port = mvebu_pcie_find_port(pcie, bus, devfn); > if (!port) > return PCIBIOS_DEVICE_NOT_FOUND; It would be nice to go from 'bus' to 'bridge' ptr directly, but I still had this in my version. I guess a standard RP struct as part of decoupling host bridges from RPs would solve this issue. Rob [1] https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git/log/?h=pci-child-bus-ops
On Wednesday 05 January 2022 09:41:51 Rob Herring wrote: > > @@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, > > return PCIBIOS_SUCCESSFUL; > > } > > > > +static struct pci_ops mvebu_pcie_child_ops = { > > + .read = mvebu_pcie_child_rd_conf, > > + .write = mvebu_pcie_child_wr_conf, > > +}; > > + > > /* > > * Remove windows, starting from the largest ones to the smallest > > * ones. > > @@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, > > { > > struct mvebu_pcie *pcie = bus->sysdata; > > struct mvebu_pcie_port *port; > > - int ret; > > > > port = mvebu_pcie_find_port(pcie, bus, devfn); > > if (!port) > > return PCIBIOS_DEVICE_NOT_FOUND; > > It would be nice to go from 'bus' to 'bridge' ptr directly, but I > still had this in my version. I guess a standard RP struct as part of > decoupling host bridges from RPs would solve this issue. Hello! The problem is somewhere else. This driver is misusing bus 0 for doing non-transparent bus-bridging between multiple PCI domains by registering roots ports across all domains into domain 0, bus 0. All details are in this my patch which documents this strange driver behavior: https://lore.kernel.org/linux-pci/20211125124605.25915-12-pali@kernel.org/ So the correct solution is is split these multidomain mixing and then every domain would have exactly one root port (as it is designed in HW).
On Wednesday 05 January 2022 09:41:51 Rob Herring wrote: > On Wed, Jan 5, 2022 at 9:03 AM Pali Rohár <pali@kernel.org> wrote: > > > > Split struct pci_ops between ops and child_ops. Member ops is used for > > accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for > > accessing real PCIe cards. > > > > There is no need to mix these two struct pci_ops into one as PCI core code > > already provides separate callbacks via bridge->ops and bridge->child_ops. > > I had similar patches including mvebu that I never got around to > sending out. I pushed the branch out now at least[1]. Are you going to finish your patch series and send it? Because if yes, I can drop this my patch in v3 and let all ->child_ops conversion for you.
On Tue, Jan 11, 2022 at 7:44 PM Pali Rohár <pali@kernel.org> wrote: > > On Wednesday 05 January 2022 09:41:51 Rob Herring wrote: > > On Wed, Jan 5, 2022 at 9:03 AM Pali Rohár <pali@kernel.org> wrote: > > > > > > Split struct pci_ops between ops and child_ops. Member ops is used for > > > accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for > > > accessing real PCIe cards. > > > > > > There is no need to mix these two struct pci_ops into one as PCI core code > > > already provides separate callbacks via bridge->ops and bridge->child_ops. > > > > I had similar patches including mvebu that I never got around to > > sending out. I pushed the branch out now at least[1]. > > Are you going to finish your patch series and send it? Because if yes, > I can drop this my patch in v3 and let all ->child_ops conversion for > you. Not any time soon. Rob
diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 9ea2f6a7c2b0..1e90ab888075 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) mvebu_writel(port, mask, PCIE_MASK_OFF); } -static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, + struct pci_bus *bus, + int devfn); + +static int mvebu_pcie_child_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) { - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; + void __iomem *conf_data; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (!mvebu_pcie_link_up(port)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + conf_data = port->base + PCIE_CONF_DATA_OFF; mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), PCIE_CONF_ADDR_OFF); @@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, return PCIBIOS_SUCCESSFUL; } -static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) +static int mvebu_pcie_child_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) { - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; + void __iomem *conf_data; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (!mvebu_pcie_link_up(port)) + return PCIBIOS_DEVICE_NOT_FOUND; + + conf_data = port->base + PCIE_CONF_DATA_OFF; mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), PCIE_CONF_ADDR_OFF); @@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, return PCIBIOS_SUCCESSFUL; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; + /* * Remove windows, starting from the largest ones to the smallest * ones. @@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, { struct mvebu_pcie *pcie = bus->sysdata; struct mvebu_pcie_port *port; - int ret; port = mvebu_pcie_find_port(pcie, bus, devfn); if (!port) return PCIBIOS_DEVICE_NOT_FOUND; - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return pci_bridge_emul_conf_write(&port->bridge, where, - size, val); - - if (!mvebu_pcie_link_up(port)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, - where, size, val); - - return ret; + return pci_bridge_emul_conf_write(&port->bridge, where, size, val); } /* PCI configuration space read function */ @@ -889,7 +909,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, { struct mvebu_pcie *pcie = bus->sysdata; struct mvebu_pcie_port *port; - int ret; port = mvebu_pcie_find_port(pcie, bus, devfn); if (!port) { @@ -897,21 +916,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, return PCIBIOS_DEVICE_NOT_FOUND; } - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return pci_bridge_emul_conf_read(&port->bridge, where, - size, val); - - if (!mvebu_pcie_link_up(port)) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, - where, size, val); - - return ret; + return pci_bridge_emul_conf_read(&port->bridge, where, size, val); } static struct pci_ops mvebu_pcie_ops = { @@ -1421,6 +1426,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; + bridge->child_ops = &mvebu_pcie_child_ops; bridge->align_resource = mvebu_pcie_align_resource; bridge->map_irq = mvebu_pcie_map_irq;
Split struct pci_ops between ops and child_ops. Member ops is used for accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for accessing real PCIe cards. There is no need to mix these two struct pci_ops into one as PCI core code already provides separate callbacks via bridge->ops and bridge->child_ops. Signed-off-by: Pali Rohár <pali@kernel.org> --- drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- 1 file changed, 44 insertions(+), 38 deletions(-)