mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers
@ 2026-05-30 15:30 Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 01/16] PCI: dwc: Add pcie_cap field and helper in designware header Hans Zhang
                   ` (16 more replies)
  0 siblings, 17 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Hi,

The DWC PCIe core and its many platform drivers repeatedly call
dw_pcie_find_capability(pci, PCI_CAP_ID_EXP) to obtain the offset of the
PCI Express Capability structure. This is wasteful and makes the code
verbose. Some drivers even search for the offset in suspend/resume paths.

Add a cached pcie_cap field in struct dw_pcie and a helper
dw_pcie_get_pcie_cap() to initialize it once at the point when the
hardware is ready. Then replace all explicit capability searches with
the cached value across the entire dwc subtree.

**Safety analysis: DBI access timing**
The PCIe Capability offset is read from DBI configuration space. DBI
registers are only accessible after the controller's clocks, resets, and
power are enabled. The following call graph demonstrates that all
dw_pcie_find_capability() replacements occur only after hardware is ready:

- For Root Complex mode:
  dw_pcie_host_init()
    ...
    if (pp->ops->init)
      pp->ops->init   [enables clocks/resets]
    dw_pcie_get_pcie_cap()   [caches offset]
    ...
    dw_pcie_link_up
      pci->ops->link_up
    ...
    dw_pcie_start_link
      pci->ops->start_link
    ...
    pci_host_probe
    if (pp->ops->post_init)
      pp->ops->post_init
    ...

- For Endpoint mode:
  dw_pcie_ep_init()
    ep->ops->pre_init()    [enables clocks/resets]
    dw_pcie_get_pcie_cap()   [caches offset]
    dw_pcie_ep_init_non_sticky_registers() etc.

- Some platform drivers (e.g., layerscape-ep, tegra194) need the offset
  before calling dw_pcie_ep_init() / dw_pcie_host_init(). They already
  enable hardware themselves and explicitly call dw_pcie_get_pcie_cap()
  before the core caches it. This is safe and ensures the cached value is
  available for all later users.

Thus, no DBI access occurs before hardware is operational.

Changes in v2:
- Move dw_pcie_get_pcie_cap() from the beginning of dw_pcie_host_init()
  and dw_pcie_ep_init() to after the hardware initialization callbacks
  (host_init/pre_init). This can prevent the reading of DBI when the clock
  is turned off or when the hardware is not ready.
- Convert all platform drivers to use the cached offset via
  dw_pcie_get_pcie_cap() or directly pci->pcie_cap where safe.
- Fix type mismatches (u16 -> u8).
- Properly split per-driver modifications.

v1:
https://patchwork.kernel.org/project/linux-pci/cover/20260509135152.2241235-1-18255117159@163.com/

---
**Why splitting into per-driver patches**  
To facilitate review and potential bisection, each platform driver is
modified in its own patch. The core changes are in patches 1-2, and
follow-up patches convert individual glue drivers. If maintainers prefer
to squash them, please let me know and I will merge in v3.

This splitting patch of each controller was carried out solely for
the convenience of code review.
---

Hans Zhang (16):
  PCI: dwc: Add pcie_cap field and helper in designware header
  PCI: dwc: Use cached PCIe capability offset in core
  PCI: dwc: imx6: Use cached PCIe capability offset
  PCI: dwc: layerscape-ep: Use cached PCIe capability offset
  PCI: dwc: meson: Use cached PCIe capability offset
  PCI: dwc: rockchip: Use cached PCIe capability offset
  PCI: dwc: eswin: Use cached PCIe capability offset
  PCI: dwc: fu740: Use cached PCIe capability offset
  PCI: dwc: intel-gw: Use cached PCIe capability offset
  PCI: dwc: qcom-ep: Use cached PCIe capability offset
  PCI: dwc: qcom: Use cached PCIe capability offset
  PCI: dwc: sophgo: Use cached PCIe capability offset
  PCI: dwc: spacemit-k1: Use cached PCIe capability offset
  PCI: dwc: spear13xx: Use cached PCIe capability offset
  PCI: dwc: tegra194: Use cached PCIe capability offset
  PCI: dwc: ultrarisc: Use cached PCIe capability offset

 drivers/pci/controller/dwc/pci-imx6.c         |  6 +++---
 .../pci/controller/dwc/pci-layerscape-ep.c    |  9 +++-----
 drivers/pci/controller/dwc/pci-meson.c        |  4 ++--
 .../pci/controller/dwc/pcie-designware-ep.c   |  4 +++-
 .../pci/controller/dwc/pcie-designware-host.c |  2 ++
 drivers/pci/controller/dwc/pcie-designware.c  | 16 ++++++--------
 drivers/pci/controller/dwc/pcie-designware.h  | 17 +++++++++++++++
 drivers/pci/controller/dwc/pcie-dw-rockchip.c |  2 +-
 drivers/pci/controller/dwc/pcie-eswin.c       |  3 +--
 drivers/pci/controller/dwc/pcie-fu740.c       |  2 +-
 drivers/pci/controller/dwc/pcie-intel-gw.c    |  2 +-
 drivers/pci/controller/dwc/pcie-qcom-ep.c     | 11 ++++------
 drivers/pci/controller/dwc/pcie-qcom.c        | 21 ++++++++-----------
 drivers/pci/controller/dwc/pcie-sophgo.c      |  5 +++--
 drivers/pci/controller/dwc/pcie-spacemit-k1.c |  2 +-
 drivers/pci/controller/dwc/pcie-spear13xx.c   |  2 +-
 drivers/pci/controller/dwc/pcie-tegra194.c    |  6 ++----
 drivers/pci/controller/dwc/pcie-ultrarisc.c   |  2 +-
 18 files changed, 61 insertions(+), 55 deletions(-)


base-commit: 3a97877d13e1a29c50ab15ed0a0aba87b75061dd
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 01/16] PCI: dwc: Add pcie_cap field and helper in designware header
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 02/16] PCI: dwc: Use cached PCIe capability offset in core Hans Zhang
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Add a pcie_cap field to struct dw_pcie to store the offset of the
PCI Express Capability structure. Provide a helper dw_pcie_get_pcie_cap()
which performs the capability search on first call and caches the result.

This is a preparatory step for replacing repetitive capability searches
in both core and platform drivers.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
 drivers/pci/controller/dwc/pcie-designware.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 7dac37521b8e..561b434fe335 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -596,6 +596,8 @@ struct dw_pcie {
 	 * use_parent_dt_ranges to true to avoid this warning.
 	 */
 	bool			use_parent_dt_ranges;
+
+	u8			pcie_cap;	/* PCIe capability offset */
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@@ -834,6 +836,21 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
 	dw_pcie_writel_dbi(pci, reg, val);
 }
 
+/**
+ * dw_pcie_get_pcie_cap() - Return cached PCIe Capability offset
+ * @pci: DWC instance
+ *
+ * Finds and caches the offset of PCI_CAP_ID_EXP on first call.
+ * Returns 0 if the capability is not present.
+ */
+static inline u8 dw_pcie_get_pcie_cap(struct dw_pcie *pci)
+{
+	if (!pci->pcie_cap)
+		pci->pcie_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+
+	return pci->pcie_cap;
+}
+
 static inline int dw_pcie_start_link(struct dw_pcie *pci)
 {
 	if (pci->ops && pci->ops->start_link)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 02/16] PCI: dwc: Use cached PCIe capability offset in core
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 01/16] PCI: dwc: Add pcie_cap field and helper in designware header Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 03/16] PCI: dwc: imx6: Use cached PCIe capability offset Hans Zhang
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Modify the DWC core functions to use the cached pcie_cap offset instead
of calling dw_pcie_find_capability() each time.

In the DWC core, dw_pcie_find_capability() is called at several locations:
- dw_pcie_ep_init_non_sticky_registers()
- dw_pcie_wait_for_link()
- dw_pcie_link_set_max_speed()
- dw_pcie_link_get_max_link_width()
- dw_pcie_link_set_max_link_width()

The cached offset is initialized after hardware is ready:
- In host mode: dw_pcie_host_init() calls pp->ops->host_init() (enables
  clocks/resets), then dw_pcie_get_pcie_cap() caches the offset.
- In endpoint mode: dw_pcie_ep_init() calls ep->ops->pre_init() (enables
  hardware), then dw_pcie_get_pcie_cap() caches the offset.

dw_pcie_ep_init_non_sticky_registers() is called after pre_init, so it can
use dw_pcie_get_pcie_cap() safely. The other functions run after probe,
so pci->pcie_cap is already valid and can be used directly.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
 drivers/pci/controller/dwc/pcie-designware-ep.c  |  4 +++-
 .../pci/controller/dwc/pcie-designware-host.c    |  2 ++
 drivers/pci/controller/dwc/pcie-designware.c     | 16 ++++++----------
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index a396b58d6746..11b4fa6d129c 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1246,7 +1246,7 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
 	 * to all other functions as well.
 	 */
 	if (funcs > 1) {
-		offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+		offset = dw_pcie_get_pcie_cap(pci);
 		func0_lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
 		func0_lnkcap = FIELD_GET(PCI_EXP_LNKCAP_MLW |
 					 PCI_EXP_LNKCAP_SLS, func0_lnkcap);
@@ -1524,6 +1524,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 	if (ep->ops->pre_init)
 		ep->ops->pre_init(ep);
 
+	dw_pcie_get_pcie_cap(pci);
+
 	ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
 			       ep->page_size);
 	if (ret < 0) {
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index cffb34f6f3a9..b8175138e47a 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -593,6 +593,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
 			goto err_free_ecam;
 	}
 
+	dw_pcie_get_pcie_cap(pci);
+
 	if (pci_msi_enabled()) {
 		pp->use_imsi_rx = !(pp->ops->msi_init ||
 				     of_property_present(np, "msi-parent") ||
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 37d2715e38eb..810e920c1d8b 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -765,7 +765,7 @@ const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm)
  */
 int dw_pcie_wait_for_link(struct dw_pcie *pci)
 {
-	u32 offset, val, ltssm;
+	u32 val, ltssm;
 	int retries;
 
 	/* Check if the link is up or not */
@@ -811,8 +811,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
 	if (pci->max_link_speed > 2)
 		msleep(PCIE_RESET_CONFIG_WAIT_MS);
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
+	val = dw_pcie_readw_dbi(pci, pci->pcie_cap + PCI_EXP_LNKSTA);
 
 	dev_info(pci->dev, "PCIe Gen.%u x%u link up\n",
 		 FIELD_GET(PCI_EXP_LNKSTA_CLS, val),
@@ -848,7 +847,7 @@ EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
 static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
 {
 	u32 cap, ctrl2, link_speed;
-	u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = pci->pcie_cap;
 
 	cap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
 
@@ -894,8 +893,7 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
 
 int dw_pcie_link_get_max_link_width(struct dw_pcie *pci)
 {
-	u8 cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	u32 lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
+	u32 lnkcap = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
 
 	return FIELD_GET(PCI_EXP_LNKCAP_MLW, lnkcap);
 }
@@ -903,7 +901,6 @@ int dw_pcie_link_get_max_link_width(struct dw_pcie *pci)
 static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
 {
 	u32 lnkcap, lwsc, plc;
-	u8 cap;
 
 	if (!num_lanes)
 		return;
@@ -940,10 +937,9 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
 	dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
 	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
 
-	cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
+	lnkcap = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
 	FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, num_lanes);
-	dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap);
+	dw_pcie_writel_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP, lnkcap);
 }
 
 void dw_pcie_iatu_detect(struct dw_pcie *pci)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 03/16] PCI: dwc: imx6: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 01/16] PCI: dwc: Add pcie_cap field and helper in designware header Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 02/16] PCI: dwc: Use cached PCIe capability offset in core Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 04/16] PCI: dwc: layerscape-ep: " Hans Zhang
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Thus, the cached offset is available when these functions are called, and
DBI access occurs only after hardware is enabled.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-imx6, dw_pcie_find_capability() is called in the following
call chain:

  static const struct dw_pcie_ops dw_pcie_ops = {
    .start_link = imx_pcie_start_link,
  };
  imx_pcie_start_link()
    -> dw_pcie_find_capability()
    -> imx_pcie_ltssm_enable()
      -> dw_pcie_find_capability()

Replace these calls with the cached pci->pcie_cap. The call chain after
modification becomes:

  dw_pcie_host_init()
    -> pp->ops->init() [imx6_pcie_host_init]
    -> dw_pcie_get_pcie_cap()  (caches offset)
    -> pci->ops->start_link = imx_pcie_start_link
      -> uses pci->pcie_cap
      -> imx_pcie_ltssm_enable()
        -> uses pci->pcie_cap
---
 drivers/pci/controller/dwc/pci-imx6.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 773ab65b2afa..de06ff429eff 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -933,10 +933,10 @@ static void imx_pcie_ltssm_enable(struct device *dev)
 {
 	struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
 	const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
-	u8 offset = dw_pcie_find_capability(imx_pcie->pci, PCI_CAP_ID_EXP);
+	struct dw_pcie *pci = imx_pcie->pci;
 	u32 tmp;
 
-	tmp = dw_pcie_readl_dbi(imx_pcie->pci, offset + PCI_EXP_LNKCAP);
+	tmp = dw_pcie_readl_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP);
 	phy_set_speed(imx_pcie->phy, FIELD_GET(PCI_EXP_LNKCAP_SLS, tmp));
 	if (drvdata->ltssm_mask)
 		regmap_update_bits(imx_pcie->iomuxc_gpr, drvdata->ltssm_off, drvdata->ltssm_mask,
@@ -962,7 +962,7 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
 {
 	struct imx_pcie *imx_pcie = to_imx_pcie(pci);
 	struct device *dev = pci->dev;
-	u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = pci->pcie_cap;
 	u32 tmp;
 	int ret;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 04/16] PCI: dwc: layerscape-ep: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (2 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 03/16] PCI: dwc: imx6: Use cached PCIe capability offset Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 05/16] PCI: dwc: meson: " Hans Zhang
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Replace these with dw_pcie_get_pcie_cap(). The hardware is already enabled
by the driver before ls_pcie_ep_probe() and before the interrupt handler
runs. dw_pcie_get_pcie_cap() will cache the offset on first call, and
subsequent calls (including inside dw_pcie_ep_init) will use the cached
value without re-searching.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-layerscape-ep, dw_pcie_find_capability() is called in:

  ls_pcie_ep_probe()
    -> offset = dw_pcie_find_capability()
    -> pcie->lnkcap = dw_pcie_readl_dbi(offset + PCI_EXP_LNKCAP)
    -> dw_pcie_ep_init()
    -> ls_pcie_ep_interrupt_init()
        -> devm_request_irq(..., ls_pcie_ep_event_handler)
          -> ls_pcie_ep_event_handler()
            -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pci-layerscape-ep.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index 8936975ff104..b2d0b51df3c5 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -74,7 +74,6 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
 	struct ls_pcie_ep *pcie = dev_id;
 	struct dw_pcie *pci = pcie->pci;
 	u32 val, cfg;
-	u8 offset;
 
 	val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR);
 	ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);
@@ -83,9 +82,6 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
 		return IRQ_NONE;
 
 	if (val & PEX_PF0_PME_MES_DR_LUD) {
-
-		offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-
 		/*
 		 * The values of the Maximum Link Width and Supported Link
 		 * Speed from the Link Capabilities Register will be lost
@@ -93,7 +89,8 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
 		 * that configured by the Reset Configuration Word (RCW).
 		 */
 		dw_pcie_dbi_ro_wr_en(pci);
-		dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
+		dw_pcie_writel_dbi(pci, pci->pcie_cap + PCI_EXP_LNKCAP,
+				   pcie->lnkcap);
 		dw_pcie_dbi_ro_wr_dis(pci);
 
 		cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG);
@@ -266,7 +263,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, pcie);
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	offset = dw_pcie_get_pcie_cap(pci);
 	pcie->lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
 
 	ret = dw_pcie_ep_init(&pci->ep);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 05/16] PCI: dwc: meson: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (3 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 04/16] PCI: dwc: layerscape-ep: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 06/16] PCI: dwc: rockchip: " Hans Zhang
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_host_init() calls pp->ops->init (meson_pcie_host_init) before
caching the offset. Therefore, inside .init we must call
dw_pcie_get_pcie_cap() to obtain the offset (the helper will perform the
DBI read and cache the result). This is safe because the hardware is
already enabled by the driver's own initialization.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pci-meson, the call chain is:

  static const struct dw_pcie_host_ops meson_pcie_host_ops = {
    .init = meson_pcie_host_init,
  };
  meson_pcie_host_init()
    -> meson_set_max_payload()
      -> dw_pcie_find_capability()
    -> meson_set_max_rd_req_size()
      -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pci-meson.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 0694084f612b..81767d010c84 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -276,7 +276,7 @@ static void meson_set_max_payload(struct meson_pcie *mp, int size)
 {
 	struct dw_pcie *pci = &mp->pci;
 	u32 val;
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = dw_pcie_get_pcie_cap(pci);
 	int max_payload_size = meson_size_to_payload(mp, size);
 
 	val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_DEVCTL);
@@ -292,7 +292,7 @@ static void meson_set_max_rd_req_size(struct meson_pcie *mp, int size)
 {
 	struct dw_pcie *pci = &mp->pci;
 	u32 val;
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = dw_pcie_get_pcie_cap(pci);
 	int max_rd_req_size = meson_size_to_payload(mp, size);
 
 	val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_DEVCTL);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 06/16] PCI: dwc: rockchip: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (4 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 05/16] PCI: dwc: meson: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-06-01 17:11   ` Sebastian Reichel
  2026-05-30 15:30 ` [PATCH v2 07/16] PCI: dwc: eswin: " Hans Zhang
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_host_init() calls .init before caching the offset. So inside
.init we call dw_pcie_get_pcie_cap() to trigger caching. The helper will
perform the DBI read (hardware is already enabled) and cache the result.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-dw-rockchip, the call chain is:

  static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
    .init = rockchip_pcie_host_init,
  };
  rockchip_pcie_host_init()
    -> rockchip_pcie_enable_l0s()
      -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 731d93663cca..be8b6187913d 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -369,7 +369,7 @@ static void rockchip_pcie_enable_l0s(struct dw_pcie *pci)
 	u32 cap, lnkcap;
 
 	/* Enable L0S capability for all SoCs */
-	cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	cap = dw_pcie_get_pcie_cap(pci);
 	if (cap) {
 		lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
 		lnkcap |= PCI_EXP_LNKCAP_ASPM_L0S;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 07/16] PCI: dwc: eswin: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (5 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 06/16] PCI: dwc: rockchip: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 08/16] PCI: dwc: fu740: " Hans Zhang
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

This function is called after dw_pcie_host_init() has already cached the
offset (dw_pcie_link_up() is called after .init and after caching).
Therefore, we can directly use pci->pcie_cap.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-eswin, the call chain is:

  static const struct dw_pcie_ops dw_pcie_ops = {
    .link_up = eswin_pcie_link_up,
  };
  eswin_pcie_link_up()
    -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-eswin.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c
index ce8d64f8a395..6a37fd5a8384 100644
--- a/drivers/pci/controller/dwc/pcie-eswin.c
+++ b/drivers/pci/controller/dwc/pcie-eswin.c
@@ -84,8 +84,7 @@ static int eswin_pcie_start_link(struct dw_pcie *pci)
 
 static bool eswin_pcie_link_up(struct dw_pcie *pci)
 {
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	u16 val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
+	u16 val = dw_pcie_readw_dbi(pci, pci->pcie_cap + PCI_EXP_LNKSTA);
 
 	return val & PCI_EXP_LNKSTA_DLLLA;
 }
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 08/16] PCI: dwc: fu740: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (6 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 07/16] PCI: dwc: eswin: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 09/16] PCI: dwc: intel-gw: " Hans Zhang
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

This callback is invoked after dw_pcie_host_init() has cached the offset,
so we can use pci->pcie_cap directly.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-fu740, the call chain is:

  static const struct dw_pcie_ops dw_pcie_ops = {
    .start_link = fu740_pcie_start_link,
  };
  fu740_pcie_start_link()
    -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-fu740.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c
index d0a34f680397..72c0a6316fff 100644
--- a/drivers/pci/controller/dwc/pcie-fu740.c
+++ b/drivers/pci/controller/dwc/pcie-fu740.c
@@ -178,7 +178,7 @@ static int fu740_pcie_start_link(struct dw_pcie *pci)
 {
 	struct device *dev = pci->dev;
 	struct fu740_pcie *afp = dev_get_drvdata(dev);
-	u8 cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 cap_exp = pci->pcie_cap;
 	int ret;
 	u32 orig, tmp;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 09/16] PCI: dwc: intel-gw: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (7 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 08/16] PCI: dwc: fu740: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 10/16] PCI: dwc: qcom-ep: " Hans Zhang
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_host_init() calls .init before caching the offset, so inside
.init we must call dw_pcie_get_pcie_cap() to obtain the offset (hardware
is already enabled). The helper will cache the result for later use.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-intel-gw, the call chain is:

  static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
    .init = intel_pcie_rc_init,
  };
  intel_pcie_rc_init()
    -> intel_pcie_host_setup()
      -> intel_pcie_link_setup()
        -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-intel-gw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
index 2674cd376f49..568b2634d08d 100644
--- a/drivers/pci/controller/dwc/pcie-intel-gw.c
+++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
@@ -120,7 +120,7 @@ static void intel_pcie_ltssm_disable(struct intel_pcie *pcie)
 static void intel_pcie_link_setup(struct intel_pcie *pcie)
 {
 	u32 val;
-	u8 offset = dw_pcie_find_capability(&pcie->pci, PCI_CAP_ID_EXP);
+	u8 offset = dw_pcie_get_pcie_cap(&pcie->pci);
 
 	val = pcie_rc_cfg_rd(pcie, offset + PCI_EXP_LNKCTL);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 10/16] PCI: dwc: qcom-ep: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (8 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 09/16] PCI: dwc: intel-gw: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 11/16] PCI: dwc: qcom: " Hans Zhang
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_ep_init() calls ep->ops->pre_init() (enables hardware) and then
dw_pcie_get_pcie_cap() to cache the offset. Therefore, the IRQ handlers
run after the cache is populated, so they can safely use pci->pcie_cap
directly.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-qcom-ep, dw_pcie_find_capability() appears in:

  qcom_pcie_ep_probe()
    -> dw_pcie_ep_init()
    -> qcom_pcie_ep_enable_irq_resources()
      -> qcom_pcie_ep_global_irq_thread()
	-> qcom_pcie_ep_icc_update()
	  -> dw_pcie_find_capability()
	-> qcom_pcie_ep_perst_irq_thread()
	  -> qcom_pcie_perst_deassert()
	    -> offset = dw_pcie_find_capability()  (called twice)
---
 drivers/pci/controller/dwc/pcie-qcom-ep.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 56184e6ca6e6..0471f96d3c78 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -307,15 +307,13 @@ static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base,
 static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
 {
 	struct dw_pcie *pci = &pcie_ep->pci;
-	u32 offset, status;
-	int speed, width;
-	int ret;
+	int speed, width, ret;
+	u32 status;
 
 	if (!pcie_ep->icc_mem)
 		return;
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+	status = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
 
 	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status);
 	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status);
@@ -492,13 +490,12 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
 	dw_pcie_dbi_ro_wr_en(pci);
 
 	/* Set the L0s Exit Latency to 2us-4us = 0x6 */
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	offset = pci->pcie_cap;
 	val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
 	FIELD_MODIFY(PCI_EXP_LNKCAP_L0SEL, &val, 0x6);
 	dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
 
 	/* Set the L1 Exit Latency to be 32us-64 us = 0x6 */
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
 	val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
 	FIELD_MODIFY(PCI_EXP_LNKCAP_L1EL, &val, 0x6);
 	dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 11/16] PCI: dwc: qcom: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (9 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 10/16] PCI: dwc: qcom-ep: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 12/16] PCI: dwc: sophgo: " Hans Zhang
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_host_init() caches the offset after .init, so .post_init callbacks
and later functions (.link_up, icc_opp_update) can use pci->pcie_cap
directly. For .init itself, we must call dw_pcie_get_pcie_cap() inside
qcom_pcie_host_init() to obtain the offset (hardware is already enabled).

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-qcom, dw_pcie_find_capability() appears in multiple call chains:

  static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
    .init = qcom_pcie_host_init,
  };
  qcom_pcie_host_init()
    -> qcom_pcie_clear_aspm_l0s()
      -> dw_pcie_find_capability()

  static const struct qcom_pcie_ops ops_* = {
    .post_init = qcom_pcie_post_init_*,
  };
  qcom_pcie_post_init_*()
    -> qcom_pcie_set_slot_nccs()  (for many versions)
      -> dw_pcie_find_capability()
    -> For 2_3_3 and 2_9_0: also calls dw_pcie_find_capability() directly

  static const struct dw_pcie_ops dw_pcie_ops = {
    .link_up = qcom_pcie_link_up,
  };
  qcom_pcie_link_up()
    -> dw_pcie_find_capability()

  qcom_pcie_probe()
    -> dw_pcie_host_init()
    -> qcom_pcie_icc_opp_update()
      -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-qcom.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 11fc60489892..80783353d539 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -341,13 +341,13 @@ static int qcom_pcie_start_link(struct dw_pcie *pci)
 static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
 {
 	struct qcom_pcie *pcie = to_qcom_pcie(pci);
-	u16 offset;
+	u8 offset;
 	u32 val;
 
 	if (!pcie->cfg->no_l0s)
 		return;
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	offset = dw_pcie_get_pcie_cap(pci);
 
 	dw_pcie_dbi_ro_wr_en(pci);
 
@@ -360,7 +360,6 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
 
 static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
 {
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
 	u32 val;
 
 	dw_pcie_dbi_ro_wr_en(pci);
@@ -370,9 +369,9 @@ static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
 	 * notifications for the Hot-Plug commands. So set the NCCS field to
 	 * avoid waiting for the completions.
 	 */
-	val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP);
+	val = readl(pci->dbi_base + pci->pcie_cap + PCI_EXP_SLTCAP);
 	val |= PCI_EXP_SLTCAP_NCCS;
-	writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP);
+	writel(val, pci->dbi_base + pci->pcie_cap + PCI_EXP_SLTCAP);
 
 	dw_pcie_dbi_ro_wr_dis(pci);
 }
@@ -935,7 +934,7 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)
 static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
 {
 	struct dw_pcie *pci = pcie->pci;
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = pci->pcie_cap;
 	u32 val;
 
 	/* Force PHY out of lowest power state */
@@ -1257,7 +1256,7 @@ static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie)
 static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
 {
 	struct dw_pcie *pci = pcie->pci;
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 offset = pci->pcie_cap;
 	u32 val;
 	int i;
 
@@ -1303,8 +1302,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
 
 static bool qcom_pcie_link_up(struct dw_pcie *pci)
 {
-	u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	u16 val = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+	u16 val = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
 
 	return val & PCI_EXP_LNKSTA_DLLLA;
 }
@@ -1663,15 +1661,14 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
 
 static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)
 {
-	u32 offset, status, width, speed;
+	u32 status, width, speed;
 	struct dw_pcie *pci = pcie->pci;
 	struct dev_pm_opp_key key = {};
 	unsigned long freq_kbps;
 	struct dev_pm_opp *opp;
 	int ret, freq_mbps;
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
-	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA);
+	status = readw(pci->dbi_base + pci->pcie_cap + PCI_EXP_LNKSTA);
 
 	/* Only update constraints if link is up. */
 	if (!(status & PCI_EXP_LNKSTA_DLLLA))
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 12/16] PCI: dwc: sophgo: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (10 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 11/16] PCI: dwc: qcom: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 13/16] PCI: dwc: spacemit-k1: " Hans Zhang
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

dw_pcie_host_init() calls .init before caching, so we must call
dw_pcie_get_pcie_cap() inside .init. The hardware is already enabled by
the driver's own initialization before this point. The helper will cache
the offset and avoid redundant searches.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-sophgo, the call chain is:

  static const struct dw_pcie_host_ops sophgo_pcie_host_ops = {
    .init = sophgo_pcie_host_init,
  };
  sophgo_pcie_host_init()
    -> sophgo_pcie_disable_l0s_l1()
      -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-sophgo.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c
index 044088898819..39703d2b7b5f 100644
--- a/drivers/pci/controller/dwc/pcie-sophgo.c
+++ b/drivers/pci/controller/dwc/pcie-sophgo.c
@@ -164,9 +164,10 @@ static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp)
 static void sophgo_pcie_disable_l0s_l1(struct dw_pcie_rp *pp)
 {
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-	u32 offset, val;
+	u8 offset;
+	u32 val;
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	offset = dw_pcie_get_pcie_cap(pci);
 
 	dw_pcie_dbi_ro_wr_en(pci);
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 13/16] PCI: dwc: spacemit-k1: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (11 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 12/16] PCI: dwc: sophgo: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:30 ` [PATCH v2 14/16] PCI: dwc: spear13xx: " Hans Zhang
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Because .init runs before core caching, we call dw_pcie_get_pcie_cap()
inside k1_pcie_disable_aspm_l1() to get the capability base, then add
PCI_EXP_LNKCAP. Hardware is already enabled at this point.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-spacemit-k1, the call chain is:

  static const struct dw_pcie_host_ops k1_pcie_host_ops = {
    .init = k1_pcie_init,
  };
  k1_pcie_init()
    -> k1_pcie_disable_aspm_l1()
      -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-spacemit-k1.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
