Re: [PATCH v8 04/10] PCI: dwc: ep: Fix DBI access failure for drivers requiring refclk from host

2024-02-26 Thread Frank Li
On Sat, Feb 24, 2024 at 12:24:10PM +0530, Manivannan Sadhasivam wrote:
> The DWC glue drivers requiring an active reference clock from the PCIe host
> for initializing their PCIe EP core, set a flag called 'core_init_notifier'
> to let DWC driver know that these drivers need a special attention during
> initialization. In these drivers, access to the hw registers (like DBI)
> before receiving the active refclk from host will result in access failure
> and also could cause a whole system hang.
> 
> But the current DWC EP driver doesn't honor the requirements of the drivers
> setting 'core_init_notifier' flag and tries to access the DBI registers
> during dw_pcie_ep_init(). This causes the system hang for glue drivers such
> as Tegra194 and Qcom EP as they depend on refclk from host and have set the
> above mentioned flag.
> 
> To workaround this issue, users of the affected platforms have to maintain
> the dependency with the PCIe host by booting the PCIe EP after host boot.
> But this won't provide a good user experience, since PCIe EP is _one_ of
> the features of those platforms and it doesn't make sense to delay the
> whole platform booting due to PCIe requiring active refclk.
> 
> So to fix this issue, let's move all the DBI access from
> dw_pcie_ep_init() in the DWC EP driver to the dw_pcie_ep_init_complete()
> API. This API will only be called by the drivers setting
> 'core_init_notifier' flag once refclk is received from host. For the rest
> of the drivers that gets the refclk locally, this API will be called
> within dw_pcie_ep_init().
> 
> Fixes: e966f7390da9 ("PCI: dwc: Refactor core initialization code for EP 
> mode")
> Co-developed-by: Vidya Sagar 
> Signed-off-by: Vidya Sagar 
> Signed-off-by: Manivannan Sadhasivam 

Reviewed-by: Frank Li 

> ---
>  drivers/pci/controller/dwc/pcie-designware-ep.c | 120 
> ++--
>  1 file changed, 71 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c 
> b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 1205bfba8310..99d66b0fa59b 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -606,11 +606,16 @@ static unsigned int 
> dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
>  int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
>  {
>   struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> + struct dw_pcie_ep_func *ep_func;
> + struct device *dev = pci->dev;
> + struct pci_epc *epc = ep->epc;
>   unsigned int offset, ptm_cap_base;
>   unsigned int nbars;
>   u8 hdr_type;
> + u8 func_no;
> + int i, ret;
> + void *addr;
>   u32 reg;
> - int i;
>  
>   hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
>  PCI_HEADER_TYPE_MASK;
> @@ -621,6 +626,58 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
>   return -EIO;
>   }
>  
> + dw_pcie_version_detect(pci);
> +
> + dw_pcie_iatu_detect(pci);
> +
> + ret = dw_pcie_edma_detect(pci);
> + if (ret)
> + return ret;
> +
> + if (!ep->ib_window_map) {
> + ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows,
> +GFP_KERNEL);
> + if (!ep->ib_window_map)
> + goto err_remove_edma;
> + }
> +
> + if (!ep->ob_window_map) {
> + ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows,
> +GFP_KERNEL);
> + if (!ep->ob_window_map)
> + goto err_remove_edma;
> + }
> +
> + if (!ep->outbound_addr) {
> + addr = devm_kcalloc(dev, pci->num_ob_windows, 
> sizeof(phys_addr_t),
> + GFP_KERNEL);
> + if (!addr)
> + goto err_remove_edma;
> + ep->outbound_addr = addr;
> + }
> +
> + for (func_no = 0; func_no < epc->max_functions; func_no++) {
> +
> + ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
> + if (ep_func)
> + continue;
> +
> + ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL);
> + if (!ep_func)
> + goto err_remove_edma;
> +
> + ep_func->func_no = func_no;
> + ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no,
> +   PCI_CAP_ID_MSI);
> + ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no,
> +PCI_CAP_ID_MSIX);
> +
> + list_add_tail(_func->list, >func_list);
> + }
> +
> + if (ep->ops->init)
> + ep->ops->init(ep);
> +
>   offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
>   ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
> 

