On Fri, Nov 22, 2024 at 12:57:10PM +0100, Niklas Cassel wrote: > The DWC Databook description for the LWR_TARGET_RW and LWR_TARGET_HW fields > in the IATU_LWR_TARGET_ADDR_OFF_INBOUND_i registers state that: > "Field size depends on log2(BAR_MASK+1) in BAR match mode." > > I.e. only the upper bits are writable, and the number of writable bits is > dependent on the configured BAR_MASK. > > If we do not write the BAR_MASK before writing the iATU registers, we are > relying the reset value of the BAR_MASK being larger than the requested > size of the first set_bar() call. The reset value of the BAR_MASK is SoC > dependent. > > Thus, if the first set_bar() call requests a size that is larger than the > reset value of the BAR_MASK, the iATU will try to write to read-only bits, > which will cause the iATU to end up redirecting to a physical address that > is different from the address that was intended. > > Thus, we should always write the iATU registers after writing the BAR_MASK. > > Since set_bar() supports dynamically changing the physical address of a > BAR, this change is slightly bigger than a single line change. > > While at it, add comments which clarifies the support for dynamically > changing the physical address of a BAR. (Which was previously missing.) > This is not strictly relevant to this patch, so should be separate. > Fixes: f8aed6ec624f ("PCI: dwc: designware: Add EP mode support") > Signed-off-by: Niklas Cassel <cassel@xxxxxxxxxx> LGTM! This patch should only have the iATU config change and should be backported also. - Mani > --- > .../pci/controller/dwc/pcie-designware-ep.c | 46 ++++++++++++++----- > 1 file changed, 34 insertions(+), 12 deletions(-) > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c > index 507e40bd18c8..34d60142ffb5 100644 > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c > @@ -222,19 +222,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1)) > return -EINVAL; > > - reg = PCI_BASE_ADDRESS_0 + (4 * bar); > - > - if (!(flags & PCI_BASE_ADDRESS_SPACE)) > - type = PCIE_ATU_TYPE_MEM; > - else > - type = PCIE_ATU_TYPE_IO; > + /* > + * Certain EPF drivers dynamically change the physical address of a BAR > + * (i.e. they call set_bar() twice, without ever calling clear_bar(), as > + * calling clear_bar() would clear the BAR's PCI address assigned by the > + * host). > + */ > + if (ep->epf_bar[bar]) { > + /* > + * We can only dynamically change a BAR if the new configuration > + * doesn't fundamentally differ from the existing configuration. > + */ > + if (ep->epf_bar[bar]->barno != bar || > + ep->epf_bar[bar]->size != size || > + ep->epf_bar[bar]->flags != flags) > + return -EINVAL; > > - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar); > - if (ret) > - return ret; > + /* > + * When dynamically changing a BAR, skip writing the BAR reg, as > + * that would clear the BAR's PCI address assigned by the host. > + */ > + goto config_atu; > + } > > - if (ep->epf_bar[bar]) > - return 0; > + reg = PCI_BASE_ADDRESS_0 + (4 * bar); > > dw_pcie_dbi_ro_wr_en(pci); > > @@ -246,9 +257,20 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); > } > > - ep->epf_bar[bar] = epf_bar; > dw_pcie_dbi_ro_wr_dis(pci); > > +config_atu: > + if (!(flags & PCI_BASE_ADDRESS_SPACE)) > + type = PCIE_ATU_TYPE_MEM; > + else > + type = PCIE_ATU_TYPE_IO; > + > + ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar); > + if (ret) > + return ret; > + > + ep->epf_bar[bar] = epf_bar; > + > return 0; > } > > -- > 2.47.0 > -- மணிவண்ணன் சதாசிவம்