index be20a520255b..65114060311e 100644
--- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c
+++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
@@ -117,7 +117,7 @@ static void k1_pcie_disable_aspm_l1(struct k1_pcie *k1)
 	u8 offset;
 	u32 val;
 
-	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	offset = dw_pcie_get_pcie_cap(pci);
 	offset += PCI_EXP_LNKCAP;
 
 	dw_pcie_dbi_ro_wr_en(pci);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 14/16] PCI: dwc: spear13xx: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (12 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 13/16] PCI: dwc: spacemit-k1: " Hans Zhang
@ 2026-05-30 15:30 ` Hans Zhang
  2026-05-30 15:31 ` [PATCH v2 15/16] PCI: dwc: tegra194: " Hans Zhang
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:30 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Inside .init we must call dw_pcie_get_pcie_cap() to obtain the offset,
because the core has not yet cached it. The hardware is already enabled
by the driver's own initialization before this point.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-spear13xx, the call chain is:

  static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = {
    .init = spear13xx_pcie_host_init,
  };
  spear13xx_pcie_host_init()
    -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-spear13xx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c
index 01794a9d3ad2..6e4c11b497d4 100644
--- a/drivers/pci/controller/dwc/pcie-spear13xx.c
+++ b/drivers/pci/controller/dwc/pcie-spear13xx.c
@@ -122,7 +122,7 @@ static int spear13xx_pcie_host_init(struct dw_pcie_rp *pp)
 {
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 	struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci);
-	u32 exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	u8 exp_cap_off = dw_pcie_get_pcie_cap(pci);
 	u32 val;
 
 	spear13xx_pcie->app_base = pci->dbi_base + 0x2000;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 15/16] PCI: dwc: tegra194: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (13 preceding siblings ...)
  2026-05-30 15:30 ` [PATCH v2 14/16] PCI: dwc: spear13xx: " Hans Zhang
@ 2026-05-30 15:31 ` Hans Zhang
  2026-05-30 15:31 ` [PATCH v2 16/16] PCI: dwc: ultrarisc: " Hans Zhang
  2026-07-15 13:35 ` [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Manivannan Sadhasivam
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:31 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

In host mode: tegra_pcie_dw_host_init() runs inside .start_link, which is
called after dw_pcie_host_init() has cached the offset. However, the
driver's own initialization already enables hardware, so calling
dw_pcie_get_pcie_cap() is safe and will return the cached value.

In endpoint mode: pex_ep_event_pex_rst_deassert() runs before
dw_pcie_ep_init(), but the driver has already enabled hardware.
dw_pcie_get_pcie_cap() will perform the DBI read and cache the offset.

Thus, the private pcie_cap_base is always valid and can still be used for
register accesses.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-tegra194, dw_pcie_find_capability() appears in:

  static const struct dw_pcie_ops tegra_dw_pcie_ops = {
    .start_link = tegra_pcie_dw_start_link,
  };
  tegra_pcie_dw_start_link()
    -> tegra_pcie_dw_host_init()
      -> dw_pcie_find_capability()

  tegra_pcie_dw_probe()
    case DW_PCIE_EP_TYPE:
      tegra_pcie_config_ep()
        -> pex_ep_event_pex_rst_deassert()
	  -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-tegra194.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 795cef5a915d..bf482bc66a92 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -905,8 +905,7 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
 	pp->bridge->ops = &tegra_pci_ops;
 
 	if (!pcie->pcie_cap_base)
-		pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
-							      PCI_CAP_ID_EXP);
+		pcie->pcie_cap_base = dw_pcie_get_pcie_cap(pci);
 
 	val = dw_pcie_readl_dbi(pci, PCI_IO_BASE);
 	val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8);