[PATCH v8 04/10] PCI: dwc: ep: Fix DBI access failure for drivers requiring refclk from host

2024-02-23 Thread Manivannan Sadhasivam
The DWC glue drivers requiring an active reference clock from the PCIe host
for initializing their PCIe EP core, set a flag called 'core_init_notifier'
to let DWC driver know that these drivers need a special attention during
initialization. In these drivers, access to the hw registers (like DBI)
before receiving the active refclk from host will result in access failure
and also could cause a whole system hang.

But the current DWC EP driver doesn't honor the requirements of the drivers
setting 'core_init_notifier' flag and tries to access the DBI registers
during dw_pcie_ep_init(). This causes the system hang for glue drivers such
as Tegra194 and Qcom EP as they depend on refclk from host and have set the
above mentioned flag.

To workaround this issue, users of the affected platforms have to maintain
the dependency with the PCIe host by booting the PCIe EP after host boot.
But this won't provide a good user experience, since PCIe EP is _one_ of
the features of those platforms and it doesn't make sense to delay the
whole platform booting due to PCIe requiring active refclk.

So to fix this issue, let's move all the DBI access from
dw_pcie_ep_init() in the DWC EP driver to the dw_pcie_ep_init_complete()
API. This API will only be called by the drivers setting
'core_init_notifier' flag once refclk is received from host. For the rest
of the drivers that gets the refclk locally, this API will be called
within dw_pcie_ep_init().

Fixes: e966f7390da9 ("PCI: dwc: Refactor core initialization code for EP mode")
Co-developed-by: Vidya Sagar 
Signed-off-by: Vidya Sagar 
Signed-off-by: Manivannan Sadhasivam 
---
 drivers/pci/controller/dwc/pcie-designware-ep.c | 120 ++--
 1 file changed, 71 insertions(+), 49 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c 
b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 1205bfba8310..99d66b0fa59b 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -606,11 +606,16 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct 
dw_pcie *pci, int cap)
 int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
 {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+   struct dw_pcie_ep_func *ep_func;
+   struct device *dev = pci->dev;
+   struct pci_epc *epc = ep->epc;
unsigned int offset, ptm_cap_base;
unsigned int nbars;
u8 hdr_type;
+   u8 func_no;
+   int i, ret;
+   void *addr;
u32 reg;
-   int i;
 
hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
   PCI_HEADER_TYPE_MASK;
@@ -621,6 +626,58 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
return -EIO;
}
 
+   dw_pcie_version_detect(pci);
+
+   dw_pcie_iatu_detect(pci);
+
+   ret = dw_pcie_edma_detect(pci);
+   if (ret)
+   return ret;
+
+   if (!ep->ib_window_map) {
+   ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows,
+  GFP_KERNEL);
+   if (!ep->ib_window_map)
+   goto err_remove_edma;
+   }
+
+   if (!ep->ob_window_map) {
+   ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows,
+  GFP_KERNEL);
+   if (!ep->ob_window_map)
+   goto err_remove_edma;
+   }
+
+   if (!ep->outbound_addr) {
+   addr = devm_kcalloc(dev, pci->num_ob_windows, 
sizeof(phys_addr_t),
+   GFP_KERNEL);
+   if (!addr)
+   goto err_remove_edma;
+   ep->outbound_addr = addr;
+   }
+
+   for (func_no = 0; func_no < epc->max_functions; func_no++) {
+
+   ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
+   if (ep_func)
+   continue;
+
+   ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL);
+   if (!ep_func)
+   goto err_remove_edma;
+
+   ep_func->func_no = func_no;
+   ep_func->msi_cap = dw_pcie_ep_find_capability(ep, func_no,
+ PCI_CAP_ID_MSI);
+   ep_func->msix_cap = dw_pcie_ep_find_capability(ep, func_no,
+  PCI_CAP_ID_MSIX);
+
+   list_add_tail(_func->list, >func_list);
+   }
+
+   if (ep->ops->init)
+   ep->ops->init(ep);
+
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
 
@@ -655,14 +712,17 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
dw_pcie_dbi_ro_wr_dis(pci);
 
return 0;
+
+err_remove_edma:
+   dw_pcie_edma_remove(pci);
+
+   return ret;
 }