@@ -1889,8 +1888,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
 		dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
 	}
 
-	pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
-						      PCI_CAP_ID_EXP);
+	pcie->pcie_cap_base = dw_pcie_get_pcie_cap(pci);
 
 	/* Clear Slot Clock Configuration bit if SRNS configuration */
 	if (pcie->enable_srns) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 16/16] PCI: dwc: ultrarisc: Use cached PCIe capability offset
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (14 preceding siblings ...)
  2026-05-30 15:31 ` [PATCH v2 15/16] PCI: dwc: tegra194: " Hans Zhang
@ 2026-05-30 15:31 ` Hans Zhang
  2026-07-15 13:35 ` [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Manivannan Sadhasivam
  16 siblings, 0 replies; 19+ messages in thread
From: Hans Zhang @ 2026-05-30 15:31 UTC (permalink / raw)
  To: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7
  Cc: robh, linux-pci, linux-kernel, Hans Zhang

Inside .init we call dw_pcie_get_pcie_cap() to obtain the offset, because
the core has not yet cached it. Hardware is already enabled by the driver
before this point.

Signed-off-by: Hans Zhang <18255117159@163.com>
---
In pcie-ultrarisc, the call chain is:

  static const struct dw_pcie_host_ops ultrarisc_pcie_host_ops = {
    .init = ultrarisc_pcie_host_init,
  };
  ultrarisc_pcie_host_init()
    -> dw_pcie_find_capability()
---
 drivers/pci/controller/dwc/pcie-ultrarisc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c
index 6ee661ceff67..3f6e3ab0aa69 100644
--- a/drivers/pci/controller/dwc/pcie-ultrarisc.c
+++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c
@@ -49,7 +49,7 @@ static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp)
 	FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64);
 	dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val);
 
-	cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	cap_exp = dw_pcie_get_pcie_cap(pci);
 	val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2);
 	FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT);
 	dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 06/16] PCI: dwc: rockchip: Use cached PCIe capability offset
  2026-05-30 15:30 ` [PATCH v2 06/16] PCI: dwc: rockchip: " Hans Zhang
@ 2026-06-01 17:11   ` Sebastian Reichel
  0 siblings, 0 replies; 19+ messages in thread
From: Sebastian Reichel @ 2026-06-01 17:11 UTC (permalink / raw)
  To: Hans Zhang
  Cc: bhelgaas, lpieralisi, kwilczynski, mani, s-vadapalli, a-garg7,
	robh, linux-pci, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]

Hi,

On Sat, May 30, 2026 at 11:30:51PM +0800, Hans Zhang wrote:
> dw_pcie_host_init() calls .init before caching the offset. So inside
> .init we call dw_pcie_get_pcie_cap() to trigger caching. The helper will
> perform the DBI read (hardware is already enabled) and cache the result.
> 
> Signed-off-by: Hans Zhang <18255117159@163.com>
> ---

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>

Greetings,

-- Sebastian

> In pcie-dw-rockchip, the call chain is:
> 
>   static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
>     .init = rockchip_pcie_host_init,
>   };
>   rockchip_pcie_host_init()
>     -> rockchip_pcie_enable_l0s()
>       -> dw_pcie_find_capability()
> ---
>  drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> index 731d93663cca..be8b6187913d 100644
> --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
> @@ -369,7 +369,7 @@ static void rockchip_pcie_enable_l0s(struct dw_pcie *pci)
>  	u32 cap, lnkcap;
>  
>  	/* Enable L0S capability for all SoCs */
> -	cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
> +	cap = dw_pcie_get_pcie_cap(pci);
>  	if (cap) {
>  		lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
>  		lnkcap |= PCI_EXP_LNKCAP_ASPM_L0S;
> -- 
> 2.34.1
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers
  2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
                   ` (15 preceding siblings ...)
  2026-05-30 15:31 ` [PATCH v2 16/16] PCI: dwc: ultrarisc: " Hans Zhang
@ 2026-07-15 13:35 ` Manivannan Sadhasivam
  16 siblings, 0 replies; 19+ messages in thread
From: Manivannan Sadhasivam @ 2026-07-15 13:35 UTC (permalink / raw)
  To: Hans Zhang
  Cc: bhelgaas, lpieralisi, kwilczynski, s-vadapalli, a-garg7, robh,
	linux-pci, linux-kernel

On Sat, May 30, 2026 at 11:30:45PM +0800, Hans Zhang wrote:
> Hi,
> 
> The DWC PCIe core and its many platform drivers repeatedly call
> dw_pcie_find_capability(pci, PCI_CAP_ID_EXP) to obtain the offset of the
> PCI Express Capability structure. This is wasteful and makes the code
> verbose. Some drivers even search for the offset in suspend/resume paths.
> 
> Add a cached pcie_cap field in struct dw_pcie and a helper
> dw_pcie_get_pcie_cap() to initialize it once at the point when the
> hardware is ready. Then replace all explicit capability searches with
> the cached value across the entire dwc subtree.
> 
> **Safety analysis: DBI access timing**
> The PCIe Capability offset is read from DBI configuration space. DBI
> registers are only accessible after the controller's clocks, resets, and
> power are enabled. The following call graph demonstrates that all
> dw_pcie_find_capability() replacements occur only after hardware is ready:
> 
> - For Root Complex mode:
>   dw_pcie_host_init()
>     ...
>     if (pp->ops->init)
>       pp->ops->init   [enables clocks/resets]
>     dw_pcie_get_pcie_cap()   [caches offset]
>     ...
>     dw_pcie_link_up
>       pci->ops->link_up
>     ...
>     dw_pcie_start_link
>       pci->ops->start_link
>     ...
>     pci_host_probe
>     if (pp->ops->post_init)
>       pp->ops->post_init
>     ...
> 
> - For Endpoint mode:
>   dw_pcie_ep_init()
>     ep->ops->pre_init()    [enables clocks/resets]
>     dw_pcie_get_pcie_cap()   [caches offset]
>     dw_pcie_ep_init_non_sticky_registers() etc.
> 
> - Some platform drivers (e.g., layerscape-ep, tegra194) need the offset
>   before calling dw_pcie_ep_init() / dw_pcie_host_init(). They already
>   enable hardware themselves and explicitly call dw_pcie_get_pcie_cap()
>   before the core caches it. This is safe and ensures the cached value is
>   available for all later users.
> 
> Thus, no DBI access occurs before hardware is operational.
> 

Not true. As Sashiko pointed out, EP platforms like Qcom, Tegra, enable
resources like clocks only after PERST# deassert. So any DBI access before that
will cause sync abort.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-07-15 13:35 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-30 15:30 [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Hans Zhang
2026-05-30 15:30 ` [PATCH v2 01/16] PCI: dwc: Add pcie_cap field and helper in designware header Hans Zhang
2026-05-30 15:30 ` [PATCH v2 02/16] PCI: dwc: Use cached PCIe capability offset in core Hans Zhang
2026-05-30 15:30 ` [PATCH v2 03/16] PCI: dwc: imx6: Use cached PCIe capability offset Hans Zhang
2026-05-30 15:30 ` [PATCH v2 04/16] PCI: dwc: layerscape-ep: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 05/16] PCI: dwc: meson: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 06/16] PCI: dwc: rockchip: " Hans Zhang
2026-06-01 17:11   ` Sebastian Reichel
2026-05-30 15:30 ` [PATCH v2 07/16] PCI: dwc: eswin: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 08/16] PCI: dwc: fu740: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 09/16] PCI: dwc: intel-gw: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 10/16] PCI: dwc: qcom-ep: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 11/16] PCI: dwc: qcom: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 12/16] PCI: dwc: sophgo: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 13/16] PCI: dwc: spacemit-k1: " Hans Zhang
2026-05-30 15:30 ` [PATCH v2 14/16] PCI: dwc: spear13xx: " Hans Zhang
2026-05-30 15:31 ` [PATCH v2 15/16] PCI: dwc: tegra194: " Hans Zhang
2026-05-30 15:31 ` [PATCH v2 16/16] PCI: dwc: ultrarisc: " Hans Zhang
2026-07-15 13:35 ` [PATCH v2 00/16] PCI: dwc: Cache PCIe capability offset and simplify drivers Manivannan Sadhasivam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®