From a8aeea1bf3c80cc87983689e0118770e019bd4f3 Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Wed, 11 Feb 2026 20:46:24 +0800 Subject: [PATCH 001/165] PCI/AER: Clear only error bits in PCIe Device Status Currently, pcie_clear_device_status() clears the entire PCIe Device Status register (PCI_EXP_DEVSTA) by writing back the value read from the register, which affects not only the error status bits but also other writable bits. According to PCIe r7.0, sec 7.5.3.5, this register contains: - RW1C error status bits (CED, NFED, FED, URD at bits 0-3): These are the four error status bits that need to be cleared. - Read-only bits (AUXPD at bit 4, TRPND at bit 5): Writing to these has no effect. - Emergency Power Reduction Detected (bit 6): A RW1C non-error bit introduced in PCIe r5.0 (2019). This is currently the only writable non-error bit in the Device Status register. Unconditionally clearing this bit can interfere with other software components that rely on this power management indication. - Reserved bits (RsvdZ): These bits are required to be written as zero. Writing 1s to them (as the current implementation may do) violates the specification. To prevent unintended side effects, modify pcie_clear_device_status() to only write 1s to the four error status bits (CED, NFED, FED, URD), leaving the Emergency Power Reduction Detected bit and reserved bits unaffected. Fixes: ec752f5d54d7 ("PCI/AER: Clear device status bits during ERR_FATAL and ERR_NONFATAL") Suggested-by: Lukas Wunner Signed-off-by: Shuai Xue Signed-off-by: Bjorn Helgaas Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Lukas Wunner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260211124624.49656-1-xueshuai@linux.alibaba.com --- drivers/pci/pci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..8e3e4e24c909 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2241,10 +2241,9 @@ EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); #ifdef CONFIG_PCIEAER void pcie_clear_device_status(struct pci_dev *dev) { - u16 sta; - - pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &sta); - pcie_capability_write_word(dev, PCI_EXP_DEVSTA, sta); + pcie_capability_write_word(dev, PCI_EXP_DEVSTA, + PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED | + PCI_EXP_DEVSTA_FED | PCI_EXP_DEVSTA_URD); } #endif From cc33985d26c92a5c908c0185239c59ec35b8637c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 16 Feb 2026 08:46:13 +0100 Subject: [PATCH 002/165] PCI/ASPM: Fix pci_clear_and_set_config_dword() usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When aspm_calc_l12_info() programs the L1 PM Substates Control 1 register fields Common_Mode_Restore_Time, LTR_L1.2_THRESHOLD_Value and _Scale, it invokes pci_clear_and_set_config_dword() in an incorrect way: For the bits to clear it selects those corresponding to the field. So far so good. But for the bits to set it passes a full register value. pci_clear_and_set_config_dword() performs a boolean OR operation which sets all bits of that value, not just the ones that were just cleared. Thus, when setting the LTR_L1.2_THRESHOLD_Value and _Scale on the child of an ASPM link, aspm_calc_l12_info() also sets the Common_Mode_Restore_Time. That's a spec violation: PCIe r7.0 sec 7.8.3.3 says this field is RsvdP for Upstream Ports. On Adrià's Pixelbook Eve, Common_Mode_Restore_Time of the Intel 7265 "Stone Peak" wifi card is zero, yet aspm_calc_l12_info() does not preserve the zero bits but instead programs the value calculated for the Root Port into the wifi card. Likewise, when setting the Common_Mode_Restore_Time on the Root Port, aspm_calc_l12_info() also changes the LTR_L1.2_THRESHOLD_Value and _Scale from the initial 163840 nsec to 237568 nsec (due to ORing those fields), only to reduce it afterwards to 106496 nsec. Amend all invocations of pci_clear_and_set_config_dword() to only set bits which are cleared. Finally, when setting the T_POWER_ON_Value and _Scale on the Root Port and the wifi card, aspm_calc_l12_info() fails to preserve bits declared RsvdP and instead overwrites them with zeroes. Replace pci_write_config_dword() with pci_clear_and_set_config_dword() to avoid this. Fixes: aeda9adebab8 ("PCI/ASPM: Configure L1 substate settings") Link: https://bugzilla.kernel.org/show_bug.cgi?id=220705#c22 Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Tested-by: Adrià Vilanova Martínez Cc: stable@vger.kernel.org # v4.11+ Link: https://patch.msgid.link/5c1752d7512eed0f4ea57b84b12d7ee08ca61fc5.1771226659.git.lukas@wunner.de --- drivers/pci/pcie/aspm.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 21f5d23e0b61..925373b98dff 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -706,22 +706,29 @@ static void aspm_calc_l12_info(struct pcie_link_state *link, } /* Program T_POWER_ON times in both ports */ - pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, ctl2); - pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, + PCI_L1SS_CTL2_T_PWR_ON_VALUE | + PCI_L1SS_CTL2_T_PWR_ON_SCALE, ctl2); + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL2, + PCI_L1SS_CTL2_T_PWR_ON_VALUE | + PCI_L1SS_CTL2_T_PWR_ON_SCALE, ctl2); /* Program Common_Mode_Restore_Time in upstream device */ pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1); + PCI_L1SS_CTL1_CM_RESTORE_TIME, + ctl1 & PCI_L1SS_CTL1_CM_RESTORE_TIME); /* Program LTR_L1.2_THRESHOLD time in both ports */ pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, PCI_L1SS_CTL1_LTR_L12_TH_VALUE | PCI_L1SS_CTL1_LTR_L12_TH_SCALE, - ctl1); + ctl1 & (PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE)); pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, PCI_L1SS_CTL1_LTR_L12_TH_VALUE | PCI_L1SS_CTL1_LTR_L12_TH_SCALE, - ctl1); + ctl1 & (PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE)); if (pl1_2_enables || cl1_2_enables) { pci_clear_and_set_config_dword(parent, From d3e996a596967a62c8a13a279221513461f6ab97 Mon Sep 17 00:00:00 2001 From: George Abraham P Date: Fri, 9 Jan 2026 10:59:23 +0530 Subject: [PATCH 003/165] PCI/TPH: Allow TPH enable for RCiEPs Previously, pcie_enable_tph() only enabled TLP Processing Hints (TPH) if both the Endpoint and its Root Port advertised TPH support. Root Complex Integrated Endpoints (RCiEPs) are directly integrated into a Root Complex and do not have an associated Root Port, so pcie_enable_tph() never enabled TPH for RCiEPs. PCIe r7.0 doesn't seem to include a way to learn whether a Root Complex supports TPH, but sec 2.2.7.1.1 says Functions that lack TPH support should ignore TPH, and maybe the same is true for Root Complexes: A Function that does not support the TPH Completer or Routing capability and receives a transaction with the TH bit [which indicates the presence of TPH in the TLP header] Set is required to ignore the TH bit and handle the Request in the same way as Requests of the same transaction type without the TH bit Set. Allow drivers to enable TPH for any RCiEP with a TPH Requester Capability. Fixes: f69767a1ada3 ("PCI: Add TLP Processing Hints (TPH) support") Signed-off-by: George Abraham P [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260109052923.1170070-1-george.abraham.p@intel.com --- drivers/pci/tph.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c index ca4f97be7538..e896b3958281 100644 --- a/drivers/pci/tph.c +++ b/drivers/pci/tph.c @@ -407,10 +407,13 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode) else pdev->tph_req_type = PCI_TPH_REQ_TPH_ONLY; - rp_req_type = get_rp_completer_type(pdev); + /* Check if the device is behind a Root Port */ + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) { + rp_req_type = get_rp_completer_type(pdev); - /* Final req_type is the smallest value of two */ - pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type); + /* Final req_type is the smallest value of two */ + pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type); + } if (pdev->tph_req_type == PCI_TPH_REQ_DISABLE) return -EINVAL; From 03e4905402ae60aa1d7a65770076bb2c1df8f6ac Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 11 Feb 2026 16:24:57 +0800 Subject: [PATCH 004/165] PCI/MSI: Clarify pci_free_irq_vectors() usage for managed devices Update pci_free_irq_vectors() documentation to clarify that drivers using pcim_enable_device() must not call pci_free_irq_vectors(). For legacy reasons, pcim_enable_device() switches several normally un-managed functions into managed mode. Currently, the only function affected in this way is pcim_setup_msi_release(), which results in automatic IRQ vector management. This behavior is dangerous and confusing. Drivers using pcim_enable_device() should rely on the automatic IRQ vector management and avoid calling pci_free_irq_vectors() manually. Suggested-by: Philipp Stanner Signed-off-by: Shawn Lin [bhelgaas: squash both updates to pci_free_irq_vectors() documentation] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/1770798299-202288-2-git-send-email-shawn.lin@rock-chips.com Link: https://patch.msgid.link/1770798299-202288-3-git-send-email-shawn.lin@rock-chips.com --- Documentation/PCI/msi-howto.rst | 7 +++++-- drivers/pci/msi/api.c | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst index 667ebe2156b4..844c1d3c395d 100644 --- a/Documentation/PCI/msi-howto.rst +++ b/Documentation/PCI/msi-howto.rst @@ -113,8 +113,11 @@ vectors, use the following function:: int pci_irq_vector(struct pci_dev *dev, unsigned int nr); -Any allocated resources should be freed before removing the device using -the following function:: +If the driver enables the device using pcim_enable_device(), the driver +shouldn't call pci_free_irq_vectors() because pcim_enable_device() +activates automatic management for IRQ vectors. Otherwise, the driver should +free any allocated IRQ vectors before removing the device using the following +function:: void pci_free_irq_vectors(struct pci_dev *dev); diff --git a/drivers/pci/msi/api.c b/drivers/pci/msi/api.c index 818d55fbad0d..c18559b6272c 100644 --- a/drivers/pci/msi/api.c +++ b/drivers/pci/msi/api.c @@ -370,6 +370,11 @@ EXPORT_SYMBOL(pci_irq_get_affinity); * Undo the interrupt vector allocations and possible device MSI/MSI-X * enablement earlier done through pci_alloc_irq_vectors_affinity() or * pci_alloc_irq_vectors(). + * + * WARNING: Do not call this function if the device has been enabled + * with pcim_enable_device(). In that case, IRQ vectors are automatically + * managed via pcim_msi_release() and calling pci_free_irq_vectors() can + * lead to double-free issues. */ void pci_free_irq_vectors(struct pci_dev *dev) { From 874b07eb0875729b9a47441e03b125d9fa735645 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 11 Feb 2026 16:24:59 +0800 Subject: [PATCH 005/165] PCI/MSI: Add TODO comment about legacy pcim_enable_device() side-effect Add a TODO comment in pci/msi/msi.c to document that the automatic IRQ vector management activated by pcim_enable_device() is a dangerous and confusing. Suggested-by: Philipp Stanner Signed-off-by: Shawn Lin Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/1770798299-202288-4-git-send-email-shawn.lin@rock-chips.com --- drivers/pci/msi/msi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index e2412175d7af..81d24a270a79 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -77,6 +77,16 @@ static void pcim_msi_release(void *pcidev) /* * Needs to be separate from pcim_release to prevent an ordering problem * vs. msi_device_data_release() in the MSI core code. + * + * TODO: Remove the legacy side-effect of pcim_enable_device() that + * activates automatic IRQ vector management. This design is dangerous + * and confusing because it switches normally un-managed functions + * into managed mode. Drivers should explicitly manage their IRQ vectors + * without this implicit behavior. + * + * The current implementation uses both pdev->is_managed and + * pdev->is_msi_managed flags, which adds unnecessary complexity. + * This should be simplified in a future kernel version. */ static int pcim_setup_msi_release(struct pci_dev *dev) { From cc04f2bfb9dae60b6e34d6bff75c26d4ec3237ce Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 17 Feb 2026 15:38:54 +0900 Subject: [PATCH 006/165] PCI: endpoint: pci-epf-vntb: Fix MSI doorbell IRQ unwind epf_ntb_db_bar_init_msi_doorbell() requests ntb->db_count doorbell IRQs and then performs additional MSI doorbell setup that may still fail. The error path unwinds the requested IRQs, but it uses a loop variable that is reused later in the function. When a later step fails, the unwind can run with an unexpected index value and leave some IRQs requested. Track the number of successfully requested IRQs separately and use that counter for the unwind so all previously requested IRQs are freed on failure. Fixes: dc693d606644 ("PCI: endpoint: pci-epf-vntb: Add MSI doorbell support") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260217063856.3759713-2-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 20a400e83439..52cf442ca1d9 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -527,20 +527,20 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, struct msi_msg *msg; size_t sz; int ret; - int i; + int i, req; ret = pci_epf_alloc_doorbell(epf, ntb->db_count); if (ret) return ret; - for (i = 0; i < ntb->db_count; i++) { - ret = request_irq(epf->db_msg[i].virq, epf_ntb_doorbell_handler, + for (req = 0; req < ntb->db_count; req++) { + ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler, 0, "pci_epf_vntb_db", ntb); if (ret) { dev_err(&epf->dev, "Failed to request doorbell IRQ: %d\n", - epf->db_msg[i].virq); + epf->db_msg[req].virq); goto err_free_irq; } } @@ -598,8 +598,8 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, return 0; err_free_irq: - for (i--; i >= 0; i--) - free_irq(epf->db_msg[i].virq, ntb); + for (req--; req >= 0; req--) + free_irq(epf->db_msg[req].virq, ntb); pci_epf_free_doorbell(ntb->epf); return ret; From e81fa70179aac6ac3a6636565d5d35968dca3900 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 17 Feb 2026 15:38:55 +0900 Subject: [PATCH 007/165] PCI: endpoint: pci-epf-test: Don't free doorbell IRQ unless requested pci_epf_test_doorbell_cleanup() unconditionally calls free_irq() for the doorbell virq, which can trigger "Trying to free already-free IRQ" warnings when the IRQ was never requested or when request_threaded_irq() failed. Move free_irq() out of pci_epf_test_doorbell_cleanup() and invoke it only after a successful request, so that free_irq() is not called for an unrequested IRQ. Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260217063856.3759713-3-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-test.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 582938b7b4f1..5893de370b6e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -715,7 +715,6 @@ static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test) struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar]; struct pci_epf *epf = epf_test->epf; - free_irq(epf->db_msg[0].virq, epf_test); reg->doorbell_bar = cpu_to_le32(NO_BAR); pci_epf_free_doorbell(epf); @@ -759,7 +758,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, &epf_test->db_bar.phys_addr, &offset); if (ret) - goto err_doorbell_cleanup; + goto err_free_irq; reg->doorbell_offset = cpu_to_le32(offset); @@ -769,12 +768,14 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); if (ret) - goto err_doorbell_cleanup; + goto err_free_irq; status |= STATUS_DOORBELL_ENABLE_SUCCESS; reg->status = cpu_to_le32(status); return; +err_free_irq: + free_irq(epf->db_msg[0].virq, epf_test); err_doorbell_cleanup: pci_epf_test_doorbell_cleanup(epf_test); set_status_err: @@ -794,6 +795,7 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test, if (bar < BAR_0) goto set_status_err; + free_irq(epf->db_msg[0].virq, epf_test); pci_epf_test_doorbell_cleanup(epf_test); /* From 1cba96c0a795124c3229293ed7b5b5765e66f259 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 17 Feb 2026 15:38:56 +0900 Subject: [PATCH 008/165] PCI: endpoint: pci-ep-msi: Fix error unwind and prevent double alloc pci_epf_alloc_doorbell() stores the allocated doorbell message array in epf->db_msg/epf->num_db before requesting MSI vectors. If MSI allocation fails, the array is freed but the EPF state may still point to freed memory. Clear epf->db_msg and epf->num_db on the MSI allocation failure path so that later cleanup cannot double-free the array and callers can retry allocation. Also return -EBUSY when doorbells have already been allocated to prevent leaking or overwriting an existing allocation. Fixes: 1c3b002c6bf6 ("PCI: endpoint: Add RC-to-EP doorbell support using platform MSI controller") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260217063856.3759713-4-den@valinux.co.jp --- drivers/pci/endpoint/pci-ep-msi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 51c19942a81e..1395919571f8 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -50,6 +50,9 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) return -EINVAL; } + if (epf->db_msg) + return -EBUSY; + domain = of_msi_map_get_device_domain(epc->dev.parent, 0, DOMAIN_BUS_PLATFORM_MSI); if (!domain) { @@ -79,6 +82,8 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) if (ret) { dev_err(dev, "Failed to allocate MSI\n"); kfree(msg); + epf->db_msg = NULL; + epf->num_db = 0; return ret; } From 9a940a3d08b25cf8e864785ee06b65756d6e4573 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Mon, 16 Feb 2026 00:03:32 +0900 Subject: [PATCH 009/165] PCI: endpoint: pci-epf-test: Advertise dynamic inbound mapping support The doorbell test requires the EPC driver to support dynamic inbound mapping so the host can map the doorbell target address into a BAR aperture. Expose epc_features->dynamic_inbound_mapping via a new CAP_DYNAMIC_INBOUND_MAPPING bit in the pci-epf-test capability register, so the host-side pci_endpoint_test driver can detect missing support and return -EOPNOTSUPP instead of running the test fruitlessly. Suggested-by: Niklas Cassel Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260215150334.3391943-2-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 5893de370b6e..6030ae1373b1 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -64,6 +64,7 @@ #define CAP_MSIX BIT(2) #define CAP_INTX BIT(3) #define CAP_SUBRANGE_MAPPING BIT(4) +#define CAP_DYNAMIC_INBOUND_MAPPING BIT(5) #define PCI_EPF_TEST_BAR_SUBRANGE_NSUB 2 @@ -1104,6 +1105,9 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf) if (epf_test->epc_features->intx_capable) caps |= CAP_INTX; + if (epf_test->epc_features->dynamic_inbound_mapping) + caps |= CAP_DYNAMIC_INBOUND_MAPPING; + if (epf_test->epc_features->dynamic_inbound_mapping && epf_test->epc_features->subrange_mapping) caps |= CAP_SUBRANGE_MAPPING; From 51fba4aa66192fa65a31f213218167d9af326f1e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Mon, 16 Feb 2026 00:03:33 +0900 Subject: [PATCH 010/165] misc: pci_endpoint_test: Gate doorbell test on dynamic inbound mapping The doorbell test relies on the endpoint being able to update inbound translations at runtime so the host can reach the doorbell target through a BAR mapping. If the endpoint does not advertise CAP_DYNAMIC_INBOUND_MAPPING, return -EOPNOTSUPP from PCITEST_DOORBELL. This avoids confusing failures in user space and kselftests when the required capability is not available. Suggested-by: Niklas Cassel Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260215150334.3391943-3-den@valinux.co.jp --- drivers/misc/pci_endpoint_test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 74ab5b5b9011..93cd57d20881 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -84,6 +84,7 @@ #define CAP_MSIX BIT(2) #define CAP_INTX BIT(3) #define CAP_SUBRANGE_MAPPING BIT(4) +#define CAP_DYNAMIC_INBOUND_MAPPING BIT(5) #define PCI_ENDPOINT_TEST_DB_BAR 0x34 #define PCI_ENDPOINT_TEST_DB_OFFSET 0x38 @@ -1060,6 +1061,9 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test) u32 addr; int left; + if (!(test->ep_caps & CAP_DYNAMIC_INBOUND_MAPPING)) + return -EOPNOTSUPP; + if (irq_type < PCITEST_IRQ_TYPE_INTX || irq_type > PCITEST_IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type\n"); From b4a31737679576dc8aa6de43d3c10bfad7d3f57e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Mon, 16 Feb 2026 00:03:34 +0900 Subject: [PATCH 011/165] selftests: pci_endpoint: Skip doorbell test when unsupported PCITEST_DOORBELL may return -EOPNOTSUPP when the endpoint does not advertise CAP_DYNAMIC_INBOUND_MAPPING. Treat this like other optional capabilities and skip the doorbell test instead of reporting a failure. Suggested-by: Niklas Cassel Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260215150334.3391943-4-den@valinux.co.jp --- tools/testing/selftests/pci_endpoint/pci_endpoint_test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index eecb776c33af..e0dbbb2af8c7 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -276,6 +276,8 @@ TEST_F(pcie_ep_doorbell, DOORBELL_TEST) ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); pci_ep_ioctl(PCITEST_DOORBELL, 0); + if (ret == -EOPNOTSUPP) + SKIP(return, "Doorbell test is not supported"); EXPECT_FALSE(ret) TH_LOG("Test failed for Doorbell\n"); } TEST_HARNESS_MAIN From f457c18d7904b22f8b6a9c6475161810085c34c9 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 29 Dec 2025 12:13:29 +0100 Subject: [PATCH 012/165] PCI: endpoint: Constify struct configfs_item_operations and configfs_group_operations 'struct configfs_item_operations' and 'configfs_group_operations' are not modified in this driver. Constifying these structures moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 27503 12184 256 39943 9c07 drivers/pci/endpoint/pci-ep-cfs.o After: ===== text data bss dec hex filename 27855 11832 256 39943 9c07 drivers/pci/endpoint/pci-ep-cfs.o Signed-off-by: Christophe JAILLET Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/f1f05f1c10c6caf37dd620fa12f508c53536996b.1765705512.git.christophe.jaillet@wanadoo.fr --- drivers/pci/endpoint/pci-ep-cfs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 0f3921f28f17..653ffc72dd39 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -84,7 +84,7 @@ static void pci_secondary_epc_epf_unlink(struct config_item *epf_item, pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE); } -static struct configfs_item_operations pci_secondary_epc_item_ops = { +static const struct configfs_item_operations pci_secondary_epc_item_ops = { .allow_link = pci_secondary_epc_epf_link, .drop_link = pci_secondary_epc_epf_unlink, }; @@ -148,7 +148,7 @@ static void pci_primary_epc_epf_unlink(struct config_item *epf_item, pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); } -static struct configfs_item_operations pci_primary_epc_item_ops = { +static const struct configfs_item_operations pci_primary_epc_item_ops = { .allow_link = pci_primary_epc_epf_link, .drop_link = pci_primary_epc_epf_unlink, }; @@ -256,7 +256,7 @@ static void pci_epc_epf_unlink(struct config_item *epc_item, pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE); } -static struct configfs_item_operations pci_epc_item_ops = { +static const struct configfs_item_operations pci_epc_item_ops = { .allow_link = pci_epc_epf_link, .drop_link = pci_epc_epf_unlink, }; @@ -507,7 +507,7 @@ static void pci_epf_release(struct config_item *item) kfree(epf_group); } -static struct configfs_item_operations pci_epf_ops = { +static const struct configfs_item_operations pci_epf_ops = { .allow_link = pci_epf_vepf_link, .drop_link = pci_epf_vepf_unlink, .release = pci_epf_release, @@ -657,7 +657,7 @@ static void pci_epf_drop(struct config_group *group, struct config_item *item) config_item_put(item); } -static struct configfs_group_operations pci_epf_group_ops = { +static const struct configfs_group_operations pci_epf_group_ops = { .make_group = &pci_epf_make, .drop_item = &pci_epf_drop, }; From 26cd5ca272a44031c4ff381928aa1b816829d18d Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Mon, 5 Jan 2026 16:56:06 +0900 Subject: [PATCH 013/165] PCI: endpoint: pci-epf-vntb: Use array_index_nospec() on mws_size[] access Follow common kernel idioms for indices derived from configfs attributes and suppress Smatch warnings: epf_ntb_mw1_show() warn: potential spectre issue 'ntb->mws_size' [r] epf_ntb_mw1_store() warn: potential spectre issue 'ntb->mws_size' [w] Also fix the error message for out-of-range MW indices and %lld format for unsigned values. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://patch.msgid.link/20260105075606.1253697-1-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 52cf442ca1d9..71a35f575cc1 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -995,17 +995,19 @@ static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ struct config_group *group = to_config_group(item); \ struct epf_ntb *ntb = to_epf_ntb(group); \ struct device *dev = &ntb->epf->dev; \ - int win_no; \ + int win_no, idx; \ \ if (sscanf(#_name, "mw%d", &win_no) != 1) \ return -EINVAL; \ \ - if (win_no <= 0 || win_no > ntb->num_mws) { \ - dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + idx = win_no - 1; \ + if (idx < 0 || idx >= ntb->num_mws) { \ + dev_err(dev, "MW%d out of range (num_mws=%d)\n", \ + win_no, ntb->num_mws); \ return -EINVAL; \ } \ - \ - return sprintf(page, "%lld\n", ntb->mws_size[win_no - 1]); \ + idx = array_index_nospec(idx, ntb->num_mws); \ + return sprintf(page, "%llu\n", ntb->mws_size[idx]); \ } #define EPF_NTB_MW_W(_name) \ @@ -1015,7 +1017,7 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ struct config_group *group = to_config_group(item); \ struct epf_ntb *ntb = to_epf_ntb(group); \ struct device *dev = &ntb->epf->dev; \ - int win_no; \ + int win_no, idx; \ u64 val; \ int ret; \ \ @@ -1026,12 +1028,14 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ if (sscanf(#_name, "mw%d", &win_no) != 1) \ return -EINVAL; \ \ - if (win_no <= 0 || win_no > ntb->num_mws) { \ - dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + idx = win_no - 1; \ + if (idx < 0 || idx >= ntb->num_mws) { \ + dev_err(dev, "MW%d out of range (num_mws=%d)\n", \ + win_no, ntb->num_mws); \ return -EINVAL; \ } \ - \ - ntb->mws_size[win_no - 1] = val; \ + idx = array_index_nospec(idx, ntb->num_mws); \ + ntb->mws_size[idx] = val; \ \ return len; \ } From 8eaff52fc101c1f6b3215db93bba02c815155806 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 19 Feb 2026 23:56:33 +0900 Subject: [PATCH 014/165] PCI: endpoint: pci-epf-vntb: Return -ERANGE for out-of-range MW index The mw1..mw4 configfs attributes are only valid when the MW index is within the configured num_mws range. Return -ERANGE instead of -EINVAL when a configfs MW size attribute is accessed with an out-of-range MW index. Suggested-by: Manivannan Sadhasivam Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260219145633.4191325-1-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 71a35f575cc1..148a3b160812 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1004,7 +1004,7 @@ static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ if (idx < 0 || idx >= ntb->num_mws) { \ dev_err(dev, "MW%d out of range (num_mws=%d)\n", \ win_no, ntb->num_mws); \ - return -EINVAL; \ + return -ERANGE; \ } \ idx = array_index_nospec(idx, ntb->num_mws); \ return sprintf(page, "%llu\n", ntb->mws_size[idx]); \ @@ -1032,7 +1032,7 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ if (idx < 0 || idx >= ntb->num_mws) { \ dev_err(dev, "MW%d out of range (num_mws=%d)\n", \ win_no, ntb->num_mws); \ - return -EINVAL; \ + return -ERANGE; \ } \ idx = array_index_nospec(idx, ntb->num_mws); \ ntb->mws_size[idx] = val; \ From aa8671af0c380c15989b88325a2d5a6c5341771d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 24 Feb 2026 12:10:43 +0100 Subject: [PATCH 015/165] PCI/PTM: Drop pci_enable_ptm() granularity parameter No pci_enable_ptm() callers supply the "granularity" pointer where the clock granularity would be returned. Drop the unused pci_enable_ptm() parameter. Signed-off-by: Mika Westerberg [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260224111044.3487873-5-mika.westerberg@linux.intel.com --- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- drivers/net/ethernet/intel/idpf/idpf_main.c | 2 +- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- drivers/pci/pcie/ptm.c | 11 +++-------- include/linux/pci.h | 4 ++-- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index ebf48feffb30..b35c4e4ecd2a 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -5028,7 +5028,7 @@ static int ice_init(struct ice_pf *pf) } if (pf->hw.mac_type == ICE_MAC_E830) { - err = pci_enable_ptm(pf->pdev, NULL); + err = pci_enable_ptm(pf->pdev); if (err) dev_dbg(dev, "PCIe PTM not supported by PCIe bus/controller\n"); } diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c index 0dd741dcfcdb..ab3c409e587b 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_main.c +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c @@ -257,7 +257,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_free; } - err = pci_enable_ptm(pdev, NULL); + err = pci_enable_ptm(pdev); if (err) pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n"); diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 27e5c2109138..b030acf94ac4 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7123,7 +7123,7 @@ static int igc_probe(struct pci_dev *pdev, if (err) goto err_pci_reg; - err = pci_enable_ptm(pdev, NULL); + err = pci_enable_ptm(pdev); if (err < 0) dev_info(&pdev->dev, "PCIe PTM not supported by PCIe bus/controller\n"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index fdc3ba20912e..0b94d4ed0ef6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -960,7 +960,7 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev, mlx5_pci_vsc_init(dev); - pci_enable_ptm(pdev, NULL); + pci_enable_ptm(pdev); return 0; diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index 91a598ed534c..2c848ae4f15f 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -88,7 +88,7 @@ void pci_ptm_init(struct pci_dev *dev) if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM) - pci_enable_ptm(dev, NULL); + pci_enable_ptm(dev); } void pci_save_ptm_state(struct pci_dev *dev) @@ -182,15 +182,13 @@ static int __pci_enable_ptm(struct pci_dev *dev) /** * pci_enable_ptm() - Enable Precision Time Measurement * @dev: PCI device - * @granularity: pointer to return granularity * - * Enable Precision Time Measurement for @dev. If successful and - * @granularity is non-NULL, return the Effective Granularity. + * Enable Precision Time Measurement for @dev. * * Return: zero if successful, or -EINVAL if @dev lacks a PTM Capability or * is not a PTM Root and lacks an upstream path of PTM-enabled devices. */ -int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) +int pci_enable_ptm(struct pci_dev *dev) { int rc; char clock_desc[8]; @@ -201,9 +199,6 @@ int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) dev->ptm_enabled = 1; - if (granularity) - *granularity = dev->ptm_granularity; - switch (dev->ptm_granularity) { case 0: snprintf(clock_desc, sizeof(clock_desc), "unknown"); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1c270f1d5123..8aaa72dcb164 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1975,11 +1975,11 @@ struct pci_ptm_debugfs { }; #ifdef CONFIG_PCIE_PTM -int pci_enable_ptm(struct pci_dev *dev, u8 *granularity); +int pci_enable_ptm(struct pci_dev *dev); void pci_disable_ptm(struct pci_dev *dev); bool pcie_ptm_enabled(struct pci_dev *dev); #else -static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity) +static inline int pci_enable_ptm(struct pci_dev *dev) { return -EINVAL; } static inline void pci_disable_ptm(struct pci_dev *dev) { } static inline bool pcie_ptm_enabled(struct pci_dev *dev) From e36262c5e6c25e19d7d082936f63b3a8e62739f9 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 12 Jan 2026 20:17:11 +0100 Subject: [PATCH 016/165] PCI: layerscape: Allow to compile as module The layerscape pcie host controller could also be compiled as module. Add the necessary infrastructure to allow building as module instead of only as builtin driver. Since the driver doesn't expose an irqchip controller, it is also safe to be removed during runtime. Signed-off-by: Sascha Hauer Signed-off-by: Steffen Trumtrar [mani: added a note about driver removability] Signed-off-by: Manivannan Sadhasivam Acked-by: Roy Zang Link: https://patch.msgid.link/20260112-v6-19-topic-layerscape-pcie-v1-1-1cd863fce50e@pengutronix.de --- drivers/pci/controller/dwc/Kconfig | 2 +- drivers/pci/controller/dwc/pci-layerscape.c | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d0aa031397fa..1a69b38c2a28 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -121,7 +121,7 @@ config PCI_IMX6_EP DesignWare core functions to implement the driver. config PCI_LAYERSCAPE - bool "Freescale Layerscape PCIe controller (host mode)" + tristate "Freescale Layerscape PCIe controller (host mode)" depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) depends on PCI_MSI select PCIE_DW_HOST diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index a44b5c256d6e..14d6ac4fc53f 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -403,8 +404,16 @@ static const struct dev_pm_ops ls_pcie_pm_ops = { NOIRQ_SYSTEM_SLEEP_PM_OPS(ls_pcie_suspend_noirq, ls_pcie_resume_noirq) }; +static void ls_pcie_remove(struct platform_device *pdev) +{ + struct ls_pcie *pcie = platform_get_drvdata(pdev); + + dw_pcie_host_deinit(&pcie->pci->pp); +} + static struct platform_driver ls_pcie_driver = { .probe = ls_pcie_probe, + .remove = ls_pcie_remove, .driver = { .name = "layerscape-pcie", .of_match_table = ls_pcie_of_match, @@ -412,4 +421,9 @@ static struct platform_driver ls_pcie_driver = { .pm = &ls_pcie_pm_ops, }, }; -builtin_platform_driver(ls_pcie_driver); +module_platform_driver(ls_pcie_driver); + +MODULE_AUTHOR("Minghuan Lian "); +MODULE_DESCRIPTION("Layerscape PCIe host controller driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, ls_pcie_of_match); From 3b55079d6387805ede687e234d84669aeb0f7e98 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 24 Jan 2026 23:42:45 +0800 Subject: [PATCH 017/165] PCI: imx6: Fix device node reference leak in imx_pcie_probe() In imx_pcie_probe(), of_parse_phandle() returns the device node pointer with increased refcount. The pointer reference must be dropped by the caller when it's no longer needed. However, imx_pcie_probe() doesn't drop the reference, causing reference leak. Fix this by using the __free(device_node) cleanup handler to drop the reference when the function goes out of scope. Fixes: 1df82ec46600 ("PCI: imx: Add workaround for e10728, IMX7d PCIe PLL failure") Signed-off-by: Felix Gu [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Acked-by: Richard Zhu Link: https://patch.msgid.link/20260124-pci_imx6-v2-1-acb8d5187683@gmail.com --- drivers/pci/controller/dwc/pci-imx6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index a5b8d0b71677..81a7093494c8 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1647,7 +1647,6 @@ static int imx_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw_pcie *pci; struct imx_pcie *imx_pcie; - struct device_node *np; struct device_node *node = dev->of_node; int i, ret, domain; u16 val; @@ -1674,7 +1673,8 @@ static int imx_pcie_probe(struct platform_device *pdev) pci->pp.ops = &imx_pcie_host_dw_pme_ops; /* Find the PHY if one is defined, only imx7d uses it */ - np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); + struct device_node *np __free(device_node) = + of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); if (np) { struct resource res; From eed390775470ff0db32cce37a681f3acc2b941c3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 17 Feb 2026 17:01:42 +0530 Subject: [PATCH 018/165] PCI: dwc: Proceed with system suspend even if the endpoint doesn't respond with PME_TO_Ack message PCIe spec r7.0, sec 5.3.3.2.1, recommends proceeding with L2/L3 sequence even if one or devices do not respond with PME_TO_Ack message after 10ms timeout. So just print a warning if the timeout happens and proceed with the system suspend. Reported-by: Neil Armstrong Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Tested-by: Neil Armstrong # on SM8650-HDK Reviewed-by: Frank Li Link: https://patch.msgid.link/20260217113142.9140-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-designware-host.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 6ae6189e9b8a..ba183fc3e77c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1256,9 +1256,13 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) PCIE_PME_TO_L2_TIMEOUT_US/10, PCIE_PME_TO_L2_TIMEOUT_US, false, pci); if (ret) { - /* Only log message when LTSSM isn't in DETECT or POLL */ - dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val); - return ret; + /* + * Failure is non-fatal since spec r7.0, sec 5.3.3.2.1, + * recommends proceeding with L2/L3 sequence even if one or more + * devices do not respond with PME_TO_Ack after 10ms timeout. + */ + dev_warn(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val); + ret = 0; } /* From e1092d5e15e6a9b168bf830af9a26d7ea17cd57d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 24 Feb 2026 12:10:44 +0100 Subject: [PATCH 019/165] PCI/PTM: Do not enable PTM automatically for Root and Switch Upstream Ports Currently we enable PTM automatically for Root and Switch Upstream Ports if the advertised capabilities support the relevant role. However, there are a few issues with this. First of all, if there is no Endpoint that actually needs the PTM functionality, this is just wasting link bandwidth. There are just a couple of drivers calling pci_ptm_enable() in the tree. Secondly, we do the enablement in pci_ptm_init() that is called pretty early for the Switch Upstream Port before Downstream Ports are even enumerated. Since the Upstream Port configuration affects the whole Switch, enabling it this early might cause PTM requests to be sent. We actually do see effects of this: pcieport 0000:00:07.1: pciehp: Slot(6-1): Card present pcieport 0000:00:07.1: pciehp: Slot(6-1): Link Up pci 0000:2c:00.0: [8086:5786] type 01 class 0x060400 PCIe Switch Upstream Port ... pci 0000:2c:00.0: PTM enabled, 4ns granularity At this point we have only enumerated the Switch Upstream Port and now PTM got enabled which immediately triggers a flood of errors: pcieport 0000:00:07.1: AER: Multiple Uncorrectable (Non-Fatal) error message received from 0000:00:07.1 pcieport 0000:00:07.1: PCIe Bus Error: severity=Uncorrectable (Non-Fatal), type=Transaction Layer, (Receiver ID) pcieport 0000:00:07.1: device [8086:d44f] error status/mask=00200000/00000000 pcieport 0000:00:07.1: [21] ACSViol (First) pcieport 0000:00:07.1: AER: TLP Header: 0x34000000 0x00000052 0x00000000 0x00000000 pcieport 0000:00:07.1: AER: device recovery successful pcieport 0000:00:07.1: AER: Uncorrectable (Non-Fatal) error message received from 0000:00:07.1 In the above TLP Header the Requester ID is 0 which causes an error as we have ACS Source Validation enabled. Change the PTM enablement to happen at the time pci_enable_ptm() is called. It will try to enable PTM first for upstream devices before enabling for the Endpoint itself. For disable path we need to keep count of how many times PTM has been enabled and disable it only on the last, so change the dev->ptm_enabled to a counter (and rename it to dev->ptm_enable_cnt analogous to dev->pci_enable_cnt). Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260224111044.3487873-6-mika.westerberg@linux.intel.com --- drivers/pci/pcie/ptm.c | 70 ++++++++++++++++++++++++------------------ include/linux/pci.h | 2 +- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index 2c848ae4f15f..a41ffd1914de 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -52,6 +52,7 @@ void pci_ptm_init(struct pci_dev *dev) return; dev->ptm_cap = ptm; + atomic_set(&dev->ptm_enable_cnt, 0); pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u32)); pci_read_config_dword(dev, ptm + PCI_PTM_CAP, &cap); @@ -85,10 +86,6 @@ void pci_ptm_init(struct pci_dev *dev) dev->ptm_responder = 1; if (cap & PCI_PTM_CAP_REQ) dev->ptm_requester = 1; - - if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || - pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM) - pci_enable_ptm(dev); } void pci_save_ptm_state(struct pci_dev *dev) @@ -129,26 +126,11 @@ void pci_restore_ptm_state(struct pci_dev *dev) static int __pci_enable_ptm(struct pci_dev *dev) { u16 ptm = dev->ptm_cap; - struct pci_dev *ups; u32 ctrl; if (!ptm) return -EINVAL; - /* - * A device uses local PTM Messages to request time information - * from a PTM Root that's farther upstream. Every device along the - * path must support PTM and have it enabled so it can handle the - * messages. Therefore, if this device is not a PTM Root, the - * upstream link partner must have PTM enabled before we can enable - * PTM. - */ - if (!dev->ptm_root) { - ups = pci_upstream_ptm(dev); - if (!ups || !ups->ptm_enabled) - return -EINVAL; - } - switch (pci_pcie_type(dev)) { case PCI_EXP_TYPE_ROOT_PORT: if (!dev->ptm_root) @@ -193,11 +175,35 @@ int pci_enable_ptm(struct pci_dev *dev) int rc; char clock_desc[8]; - rc = __pci_enable_ptm(dev); - if (rc) - return rc; + /* + * A device uses local PTM Messages to request time information + * from a PTM Root that's farther upstream. Every device along + * the path must support PTM and have it enabled so it can + * handle the messages. Therefore, if this device is not a PTM + * Root, the upstream link partner must have PTM enabled before + * we can enable PTM. + */ + if (!dev->ptm_root) { + struct pci_dev *parent; - dev->ptm_enabled = 1; + parent = pci_upstream_ptm(dev); + if (!parent) + return -EINVAL; + /* Enable PTM for the parent */ + rc = pci_enable_ptm(parent); + if (rc) + return rc; + } + + /* Already enabled? */ + if (atomic_inc_return(&dev->ptm_enable_cnt) > 1) + return 0; + + rc = __pci_enable_ptm(dev); + if (rc) { + atomic_dec(&dev->ptm_enable_cnt); + return rc; + } switch (dev->ptm_granularity) { case 0: @@ -239,27 +245,31 @@ static void __pci_disable_ptm(struct pci_dev *dev) */ void pci_disable_ptm(struct pci_dev *dev) { - if (dev->ptm_enabled) { + struct pci_dev *parent; + + if (atomic_dec_and_test(&dev->ptm_enable_cnt)) __pci_disable_ptm(dev); - dev->ptm_enabled = 0; - } + + parent = pci_upstream_ptm(dev); + if (parent) + pci_disable_ptm(parent); } EXPORT_SYMBOL(pci_disable_ptm); /* - * Disable PTM, but preserve dev->ptm_enabled so we silently re-enable it on + * Disable PTM, but preserve dev->ptm_enable_cnt so we silently re-enable it on * resume if necessary. */ void pci_suspend_ptm(struct pci_dev *dev) { - if (dev->ptm_enabled) + if (atomic_read(&dev->ptm_enable_cnt)) __pci_disable_ptm(dev); } /* If PTM was enabled before suspend, re-enable it when resuming */ void pci_resume_ptm(struct pci_dev *dev) { - if (dev->ptm_enabled) + if (atomic_read(&dev->ptm_enable_cnt)) __pci_enable_ptm(dev); } @@ -268,7 +278,7 @@ bool pcie_ptm_enabled(struct pci_dev *dev) if (!dev) return false; - return dev->ptm_enabled; + return atomic_read(&dev->ptm_enable_cnt); } EXPORT_SYMBOL(pcie_ptm_enabled); diff --git a/include/linux/pci.h b/include/linux/pci.h index 8aaa72dcb164..c620d4b6c52e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -518,7 +518,7 @@ struct pci_dev { unsigned int ptm_root:1; unsigned int ptm_responder:1; unsigned int ptm_requester:1; - unsigned int ptm_enabled:1; + atomic_t ptm_enable_cnt; u8 ptm_granularity; #endif #ifdef CONFIG_PCI_MSI From 36bfc3642b19a98f1302aed4437c331df9b481f0 Mon Sep 17 00:00:00 2001 From: Daniel Hodges Date: Fri, 6 Feb 2026 15:05:29 -0500 Subject: [PATCH 020/165] PCI: epf-mhi: Return 0, not remaining timeout, when eDMA ops complete pci_epf_mhi_edma_read() and pci_epf_mhi_edma_write() start DMA operations and wait for completion with a timeout. On successful completion, they previously returned the remaining timeout, which callers may treat as an error. In particular, mhi_ep_ring_add_element(), which calls pci_epf_mhi_edma_write() via mhi_cntrl->write_sync(), interprets any non-zero return value as failure. Return 0 on success instead of the remaining timeout to prevent mhi_ep_ring_add_element() from treating successful completion as an error. Fixes: 7b99aaaddabb ("PCI: epf-mhi: Add eDMA support") Signed-off-by: Daniel Hodges [mani: changed commit log as per https://lore.kernel.org/linux-pci/20260227191510.GA3904799@bhelgaas] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krishna Chaitanya Chundru Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260206200529.10784-1-git@danielhodges.dev --- drivers/pci/endpoint/functions/pci-epf-mhi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index f9cf18aa5b34..7f5326925ed5 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -367,6 +367,8 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, dev_err(dev, "DMA transfer timeout\n"); dmaengine_terminate_sync(chan); ret = -ETIMEDOUT; + } else { + ret = 0; } err_unmap: @@ -438,6 +440,8 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, dev_err(dev, "DMA transfer timeout\n"); dmaengine_terminate_sync(chan); ret = -ETIMEDOUT; + } else { + ret = 0; } err_unmap: From 5e5ea39ff55297fc9d6338f26346c2a7738a78ea Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Feb 2026 15:21:12 +0100 Subject: [PATCH 021/165] PCI: dwc: Remove not-going-to-be-supported code for Baikal SoC As noticed in the discussion [1] the Baikal SoC and platforms are not going to be finalized, hence remove stale code. Signed-off-by: Andy Shevchenko Signed-off-by: Manivannan Sadhasivam Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/lkml/22b92ddf-6321-41b5-8073-f9c7064d3432@infradead.org/ [1] Link: https://patch.msgid.link/20260220142600.2397070-1-andriy.shevchenko@linux.intel.com --- .../bindings/pci/baikal,bt1-pcie.yaml | 168 ----- drivers/pci/controller/dwc/Kconfig | 9 - drivers/pci/controller/dwc/Makefile | 1 - drivers/pci/controller/dwc/pcie-bt1.c | 645 ------------------ 4 files changed, 823 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml delete mode 100644 drivers/pci/controller/dwc/pcie-bt1.c diff --git a/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml deleted file mode 100644 index 8eaa07ae9774..000000000000 --- a/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml +++ /dev/null @@ -1,168 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/pci/baikal,bt1-pcie.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 PCIe Root Port Controller - -maintainers: - - Serge Semin - -description: - Embedded into Baikal-T1 SoC Root Complex controller with a single port - activated. It's based on the DWC RC PCIe v4.60a IP-core, which is configured - to have just a single Root Port function and is capable of establishing the - link up to Gen.3 speed on x4 lanes. It doesn't have embedded clock and reset - control module, so the proper interface initialization is supposed to be - performed by software. There four in- and four outbound iATU regions - which can be used to emit all required TLP types on the PCIe bus. - -allOf: - - $ref: /schemas/pci/snps,dw-pcie.yaml# - -properties: - compatible: - const: baikal,bt1-pcie - - reg: - description: - DBI, DBI2 and at least 4KB outbound iATU-capable region for the - peripheral devices CFG-space access. - maxItems: 3 - - reg-names: - items: - - const: dbi - - const: dbi2 - - const: config - - interrupts: - description: - MSI, AER, PME, Hot-plug, Link Bandwidth Management, Link Equalization - request and eight Read/Write eDMA IRQ lines are available. - maxItems: 14 - - interrupt-names: - items: - - const: dma0 - - const: dma1 - - const: dma2 - - const: dma3 - - const: dma4 - - const: dma5 - - const: dma6 - - const: dma7 - - const: msi - - const: aer - - const: pme - - const: hp - - const: bw_mg - - const: l_eq - - clocks: - description: - DBI (attached to the APB bus), AXI-bus master and slave interfaces - are fed up by the dedicated application clocks. A common reference - clock signal is supposed to be attached to the corresponding Ref-pad - of the SoC. It will be redistributed amongst the controller core - sub-modules (pipe, core, aux, etc). - maxItems: 4 - - clock-names: - items: - - const: dbi - - const: mstr - - const: slv - - const: ref - - resets: - description: - A comprehensive controller reset logic is supposed to be implemented - by software, so almost all the possible application and core reset - signals are exposed via the system CCU module. - maxItems: 9 - - reset-names: - items: - - const: mstr - - const: slv - - const: pwr - - const: hot - - const: phy - - const: core - - const: pipe - - const: sticky - - const: non-sticky - - baikal,bt1-syscon: - $ref: /schemas/types.yaml#/definitions/phandle - description: - Phandle to the Baikal-T1 System Controller DT node. It's required to - access some additional PM, Reset-related and LTSSM signals. - - num-lanes: - maximum: 4 - - max-link-speed: - maximum: 3 - -required: - - compatible - - reg - - reg-names - - interrupts - - interrupt-names - -unevaluatedProperties: false - -examples: - - | - #include - #include - - pcie@1f052000 { - compatible = "baikal,bt1-pcie"; - device_type = "pci"; - reg = <0x1f052000 0x1000>, <0x1f053000 0x1000>, <0x1bdbf000 0x1000>; - reg-names = "dbi", "dbi2", "config"; - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x81000000 0 0x00000000 0x1bdb0000 0 0x00008000>, - <0x82000000 0 0x20000000 0x08000000 0 0x13db0000>; - bus-range = <0x0 0xff>; - - interrupts = , - , - , - , - , - , - , - , - , - , - , - , - , - ; - interrupt-names = "dma0", "dma1", "dma2", "dma3", - "dma4", "dma5", "dma6", "dma7", - "msi", "aer", "pme", "hp", "bw_mg", - "l_eq"; - - clocks = <&ccu_sys 1>, <&ccu_axi 6>, <&ccu_axi 7>, <&clk_pcie>; - clock-names = "dbi", "mstr", "slv", "ref"; - - resets = <&ccu_axi 6>, <&ccu_axi 7>, <&ccu_sys 7>, <&ccu_sys 10>, - <&ccu_sys 4>, <&ccu_sys 6>, <&ccu_sys 5>, <&ccu_sys 8>, - <&ccu_sys 9>; - reset-names = "mstr", "slv", "pwr", "hot", "phy", "core", "pipe", - "sticky", "non-sticky"; - - reset-gpios = <&port0 0 GPIO_ACTIVE_LOW>; - - num-lanes = <4>; - max-link-speed = <3>; - }; -... diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d0aa031397fa..ffacc03b6042 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -84,15 +84,6 @@ config PCIE_ARTPEC6_EP Enables support for the PCIe controller in the ARTPEC-6 SoC to work in endpoint mode. This uses the DesignWare core. -config PCIE_BT1 - tristate "Baikal-T1 PCIe controller" - depends on MIPS_BAIKAL_T1 || COMPILE_TEST - depends on PCI_MSI - select PCIE_DW_HOST - help - Enables support for the PCIe controller in the Baikal-T1 SoC to work - in host mode. It's based on the Synopsys DWC PCIe v4.60a IP-core. - config PCI_IMX6 bool diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 67ba59c02038..4867f30ab7c3 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o obj-$(CONFIG_PCIE_AMD_MDB) += pcie-amd-mdb.o -obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c deleted file mode 100644 index 1340edc18d12..000000000000 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Vadim Vlasov - * Serge Semin - * - * Baikal-T1 PCIe controller driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -/* Baikal-T1 System CCU control registers */ -#define BT1_CCU_PCIE_CLKC 0x140 -#define BT1_CCU_PCIE_REQ_PCS_CLK BIT(16) -#define BT1_CCU_PCIE_REQ_MAC_CLK BIT(17) -#define BT1_CCU_PCIE_REQ_PIPE_CLK BIT(18) - -#define BT1_CCU_PCIE_RSTC 0x144 -#define BT1_CCU_PCIE_REQ_LINK_RST BIT(13) -#define BT1_CCU_PCIE_REQ_SMLH_RST BIT(14) -#define BT1_CCU_PCIE_REQ_PHY_RST BIT(16) -#define BT1_CCU_PCIE_REQ_CORE_RST BIT(24) -#define BT1_CCU_PCIE_REQ_STICKY_RST BIT(26) -#define BT1_CCU_PCIE_REQ_NSTICKY_RST BIT(27) - -#define BT1_CCU_PCIE_PMSC 0x148 -#define BT1_CCU_PCIE_LTSSM_STATE_MASK GENMASK(5, 0) -#define BT1_CCU_PCIE_LTSSM_DET_QUIET 0x00 -#define BT1_CCU_PCIE_LTSSM_DET_ACT 0x01 -#define BT1_CCU_PCIE_LTSSM_POLL_ACT 0x02 -#define BT1_CCU_PCIE_LTSSM_POLL_COMP 0x03 -#define BT1_CCU_PCIE_LTSSM_POLL_CONF 0x04 -#define BT1_CCU_PCIE_LTSSM_PRE_DET_QUIET 0x05 -#define BT1_CCU_PCIE_LTSSM_DET_WAIT 0x06 -#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_START 0x07 -#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_ACEPT 0x08 -#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_WAIT 0x09 -#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_ACEPT 0x0a -#define BT1_CCU_PCIE_LTSSM_CFG_COMPLETE 0x0b -#define BT1_CCU_PCIE_LTSSM_CFG_IDLE 0x0c -#define BT1_CCU_PCIE_LTSSM_RCVR_LOCK 0x0d -#define BT1_CCU_PCIE_LTSSM_RCVR_SPEED 0x0e -#define BT1_CCU_PCIE_LTSSM_RCVR_RCVRCFG 0x0f -#define BT1_CCU_PCIE_LTSSM_RCVR_IDLE 0x10 -#define BT1_CCU_PCIE_LTSSM_L0 0x11 -#define BT1_CCU_PCIE_LTSSM_L0S 0x12 -#define BT1_CCU_PCIE_LTSSM_L123_SEND_IDLE 0x13 -#define BT1_CCU_PCIE_LTSSM_L1_IDLE 0x14 -#define BT1_CCU_PCIE_LTSSM_L2_IDLE 0x15 -#define BT1_CCU_PCIE_LTSSM_L2_WAKE 0x16 -#define BT1_CCU_PCIE_LTSSM_DIS_ENTRY 0x17 -#define BT1_CCU_PCIE_LTSSM_DIS_IDLE 0x18 -#define BT1_CCU_PCIE_LTSSM_DISABLE 0x19 -#define BT1_CCU_PCIE_LTSSM_LPBK_ENTRY 0x1a -#define BT1_CCU_PCIE_LTSSM_LPBK_ACTIVE 0x1b -#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT 0x1c -#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT_TOUT 0x1d -#define BT1_CCU_PCIE_LTSSM_HOT_RST_ENTRY 0x1e -#define BT1_CCU_PCIE_LTSSM_HOT_RST 0x1f -#define BT1_CCU_PCIE_LTSSM_RCVR_EQ0 0x20 -#define BT1_CCU_PCIE_LTSSM_RCVR_EQ1 0x21 -#define BT1_CCU_PCIE_LTSSM_RCVR_EQ2 0x22 -#define BT1_CCU_PCIE_LTSSM_RCVR_EQ3 0x23 -#define BT1_CCU_PCIE_SMLH_LINKUP BIT(6) -#define BT1_CCU_PCIE_RDLH_LINKUP BIT(7) -#define BT1_CCU_PCIE_PM_LINKSTATE_L0S BIT(8) -#define BT1_CCU_PCIE_PM_LINKSTATE_L1 BIT(9) -#define BT1_CCU_PCIE_PM_LINKSTATE_L2 BIT(10) -#define BT1_CCU_PCIE_L1_PENDING BIT(12) -#define BT1_CCU_PCIE_REQ_EXIT_L1 BIT(14) -#define BT1_CCU_PCIE_LTSSM_RCVR_EQ BIT(15) -#define BT1_CCU_PCIE_PM_DSTAT_MASK GENMASK(18, 16) -#define BT1_CCU_PCIE_PM_PME_EN BIT(20) -#define BT1_CCU_PCIE_PM_PME_STATUS BIT(21) -#define BT1_CCU_PCIE_AUX_PM_EN BIT(22) -#define BT1_CCU_PCIE_AUX_PWR_DET BIT(23) -#define BT1_CCU_PCIE_WAKE_DET BIT(24) -#define BT1_CCU_PCIE_TURNOFF_REQ BIT(30) -#define BT1_CCU_PCIE_TURNOFF_ACK BIT(31) - -#define BT1_CCU_PCIE_GENC 0x14c -#define BT1_CCU_PCIE_LTSSM_EN BIT(1) -#define BT1_CCU_PCIE_DBI2_MODE BIT(2) -#define BT1_CCU_PCIE_MGMT_EN BIT(3) -#define BT1_CCU_PCIE_RXLANE_FLIP_EN BIT(16) -#define BT1_CCU_PCIE_TXLANE_FLIP_EN BIT(17) -#define BT1_CCU_PCIE_SLV_XFER_PEND BIT(24) -#define BT1_CCU_PCIE_RCV_XFER_PEND BIT(25) -#define BT1_CCU_PCIE_DBI_XFER_PEND BIT(26) -#define BT1_CCU_PCIE_DMA_XFER_PEND BIT(27) - -#define BT1_CCU_PCIE_LTSSM_LINKUP(_pmsc) \ -({ \ - int __state = FIELD_GET(BT1_CCU_PCIE_LTSSM_STATE_MASK, _pmsc); \ - __state >= BT1_CCU_PCIE_LTSSM_L0 && __state <= BT1_CCU_PCIE_LTSSM_L2_WAKE; \ -}) - -/* Baikal-T1 PCIe specific control registers */ -#define BT1_PCIE_AXI2MGM_LANENUM 0xd04 -#define BT1_PCIE_AXI2MGM_LANESEL_MASK GENMASK(3, 0) - -#define BT1_PCIE_AXI2MGM_ADDRCTL 0xd08 -#define BT1_PCIE_AXI2MGM_PHYREG_ADDR_MASK GENMASK(20, 0) -#define BT1_PCIE_AXI2MGM_READ_FLAG BIT(29) -#define BT1_PCIE_AXI2MGM_DONE BIT(30) -#define BT1_PCIE_AXI2MGM_BUSY BIT(31) - -#define BT1_PCIE_AXI2MGM_WRITEDATA 0xd0c -#define BT1_PCIE_AXI2MGM_WDATA GENMASK(15, 0) - -#define BT1_PCIE_AXI2MGM_READDATA 0xd10 -#define BT1_PCIE_AXI2MGM_RDATA GENMASK(15, 0) - -/* Generic Baikal-T1 PCIe interface resources */ -#define BT1_PCIE_NUM_APP_CLKS ARRAY_SIZE(bt1_pcie_app_clks) -#define BT1_PCIE_NUM_CORE_CLKS ARRAY_SIZE(bt1_pcie_core_clks) -#define BT1_PCIE_NUM_APP_RSTS ARRAY_SIZE(bt1_pcie_app_rsts) -#define BT1_PCIE_NUM_CORE_RSTS ARRAY_SIZE(bt1_pcie_core_rsts) - -/* PCIe bus setup delays and timeouts */ -#define BT1_PCIE_RST_DELAY_MS 100 -#define BT1_PCIE_RUN_DELAY_US 100 -#define BT1_PCIE_REQ_DELAY_US 1 -#define BT1_PCIE_REQ_TIMEOUT_US 1000 -#define BT1_PCIE_LNK_DELAY_US 1000 -#define BT1_PCIE_LNK_TIMEOUT_US 1000000 - -static const enum dw_pcie_app_clk bt1_pcie_app_clks[] = { - DW_PCIE_DBI_CLK, DW_PCIE_MSTR_CLK, DW_PCIE_SLV_CLK, -}; - -static const enum dw_pcie_core_clk bt1_pcie_core_clks[] = { - DW_PCIE_REF_CLK, -}; - -static const enum dw_pcie_app_rst bt1_pcie_app_rsts[] = { - DW_PCIE_MSTR_RST, DW_PCIE_SLV_RST, -}; - -static const enum dw_pcie_core_rst bt1_pcie_core_rsts[] = { - DW_PCIE_NON_STICKY_RST, DW_PCIE_STICKY_RST, DW_PCIE_CORE_RST, - DW_PCIE_PIPE_RST, DW_PCIE_PHY_RST, DW_PCIE_HOT_RST, DW_PCIE_PWR_RST, -}; - -struct bt1_pcie { - struct dw_pcie dw; - struct platform_device *pdev; - struct regmap *sys_regs; -}; -#define to_bt1_pcie(_dw) container_of(_dw, struct bt1_pcie, dw) - -/* - * Baikal-T1 MMIO space must be read/written by the dword-aligned - * instructions. Note the methods are optimized to have the dword operations - * performed with minimum overhead as the most frequently used ones. - */ -static int bt1_pcie_read_mmio(void __iomem *addr, int size, u32 *val) -{ - unsigned int ofs = (uintptr_t)addr & 0x3; - - if (!IS_ALIGNED((uintptr_t)addr, size)) - return -EINVAL; - - *val = readl(addr - ofs) >> ofs * BITS_PER_BYTE; - if (size == 4) { - return 0; - } else if (size == 2) { - *val &= 0xffff; - return 0; - } else if (size == 1) { - *val &= 0xff; - return 0; - } - - return -EINVAL; -} - -static int bt1_pcie_write_mmio(void __iomem *addr, int size, u32 val) -{ - unsigned int ofs = (uintptr_t)addr & 0x3; - u32 tmp, mask; - - if (!IS_ALIGNED((uintptr_t)addr, size)) - return -EINVAL; - - if (size == 4) { - writel(val, addr); - return 0; - } else if (size == 2 || size == 1) { - mask = GENMASK(size * BITS_PER_BYTE - 1, 0); - tmp = readl(addr - ofs) & ~(mask << ofs * BITS_PER_BYTE); - tmp |= (val & mask) << ofs * BITS_PER_BYTE; - writel(tmp, addr - ofs); - return 0; - } - - return -EINVAL; -} - -static u32 bt1_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size) -{ - int ret; - u32 val; - - ret = bt1_pcie_read_mmio(base + reg, size, &val); - if (ret) { - dev_err(pci->dev, "Read DBI address failed\n"); - return ~0U; - } - - return val; -} - -static void bt1_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val) -{ - int ret; - - ret = bt1_pcie_write_mmio(base + reg, size, val); - if (ret) - dev_err(pci->dev, "Write DBI address failed\n"); -} - -static void bt1_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val) -{ - struct bt1_pcie *btpci = to_bt1_pcie(pci); - int ret; - - regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, - BT1_CCU_PCIE_DBI2_MODE, BT1_CCU_PCIE_DBI2_MODE); - - ret = bt1_pcie_write_mmio(base + reg, size, val); - if (ret) - dev_err(pci->dev, "Write DBI2 address failed\n"); - - regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, - BT1_CCU_PCIE_DBI2_MODE, 0); -} - -static int bt1_pcie_start_link(struct dw_pcie *pci) -{ - struct bt1_pcie *btpci = to_bt1_pcie(pci); - u32 val; - int ret; - - /* - * Enable LTSSM and make sure it was able to establish both PHY and - * data links. This procedure shall work fine to reach 2.5 GT/s speed. - */ - regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, - BT1_CCU_PCIE_LTSSM_EN, BT1_CCU_PCIE_LTSSM_EN); - - ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, - (val & BT1_CCU_PCIE_SMLH_LINKUP), - BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); - if (ret) { - dev_err(pci->dev, "LTSSM failed to set PHY link up\n"); - return ret; - } - - ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, - (val & BT1_CCU_PCIE_RDLH_LINKUP), - BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); - if (ret) { - dev_err(pci->dev, "LTSSM failed to set data link up\n"); - return ret; - } - - /* - * Activate direct speed change after the link is established in an - * attempt to reach a higher bus performance (up to Gen.3 - 8.0 GT/s). - * This is required at least to get 8.0 GT/s speed. - */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); - - ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, - BT1_CCU_PCIE_LTSSM_LINKUP(val), - BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); - if (ret) - dev_err(pci->dev, "LTSSM failed to get into L0 state\n"); - - return ret; -} - -static void bt1_pcie_stop_link(struct dw_pcie *pci) -{ - struct bt1_pcie *btpci = to_bt1_pcie(pci); - - regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, - BT1_CCU_PCIE_LTSSM_EN, 0); -} - -static const struct dw_pcie_ops bt1_pcie_ops = { - .read_dbi = bt1_pcie_read_dbi, - .write_dbi = bt1_pcie_write_dbi, - .write_dbi2 = bt1_pcie_write_dbi2, - .start_link = bt1_pcie_start_link, - .stop_link = bt1_pcie_stop_link, -}; - -static struct pci_ops bt1_pci_ops = { - .map_bus = dw_pcie_own_conf_map_bus, - .read = pci_generic_config_read32, - .write = pci_generic_config_write32, -}; - -static int bt1_pcie_get_resources(struct bt1_pcie *btpci) -{ - struct device *dev = btpci->dw.dev; - int i; - - /* DBI access is supposed to be performed by the dword-aligned IOs */ - btpci->dw.pp.bridge->ops = &bt1_pci_ops; - - /* These CSRs are in MMIO so we won't check the regmap-methods status */ - btpci->sys_regs = - syscon_regmap_lookup_by_phandle(dev->of_node, "baikal,bt1-syscon"); - if (IS_ERR(btpci->sys_regs)) - return dev_err_probe(dev, PTR_ERR(btpci->sys_regs), - "Failed to get syscon\n"); - - /* Make sure all the required resources have been specified */ - for (i = 0; i < BT1_PCIE_NUM_APP_CLKS; i++) { - if (!btpci->dw.app_clks[bt1_pcie_app_clks[i]].clk) { - dev_err(dev, "App clocks set is incomplete\n"); - return -ENOENT; - } - } - - for (i = 0; i < BT1_PCIE_NUM_CORE_CLKS; i++) { - if (!btpci->dw.core_clks[bt1_pcie_core_clks[i]].clk) { - dev_err(dev, "Core clocks set is incomplete\n"); - return -ENOENT; - } - } - - for (i = 0; i < BT1_PCIE_NUM_APP_RSTS; i++) { - if (!btpci->dw.app_rsts[bt1_pcie_app_rsts[i]].rstc) { - dev_err(dev, "App resets set is incomplete\n"); - return -ENOENT; - } - } - - for (i = 0; i < BT1_PCIE_NUM_CORE_RSTS; i++) { - if (!btpci->dw.core_rsts[bt1_pcie_core_rsts[i]].rstc) { - dev_err(dev, "Core resets set is incomplete\n"); - return -ENOENT; - } - } - - return 0; -} - -static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init) -{ - struct device *dev = btpci->dw.dev; - struct dw_pcie *pci = &btpci->dw; - int ret; - - /* Disable LTSSM for sure */ - regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, - BT1_CCU_PCIE_LTSSM_EN, 0); - - /* - * Application reset controls are trigger-based so assert the core - * resets only. - */ - ret = reset_control_bulk_assert(DW_PCIE_NUM_CORE_RSTS, pci->core_rsts); - if (ret) - dev_err(dev, "Failed to assert core resets\n"); - - /* - * Clocks are disabled by default at least in accordance with the clk - * enable counter value on init stage. - */ - if (!init) { - clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); - - clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); - } - - /* The peripheral devices are unavailable anyway so reset them too */ - gpiod_set_value_cansleep(pci->pe_rst, 1); - - /* Make sure all the resets are settled */ - msleep(BT1_PCIE_RST_DELAY_MS); -} - -/* - * Implements the cold reset procedure in accordance with the reference manual - * and available PM signals. - */ -static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci) -{ - struct device *dev = btpci->dw.dev; - struct dw_pcie *pci = &btpci->dw; - u32 val; - int ret; - - /* First get out of the Power/Hot reset state */ - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert PHY reset\n"); - return ret; - } - - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert hot reset\n"); - goto err_assert_pwr_rst; - } - - /* Wait for the PM-core to stop requesting the PHY reset */ - ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, - !(val & BT1_CCU_PCIE_REQ_PHY_RST), - BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); - if (ret) { - dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n"); - goto err_assert_hot_rst; - } - - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert PHY reset\n"); - goto err_assert_hot_rst; - } - - /* Clocks can be now enabled, but the ref one is crucial at this stage */ - ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks); - if (ret) { - dev_err(dev, "Failed to enable app clocks\n"); - goto err_assert_phy_rst; - } - - ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); - if (ret) { - dev_err(dev, "Failed to enable ref clocks\n"); - goto err_disable_app_clk; - } - - /* Wait for the PM to stop requesting the controller core reset */ - ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, - !(val & BT1_CCU_PCIE_REQ_CORE_RST), - BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); - if (ret) { - dev_err(dev, "Timed out waiting for PM to stop core resetting\n"); - goto err_disable_core_clk; - } - - /* PCS-PIPE interface and controller core can be now activated */ - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert PIPE reset\n"); - goto err_disable_core_clk; - } - - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert core reset\n"); - goto err_assert_pipe_rst; - } - - /* It's recommended to reset the core and application logic together */ - ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts); - if (ret) { - dev_err(dev, "Failed to reset app domain\n"); - goto err_assert_core_rst; - } - - /* Sticky/Non-sticky CSR flags can be now unreset too */ - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert sticky reset\n"); - goto err_assert_core_rst; - } - - ret = reset_control_deassert(pci->core_rsts[DW_PCIE_NON_STICKY_RST].rstc); - if (ret) { - dev_err(dev, "Failed to deassert non-sticky reset\n"); - goto err_assert_sticky_rst; - } - - /* Activate the PCIe bus peripheral devices */ - gpiod_set_value_cansleep(pci->pe_rst, 0); - - /* Make sure the state is settled (LTSSM is still disabled though) */ - usleep_range(BT1_PCIE_RUN_DELAY_US, BT1_PCIE_RUN_DELAY_US + 100); - - return 0; - -err_assert_sticky_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); - -err_assert_core_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); - -err_assert_pipe_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); - -err_disable_core_clk: - clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); - -err_disable_app_clk: - clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); - -err_assert_phy_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); - -err_assert_hot_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); - -err_assert_pwr_rst: - reset_control_assert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); - - return ret; -} - -static int bt1_pcie_host_init(struct dw_pcie_rp *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct bt1_pcie *btpci = to_bt1_pcie(pci); - int ret; - - ret = bt1_pcie_get_resources(btpci); - if (ret) - return ret; - - bt1_pcie_full_stop_bus(btpci, true); - - return bt1_pcie_cold_start_bus(btpci); -} - -static void bt1_pcie_host_deinit(struct dw_pcie_rp *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct bt1_pcie *btpci = to_bt1_pcie(pci); - - bt1_pcie_full_stop_bus(btpci, false); -} - -static const struct dw_pcie_host_ops bt1_pcie_host_ops = { - .init = bt1_pcie_host_init, - .deinit = bt1_pcie_host_deinit, -}; - -static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev) -{ - struct bt1_pcie *btpci; - - btpci = devm_kzalloc(&pdev->dev, sizeof(*btpci), GFP_KERNEL); - if (!btpci) - return ERR_PTR(-ENOMEM); - - btpci->pdev = pdev; - - platform_set_drvdata(pdev, btpci); - - return btpci; -} - -static int bt1_pcie_add_port(struct bt1_pcie *btpci) -{ - struct device *dev = &btpci->pdev->dev; - int ret; - - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - if (ret) - return ret; - - btpci->dw.version = DW_PCIE_VER_460A; - btpci->dw.dev = dev; - btpci->dw.ops = &bt1_pcie_ops; - - btpci->dw.pp.num_vectors = MAX_MSI_IRQS; - btpci->dw.pp.ops = &bt1_pcie_host_ops; - - dw_pcie_cap_set(&btpci->dw, REQ_RES); - - ret = dw_pcie_host_init(&btpci->dw.pp); - - return dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n"); -} - -static void bt1_pcie_del_port(struct bt1_pcie *btpci) -{ - dw_pcie_host_deinit(&btpci->dw.pp); -} - -static int bt1_pcie_probe(struct platform_device *pdev) -{ - struct bt1_pcie *btpci; - - btpci = bt1_pcie_create_data(pdev); - if (IS_ERR(btpci)) - return PTR_ERR(btpci); - - return bt1_pcie_add_port(btpci); -} - -static void bt1_pcie_remove(struct platform_device *pdev) -{ - struct bt1_pcie *btpci = platform_get_drvdata(pdev); - - bt1_pcie_del_port(btpci); -} - -static const struct of_device_id bt1_pcie_of_match[] = { - { .compatible = "baikal,bt1-pcie" }, - {}, -}; -MODULE_DEVICE_TABLE(of, bt1_pcie_of_match); - -static struct platform_driver bt1_pcie_driver = { - .probe = bt1_pcie_probe, - .remove = bt1_pcie_remove, - .driver = { - .name = "bt1-pcie", - .of_match_table = bt1_pcie_of_match, - }, -}; -module_platform_driver(bt1_pcie_driver); - -MODULE_AUTHOR("Serge Semin "); -MODULE_DESCRIPTION("Baikal-T1 PCIe driver"); -MODULE_LICENSE("GPL"); From 56435b70f778995c696d53624ba93a429aa38432 Mon Sep 17 00:00:00 2001 From: Rakuram Eswaran Date: Wed, 24 Dec 2025 00:10:03 +0530 Subject: [PATCH 022/165] PCI: amd-mdb: Correct IRQ number in INTx error message The INTx devm_request_irq() failure path logs an incorrect IRQ number. The printed 'irq' variable refers to a previous MDB interrupt mapping and does not correspond to the INTx IRQ being requested. Fix the error message to report pcie->intx_irq, which is the IRQ associated with the failing request. Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202512230112.AaiGqMWM-lkp@intel.com/ Reported-by: Dan Carpenter Signed-off-by: Rakuram Eswaran Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Sai Krishna Musham Link: https://patch.msgid.link/20251223184003.32950-1-rakuram.e96@gmail.com --- drivers/pci/controller/dwc/pcie-amd-mdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 3c6e837465bb..7e50e11fbffd 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -389,7 +389,7 @@ static int amd_mdb_setup_irq(struct amd_mdb_pcie *pcie, IRQF_NO_THREAD, NULL, pcie); if (err) { dev_err(dev, "Failed to request INTx IRQ %d, err=%d\n", - irq, err); + pcie->intx_irq, err); return err; } From 806140e9a33218f22188fe5019c7874aa78d81f8 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 26 Feb 2026 10:25:45 -0800 Subject: [PATCH 023/165] PCI: Avoid FLR for AMD NPU device The AMD NPU device (PCI Device IDs 0x1502 and 0x17f0) advertises FLR support. However, triggering an FLR causes the device to hang. Signed-off-by: Lizhi Hou Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260226182545.3057330-1-lizhi.hou@amd.com --- drivers/pci/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 48946cca4be7..757a296eae41 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5603,6 +5603,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap); * AMD Starship/Matisse HD Audio Controller 0x1487 * AMD Starship USB 3.0 Host Controller 0x148c * AMD Matisse USB 3.0 Host Controller 0x149c + * AMD Neural Processing Unit 0x1502 0x17f0 * Intel 82579LM Gigabit Ethernet Controller 0x1502 * Intel 82579V Gigabit Ethernet Controller 0x1503 * Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter @@ -5615,6 +5616,8 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1487, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x148c, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x149c, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x7901, quirk_no_flr); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1502, quirk_no_flr); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr); From 0da63230d3ec1ec5fcc443a2314233e95bfece54 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 26 Feb 2026 17:41:38 +0900 Subject: [PATCH 024/165] PCI: endpoint: pci-epf-vntb: Remove duplicate resource teardown epf_ntb_epc_destroy() duplicates the teardown that the caller is supposed to perform later. This leads to an oops when .allow_link fails or when .drop_link is performed. The following is an example oops of the former case: Unable to handle kernel paging request at virtual address dead000000000108 [...] [dead000000000108] address between user and kernel address ranges Internal error: Oops: 0000000096000044 [#1] SMP [...] Call trace: pci_epc_remove_epf+0x78/0xe0 (P) pci_primary_epc_epf_link+0x88/0xa8 configfs_symlink+0x1f4/0x5a0 vfs_symlink+0x134/0x1d8 do_symlinkat+0x88/0x138 __arm64_sys_symlinkat+0x74/0xe0 [...] Remove the helper, and drop pci_epc_put(). EPC device refcounting is tied to the configfs EPC group lifetime, and pci_epc_put() in the .drop_link path is sufficient. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260226084142.2226875-2-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 148a3b160812..42c870ee3956 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -763,19 +763,6 @@ static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws) } } -/** - * epf_ntb_epc_destroy() - Cleanup NTB EPC interface - * @ntb: NTB device that facilitates communication between HOST and VHOST - * - * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces - */ -static void epf_ntb_epc_destroy(struct epf_ntb *ntb) -{ - pci_epc_remove_epf(ntb->epf->epc, ntb->epf, 0); - pci_epc_put(ntb->epf->epc); -} - - /** * epf_ntb_is_bar_used() - Check if a bar is used in the ntb configuration * @ntb: NTB device that facilitates communication between HOST and VHOST @@ -1529,7 +1516,7 @@ static int epf_ntb_bind(struct pci_epf *epf) ret = epf_ntb_init_epc_bar(ntb); if (ret) { dev_err(dev, "Failed to create NTB EPC\n"); - goto err_bar_init; + return ret; } ret = epf_ntb_config_spad_bar_alloc(ntb); @@ -1569,9 +1556,6 @@ static int epf_ntb_bind(struct pci_epf *epf) err_bar_alloc: epf_ntb_config_spad_bar_free(ntb); -err_bar_init: - epf_ntb_epc_destroy(ntb); - return ret; } @@ -1587,7 +1571,6 @@ static void epf_ntb_unbind(struct pci_epf *epf) epf_ntb_epc_cleanup(ntb); epf_ntb_config_spad_bar_free(ntb); - epf_ntb_epc_destroy(ntb); pci_unregister_driver(&vntb_pci_driver); } From 3446beddba450c8d6f9aca2f028712ac527fead3 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 26 Feb 2026 17:41:39 +0900 Subject: [PATCH 025/165] PCI: endpoint: pci-epf-ntb: Remove duplicate resource teardown epf_ntb_epc_destroy() duplicates the teardown that the caller is supposed to do later. This leads to an oops when .allow_link fails or when .drop_link is performed. Remove the helper. Also drop pci_epc_put(). EPC device refcounting is tied to configfs EPC group lifetime, and pci_epc_put() in the .drop_link path is sufficient. Fixes: 8b821cf76150 ("PCI: endpoint: Add EP function driver to provide NTB functionality") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260226084142.2226875-3-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-ntb.c | 56 +------------------- 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index a3a588e522e7..2bdcc35b652c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -1494,47 +1494,6 @@ static int epf_ntb_db_mw_bar_init(struct epf_ntb *ntb, return ret; } -/** - * epf_ntb_epc_destroy_interface() - Cleanup NTB EPC interface - * @ntb: NTB device that facilitates communication between HOST1 and HOST2 - * @type: PRIMARY interface or SECONDARY interface - * - * Unbind NTB function device from EPC and relinquish reference to pci_epc - * for each of the interface. - */ -static void epf_ntb_epc_destroy_interface(struct epf_ntb *ntb, - enum pci_epc_interface_type type) -{ - struct epf_ntb_epc *ntb_epc; - struct pci_epc *epc; - struct pci_epf *epf; - - if (type < 0) - return; - - epf = ntb->epf; - ntb_epc = ntb->epc[type]; - if (!ntb_epc) - return; - epc = ntb_epc->epc; - pci_epc_remove_epf(epc, epf, type); - pci_epc_put(epc); -} - -/** - * epf_ntb_epc_destroy() - Cleanup NTB EPC interface - * @ntb: NTB device that facilitates communication between HOST1 and HOST2 - * - * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces - */ -static void epf_ntb_epc_destroy(struct epf_ntb *ntb) -{ - enum pci_epc_interface_type type; - - for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) - epf_ntb_epc_destroy_interface(ntb, type); -} - /** * epf_ntb_epc_create_interface() - Create and initialize NTB EPC interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 @@ -1614,15 +1573,8 @@ static int epf_ntb_epc_create(struct epf_ntb *ntb) ret = epf_ntb_epc_create_interface(ntb, epf->sec_epc, SECONDARY_INTERFACE); - if (ret) { + if (ret) dev_err(dev, "SECONDARY intf: Fail to create NTB EPC\n"); - goto err_epc_create; - } - - return 0; - -err_epc_create: - epf_ntb_epc_destroy_interface(ntb, PRIMARY_INTERFACE); return ret; } @@ -1887,7 +1839,7 @@ static int epf_ntb_bind(struct pci_epf *epf) ret = epf_ntb_init_epc_bar(ntb); if (ret) { dev_err(dev, "Failed to create NTB EPC\n"); - goto err_bar_init; + return ret; } ret = epf_ntb_config_spad_bar_alloc_interface(ntb); @@ -1909,9 +1861,6 @@ static int epf_ntb_bind(struct pci_epf *epf) err_bar_alloc: epf_ntb_config_spad_bar_free(ntb); -err_bar_init: - epf_ntb_epc_destroy(ntb); - return ret; } @@ -1927,7 +1876,6 @@ static void epf_ntb_unbind(struct pci_epf *epf) epf_ntb_epc_cleanup(ntb); epf_ntb_config_spad_bar_free(ntb); - epf_ntb_epc_destroy(ntb); } #define EPF_NTB_R(_name) \ From d799984233a50abd2667a7d17a9a710a3f10ebe2 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 26 Feb 2026 17:41:40 +0900 Subject: [PATCH 026/165] PCI: endpoint: pci-epf-vntb: Stop cmd_handler work in epf_ntb_epc_cleanup Disable the delayed work before clearing BAR mappings and doorbells to avoid running the handler after resources have been torn down. Unable to handle kernel paging request at virtual address ffff800083f46004 [...] Internal error: Oops: 0000000096000007 [#1] SMP [...] Call trace: epf_ntb_cmd_handler+0x54/0x200 [pci_epf_vntb] (P) process_one_work+0x154/0x3b0 worker_thread+0x2c8/0x400 kthread+0x148/0x210 ret_from_fork+0x10/0x20 Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260226084142.2226875-4-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 42c870ee3956..805353528967 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -942,6 +942,7 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) */ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { + disable_delayed_work_sync(&ntb->cmd_handler); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); epf_ntb_db_bar_clear(ntb); epf_ntb_config_sspad_bar_clear(ntb); From 88ce49abc2185da3d08da9f71290d46a393d3876 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Tue, 24 Feb 2026 10:39:19 -0800 Subject: [PATCH 027/165] PCI: endpoint: Fix typo in pci_epf_add_vepf() kernel-doc The function description in kernel-doc refers to pci_epf_add_epf(), but the correct function name is pci_epf_add_vepf(). Update it to match the implementation. Signed-off-by: Alok Tiwari [mani: commit log] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260224183927.1369124-1-alok.a.tiwari@oracle.com --- drivers/pci/endpoint/pci-epf-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 83fd20c11238..6987343c9e61 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -149,7 +149,7 @@ EXPORT_SYMBOL_GPL(pci_epf_bind); * @epf_vf: the virtual EP function to be added * * A physical endpoint function can be associated with multiple virtual - * endpoint functions. Invoke pci_epf_add_epf() to add a virtual PCI endpoint + * endpoint functions. Invoke pci_epf_add_vepf() to add a virtual PCI endpoint * function to a physical PCI endpoint function. */ int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf) From 6bf2305ea846868dc1ff9004eb3f61a6590d8431 Mon Sep 17 00:00:00 2001 From: Randolph Lin Date: Wed, 25 Feb 2026 16:55:01 +0800 Subject: [PATCH 028/165] dt-bindings: PCI: Add Andes QiLai PCIe support Add the Andes QiLai PCIe node, which includes 3 Root Complexes. Only one example is required in the DTS bindings YAML file. Signed-off-by: Randolph Lin Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260225085504.3757601-2-randolph@andestech.com --- .../bindings/pci/andestech,qilai-pcie.yaml | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml b/Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml new file mode 100644 index 000000000000..97ba97fdc5a9 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/andestech,qilai-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andes QiLai PCIe host controller + +description: + Andes QiLai PCIe host controller is based on the Synopsys DesignWare + PCI core. + +maintainers: + - Randolph Lin + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + const: andestech,qilai-pcie + + reg: + items: + - description: Data Bus Interface (DBI) registers. + - description: APB registers. + - description: PCIe configuration space region. + + reg-names: + items: + - const: dbi + - const: apb + - const: config + + dma-coherent: true + + ranges: + maxItems: 2 + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + +required: + - reg + - reg-names + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@80000000 { + compatible = "andestech,qilai-pcie"; + device_type = "pci"; + reg = <0x0 0x80000000 0x0 0x20000000>, + <0x0 0x04000000 0x0 0x00001000>, + <0x0 0x00000000 0x0 0x00010000>; + reg-names = "dbi", "apb", "config"; + dma-coherent; + + linux,pci-domain = <0>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x02000000 0x00 0x10000000 0x00 0x10000000 0x00 0xf0000000>, + <0x43000000 0x01 0x00000000 0x01 0x00000000 0x02 0x00000000>; + + #interrupt-cells = <1>; + interrupts = <0xf>; + interrupt-names = "msi"; + interrupt-parent = <&plic0>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 1 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>; + }; + }; +... From df5d8fb6fe55754bc2956e501a9e6acaca5af7d9 Mon Sep 17 00:00:00 2001 From: Randolph Lin Date: Wed, 25 Feb 2026 16:55:03 +0800 Subject: [PATCH 029/165] PCI: qilai: Add Andes QiLai SoC PCIe host driver support Add driver support for DesignWare based PCIe controller in Andes QiLai SoC. The driver only supports the Root Complex mode. Signed-off-by: Randolph Lin [mani: squashed the MAINTAINERS change] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260225085504.3757601-4-randolph@andestech.com --- MAINTAINERS | 7 + drivers/pci/controller/dwc/Kconfig | 11 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-andes-qilai.c | 197 ++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-andes-qilai.c diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..02619b84e6ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20128,6 +20128,13 @@ S: Supported F: Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml F: drivers/pci/controller/pcie-altera.c +PCI DRIVER FOR ANDES QILAI PCIE +M: Randolph Lin +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml +F: drivers/pci/controller/dwc/pcie-andes-qilai.c + PCI DRIVER FOR APPLIEDMICRO XGENE M: Toan Le L: linux-pci@vger.kernel.org diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d0aa031397fa..7496eaa06e1c 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -61,6 +61,17 @@ config PCI_MESON and therefore the driver re-uses the DesignWare core functions to implement the driver. +config PCIE_ANDES_QILAI + tristate "Andes QiLai PCIe controller" + depends on ARCH_ANDES || COMPILE_TEST + depends on PCI_MSI + select PCIE_DW_HOST + help + Say Y here to enable PCIe controller support on Andes QiLai SoCs, + which operate in Root Complex mode. The Andes QiLai SoC PCIe + controller is based on DesignWare IP and therefore the driver + re-uses the DesignWare core functions to implement the driver. + config PCIE_ARTPEC6 bool diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 67ba59c02038..9e61458dff00 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o obj-$(CONFIG_PCIE_AMD_MDB) += pcie-amd-mdb.o +obj-$(CONFIG_PCIE_ANDES_QILAI) += pcie-andes-qilai.o obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o diff --git a/drivers/pci/controller/dwc/pcie-andes-qilai.c b/drivers/pci/controller/dwc/pcie-andes-qilai.c new file mode 100644 index 000000000000..bd1588be44e0 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-andes-qilai.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the PCIe Controller in QiLai from Andes + * + * Copyright (C) 2026 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCIE_INTR_CONTROL1 0x15c +#define PCIE_MSI_CTRL_INT_EN BIT(28) + +#define PCIE_LOGIC_COHERENCY_CONTROL3 0x8e8 + +/* + * Refer to Table A4-5 (Memory type encoding) in the + * AMBA AXI and ACE Protocol Specification. + * + * The selected value corresponds to the Memory type field: + * "Write-back, Read and Write-allocate". + * + * The last three rows in the table A4-5 in + * AMBA AXI and ACE Protocol Specification: + * ARCACHE AWCACHE Memory type + * ------------------------------------------------------------------ + * 1111 (0111) 0111 Write-back Read-allocate + * 1011 1111 (1011) Write-back Write-allocate + * 1111 1111 Write-back Read and Write-allocate (selected) + */ +#define IOCP_ARCACHE 0b1111 +#define IOCP_AWCACHE 0b1111 + +#define PCIE_CFG_MSTR_ARCACHE_MODE GENMASK(6, 3) +#define PCIE_CFG_MSTR_AWCACHE_MODE GENMASK(14, 11) +#define PCIE_CFG_MSTR_ARCACHE_VALUE GENMASK(22, 19) +#define PCIE_CFG_MSTR_AWCACHE_VALUE GENMASK(30, 27) + +#define PCIE_GEN_CONTROL2 0x54 +#define PCIE_CFG_LTSSM_EN BIT(0) + +#define PCIE_REGS_PCIE_SII_PM_STATE 0xc0 +#define SMLH_LINK_UP BIT(6) +#define RDLH_LINK_UP BIT(7) + +struct qilai_pcie { + struct dw_pcie pci; + void __iomem *apb_base; +}; + +#define to_qilai_pcie(_pci) container_of(_pci, struct qilai_pcie, pci) + +static bool qilai_pcie_link_up(struct dw_pcie *pci) +{ + struct qilai_pcie *pcie = to_qilai_pcie(pci); + u32 val; + + val = readl(pcie->apb_base + PCIE_REGS_PCIE_SII_PM_STATE); + + return FIELD_GET(SMLH_LINK_UP, val) && FIELD_GET(RDLH_LINK_UP, val); +} + +static int qilai_pcie_start_link(struct dw_pcie *pci) +{ + struct qilai_pcie *pcie = to_qilai_pcie(pci); + u32 val; + + val = readl(pcie->apb_base + PCIE_GEN_CONTROL2); + val |= PCIE_CFG_LTSSM_EN; + writel(val, pcie->apb_base + PCIE_GEN_CONTROL2); + + return 0; +} + +static const struct dw_pcie_ops qilai_pcie_ops = { + .link_up = qilai_pcie_link_up, + .start_link = qilai_pcie_start_link, +}; + +/* + * Set up the QiLai PCIe IOCP (IO Coherence Port) Read/Write Behaviors to the + * Write-Back, Read and Write Allocate mode. + * + * The IOCP HW target is SoC last-level cache (L2 Cache), which serves as the + * system cache. The IOCP HW helps maintain cache monitoring, ensuring that + * the device can snoop data from/to the cache. + */ +static void qilai_pcie_iocp_cache_setup(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u32 val; + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3); + FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_MODE, &val, IOCP_ARCACHE); + FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_MODE, &val, IOCP_AWCACHE); + FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_VALUE, &val, IOCP_ARCACHE); + FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_VALUE, &val, IOCP_AWCACHE); + dw_pcie_writel_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3, val); + + dw_pcie_dbi_ro_wr_dis(pci); +} + +static void qilai_pcie_enable_msi(struct qilai_pcie *pcie) +{ + u32 val; + + val = readl(pcie->apb_base + PCIE_INTR_CONTROL1); + val |= PCIE_MSI_CTRL_INT_EN; + writel(val, pcie->apb_base + PCIE_INTR_CONTROL1); +} + +static int qilai_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct qilai_pcie *pcie = to_qilai_pcie(pci); + + qilai_pcie_enable_msi(pcie); + + return 0; +} + +static void qilai_pcie_host_post_init(struct dw_pcie_rp *pp) +{ + qilai_pcie_iocp_cache_setup(pp); +} + +static const struct dw_pcie_host_ops qilai_pcie_host_ops = { + .init = qilai_pcie_host_init, + .post_init = qilai_pcie_host_post_init, +}; + +static int qilai_pcie_probe(struct platform_device *pdev) +{ + struct qilai_pcie *pcie; + struct dw_pcie *pci; + struct device *dev = &pdev->dev; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + platform_set_drvdata(pdev, pcie); + + pci = &pcie->pci; + pcie->pci.dev = dev; + pcie->pci.ops = &qilai_pcie_ops; + pcie->pci.pp.ops = &qilai_pcie_host_ops; + pci->use_parent_dt_ranges = true; + + dw_pcie_cap_set(&pcie->pci, REQ_RES); + + pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb"); + if (IS_ERR(pcie->apb_base)) + return PTR_ERR(pcie->apb_base); + + pm_runtime_set_active(dev); + pm_runtime_no_callbacks(dev); + devm_pm_runtime_enable(dev); + + ret = dw_pcie_host_init(&pcie->pci.pp); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize PCIe host\n"); + + return 0; +} + +static const struct of_device_id qilai_pcie_of_match[] = { + { .compatible = "andestech,qilai-pcie" }, + {}, +}; +MODULE_DEVICE_TABLE(of, qilai_pcie_of_match); + +static struct platform_driver qilai_pcie_driver = { + .probe = qilai_pcie_probe, + .driver = { + .name = "qilai-pcie", + .of_match_table = qilai_pcie_of_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +builtin_platform_driver(qilai_pcie_driver); + +MODULE_AUTHOR("Randolph Lin "); +MODULE_DESCRIPTION("Andes QiLai PCIe driver"); +MODULE_LICENSE("GPL"); From 0b74f7d72399d4c4422ed3d68ef28b3612f71e74 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Thu, 26 Feb 2026 19:06:53 -0800 Subject: [PATCH 030/165] PCI: endpoint: Propagate error from pci_epf_create() pci_epf_make() overwrites the actual error returned by pci_epf_create() with -EINVAL, which hides the real failure reason. Use PTR_ERR(epf) instead and print the error code. Signed-off-by: Alok Tiwari Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260227030701.40533-1-alok.a.tiwari@oracle.com --- drivers/pci/endpoint/pci-ep-cfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 653ffc72dd39..076d115db02b 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -624,8 +624,8 @@ static struct config_group *pci_epf_make(struct config_group *group, epf = pci_epf_create(epf_name); if (IS_ERR(epf)) { - pr_err("failed to create endpoint function device\n"); - err = -EINVAL; + err = PTR_ERR(epf); + pr_err("failed to create endpoint function device: %d\n", err); goto free_name; } From 271d0b1f058ae9815e75233d04b23e3558c3e4f4 Mon Sep 17 00:00:00 2001 From: Aksh Garg Date: Tue, 24 Feb 2026 14:08:16 +0530 Subject: [PATCH 031/165] PCI: dwc: ep: Fix MSI-X Table Size configuration in dw_pcie_ep_set_msix() In dw_pcie_ep_set_msix(), while updating the MSI-X Table Size value for individual functions, Message Control register is read from the passed function number register space using dw_pcie_ep_readw_dbi(), but always written back to the Function 0's register space using dw_pcie_writew_dbi(). This causes incorrect MSI-X configuration for the rest of the functions, other than Function 0. Fix this by using dw_pcie_ep_writew_dbi() to write to the correct function's register space, matching the read operation. Fixes: 70fa02ca1446 ("PCI: dwc: Add dw_pcie_ep_{read,write}_dbi[2] helpers") Signed-off-by: Aksh Garg [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260224083817.916782-2-a-garg7@ti.com --- drivers/pci/controller/dwc/pcie-designware-ep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 295076cf70de..709ab60c9e39 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -754,7 +754,7 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, val = dw_pcie_ep_readw_dbi(ep, func_no, reg); val &= ~PCI_MSIX_FLAGS_QSIZE; val |= nr_irqs - 1; /* encoded as N-1 */ - dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_ep_writew_dbi(ep, func_no, reg, val); reg = ep_func->msix_cap + PCI_MSIX_TABLE; val = offset | bir; From 94cbea0f636b55602a9a10583670976680ecea67 Mon Sep 17 00:00:00 2001 From: Aksh Garg Date: Tue, 24 Feb 2026 14:08:17 +0530 Subject: [PATCH 032/165] PCI: dwc: ep: Mirror the max link width and speed fields to all functions PCIe r7.0, section 7.5.3.6 states that for multi-function devices, the Max Link Width and Max Link Speed fields in the Link Capabilities Register must report the same values for all functions. Currently, dw_pcie_setup() programs these fields only for Function 0 via dw_pcie_link_set_max_speed() and dw_pcie_link_set_max_link_width(). For multi-function endpoint configurations, Function 1 and beyond retain their default values, violating the PCIe specification. Fix this by reading the Max Link Width and Max Link Speed fields from Link Capabilities Register of Function 0 after dw_pcie_setup() completes, then mirroring these values to all other functions. Fixes: 24ede430fa49 ("PCI: designware-ep: Add multiple PFs support for DWC") Fixes: 89db0793c9f2 ("PCI: dwc: Add missing PCI_EXP_LNKCAP_MLW handling") Signed-off-by: Aksh Garg [mani: renamed ref_lnkcap to func0_lnkcap] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260224083817.916782-3-a-garg7@ti.com --- .../pci/controller/dwc/pcie-designware-ep.c | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 709ab60c9e39..21b6f4eb24cd 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -1103,7 +1103,8 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) { struct dw_pcie_ep *ep = &pci->ep; u8 funcs = ep->epc->max_functions; - u8 func_no; + u32 func0_lnkcap, lnkcap; + u8 func_no, offset; dw_pcie_dbi_ro_wr_en(pci); @@ -1111,6 +1112,32 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) dw_pcie_ep_init_rebar_registers(ep, func_no); dw_pcie_setup(pci); + + /* + * PCIe r7.0, section 7.5.3.6 states that for multi-function + * endpoints, max link width and speed fields must report same + * values for all functions. However, dw_pcie_setup() programs + * these fields only for function 0. Hence, mirror these fields + * to all other functions as well. + */ + if (funcs > 1) { + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + 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); + + for (func_no = 1; func_no < funcs; func_no++) { + offset = dw_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_EXP); + lnkcap = dw_pcie_ep_readl_dbi(ep, func_no, + offset + PCI_EXP_LNKCAP); + FIELD_MODIFY(PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS, + &lnkcap, func0_lnkcap); + dw_pcie_ep_writel_dbi(ep, func_no, + offset + PCI_EXP_LNKCAP, lnkcap); + } + } + dw_pcie_dbi_ro_wr_dis(pci); } From edb5ca3262e2255cf938a5948709d3472d4871ad Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 26 Feb 2026 19:09:51 +0530 Subject: [PATCH 033/165] PCI: dwc: Perform cleanup in the error path of dw_pcie_resume_noirq() If the dw_pcie_resume_noirq() API fails, it just returns the errno without doing cleanup in the error path, leading to resource leak. So perform cleanup in the error path. Fixes: 4774faf854f5 ("PCI: dwc: Implement generic suspend/resume functionality") Reported-by: Senchuan Zhang Closes: https://lore.kernel.org/linux-pci/78296255.3869.19c8eb694d6.Coremail.zhangsenchuan@eswincomputing.com Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260226133951.296743-1-mani@kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index ba183fc3e77c..a74339982c24 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1304,15 +1304,24 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) ret = dw_pcie_start_link(pci); if (ret) - return ret; + goto err_deinit; ret = dw_pcie_wait_for_link(pci); - if (ret) - return ret; + if (ret == -ETIMEDOUT) + goto err_stop_link; if (pci->pp.ops->post_init) pci->pp.ops->post_init(&pci->pp); + return 0; + +err_stop_link: + dw_pcie_stop_link(pci); + +err_deinit: + if (pci->pp.ops->deinit) + pci->pp.ops->deinit(&pci->pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq); From 84226677e04154000d13bb2792c4837b691ccb34 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:34 -0800 Subject: [PATCH 034/165] PCI: Rename __pci_bus_reset() and __pci_slot_reset() Make the code a little easier to navigate with more descriptive function names. The two renamed functions here "try" to do to a reset, so make that clear in the name to distinguish them from other similarly named functions: __pci_reset_bus() -> pci_try_reset_bus() __pci_reset_slot() -> pci_try_reset_slot() Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-2-kbusch@meta.com --- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.c | 10 +++++----- drivers/pci/pci.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 16eaaf749ba9..aaf92195da32 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -563,7 +563,7 @@ static ssize_t reset_subordinate_store(struct device *dev, return -EINVAL; if (val) { - int ret = __pci_reset_bus(bus); + int ret = pci_try_reset_bus(bus); if (ret) return ret; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..4fd61d6cacff 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5541,7 +5541,7 @@ int pci_probe_reset_slot(struct pci_slot *slot) EXPORT_SYMBOL_GPL(pci_probe_reset_slot); /** - * __pci_reset_slot - Try to reset a PCI slot + * pci_try_reset_slot - Try to reset a PCI slot * @slot: PCI slot to reset * * A PCI bus may host multiple slots, each slot may support a reset mechanism @@ -5555,7 +5555,7 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_slot); * * Same as above except return -EAGAIN if the slot cannot be locked */ -static int __pci_reset_slot(struct pci_slot *slot) +static int pci_try_reset_slot(struct pci_slot *slot) { int rc; @@ -5644,12 +5644,12 @@ int pci_probe_reset_bus(struct pci_bus *bus) EXPORT_SYMBOL_GPL(pci_probe_reset_bus); /** - * __pci_reset_bus - Try to reset a PCI bus + * pci_try_reset_bus - Try to reset a PCI bus * @bus: top level PCI bus to reset * * Same as above except return -EAGAIN if the bus cannot be locked */ -int __pci_reset_bus(struct pci_bus *bus) +int pci_try_reset_bus(struct pci_bus *bus) { int rc; @@ -5678,7 +5678,7 @@ int __pci_reset_bus(struct pci_bus *bus) int pci_reset_bus(struct pci_dev *pdev) { return (!pci_probe_reset_slot(pdev->slot)) ? - __pci_reset_slot(pdev->slot) : __pci_reset_bus(pdev->bus); + pci_try_reset_slot(pdev->slot) : pci_try_reset_bus(pdev->bus); } EXPORT_SYMBOL_GPL(pci_reset_bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..e319417da5ca 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -231,7 +231,7 @@ bool pci_reset_supported(struct pci_dev *dev); void pci_init_reset_methods(struct pci_dev *dev); int pci_bridge_secondary_bus_reset(struct pci_dev *dev); int pci_bus_error_reset(struct pci_dev *dev); -int __pci_reset_bus(struct pci_bus *bus); +int pci_try_reset_bus(struct pci_bus *bus); struct pci_cap_saved_data { u16 cap_nr; From 102c8b26b54e363f85c4c86099ca049a0a76bb58 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:35 -0800 Subject: [PATCH 035/165] PCI: Allow all bus devices to use the same slot A PCIe hotplug slot applies to the entire secondary bus. Thus, pciehp only allocates a single hotplug_slot for the bridge to that bus. The existing PCI slot, though, would only match to functions on device 0, meaning any devices beyond that, e.g., ARI functions, are not matched to any slot even though they share it. A slot reset will break all the missing devices because the handling skips them. For example, ARI devices with more than 8 functions fail because their state is not properly handled, nor is the attached driver notified of the reset. In the best case, the device will appear unresponsive to the driver, resulting in unexpected errors. A worse possibility may panic the kernel if in-flight transactions trigger hardware reported errors like this real observation: vfio-pci 0000:01:00.0: resetting vfio-pci 0000:01:00.0: reset done {1}[Hardware Error]: Error 1, type: fatal {1}[Hardware Error]: section_type: PCIe error {1}[Hardware Error]: port_type: 0, PCIe end point {1}[Hardware Error]: version: 0.2 {1}[Hardware Error]: command: 0x0140, status: 0x0010 {1}[Hardware Error]: device_id: 0000:01:01.0 {1}[Hardware Error]: slot: 0 {1}[Hardware Error]: secondary_bus: 0x00 {1}[Hardware Error]: vendor_id: 0x1d9b, device_id: 0x0207 {1}[Hardware Error]: class_code: 020000 {1}[Hardware Error]: bridge: secondary_status: 0x0000, control: 0x0000 {1}[Hardware Error]: aer_cor_status: 0x00008000, aer_cor_mask: 0x00002000 {1}[Hardware Error]: aer_uncor_status: 0x00010000, aer_uncor_mask: 0x00100000 {1}[Hardware Error]: aer_uncor_severity: 0x006f6030 {1}[Hardware Error]: TLP Header: 0a412800 00192080 60000004 00000004 GHES: Fatal hardware error but panic disabled Kernel panic - not syncing: GHES: Fatal hardware error Allow a slot to be created to claim all devices on a bus, not just a matching device. This is done by introducing a sentinel value, named PCI_SLOT_ALL_DEVICES, which then has the PCI slot match to any device on the bus. This fixes slot resets for pciehp. Since 0xff already has special meaning, the chosen value for this new feature is 0xfe. This will not clash with any actual slot number since they are limited to 5 bits. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-3-kbusch@meta.com --- drivers/pci/hotplug/pciehp_core.c | 3 ++- drivers/pci/slot.c | 31 +++++++++++++++++++++++++++---- include/linux/pci.h | 10 +++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 1e9158d7bac7..2cafd3b26f34 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -79,7 +79,8 @@ static int init_slot(struct controller *ctrl) snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); retval = pci_hp_initialize(&ctrl->hotplug_slot, - ctrl->pcie->port->subordinate, 0, name); + ctrl->pcie->port->subordinate, + PCI_SLOT_ALL_DEVICES, name); if (retval) { ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); kfree(ops); diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 787311614e5b..e0b7fb43423c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -42,6 +42,15 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf) pci_domain_nr(slot->bus), slot->bus->number); + /* + * Preserve legacy ABI expectations that hotplug drivers that manage + * multiple devices per slot emit 0 for the device number. + */ + if (slot->number == PCI_SLOT_ALL_DEVICES) + return sysfs_emit(buf, "%04x:%02x:00\n", + pci_domain_nr(slot->bus), + slot->bus->number); + return sysfs_emit(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus), slot->bus->number, @@ -73,7 +82,8 @@ static void pci_slot_release(struct kobject *kobj) down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = NULL; up_read(&pci_bus_sem); @@ -166,7 +176,8 @@ void pci_dev_assign_slot(struct pci_dev *dev) mutex_lock(&pci_slot_mutex); list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = slot; mutex_unlock(&pci_slot_mutex); } @@ -188,7 +199,8 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) /** * pci_create_slot - create or increment refcount for physical PCI slot * @parent: struct pci_bus of parent bridge - * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder + * @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or + * PCI_SLOT_ALL_DEVICES * @name: user visible string presented in /sys/bus/pci/slots/ * @hotplug: set if caller is hotplug driver, NULL otherwise * @@ -222,6 +234,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the * %struct pci_bus and bb is the bus number. In other words, the devfn of * the 'placeholder' slot will not be displayed. + * + * Bus-wide slots: + * For PCIe hotplug, the physical slot encompasses the entire secondary + * bus, not just a single device number. If the device supports ARI and ARI + * Forwarding is enabled in the upstream bridge, a multi-function device + * may include functions that appear to have several different device + * numbers, i.e., PCI_SLOT() values. Pass @slot_nr == PCI_SLOT_ALL_DEVICES + * to create a slot that matches all devices on the bus. Unlike placeholder + * slots, bus-wide slots go through normal slot lookup and reuse existing + * slots if present. */ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, @@ -285,7 +307,8 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot_nr) + if (slot_nr == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; up_read(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1c270f1d5123..5ae2dfdb2d6f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -72,12 +72,20 @@ /* return bus from PCI devid = ((u16)bus_number) << 8) | devfn */ #define PCI_BUS_NUM(x) (((x) >> 8) & 0xff) +/* + * PCI_SLOT_ALL_DEVICES indicates a slot that covers all devices on the bus. + * Used for PCIe hotplug where the physical slot is the entire secondary bus, + * and, if ARI Forwarding is enabled, functions may appear to be on multiple + * devices. + */ +#define PCI_SLOT_ALL_DEVICES 0xfe + /* pci_slot represents a physical slot */ struct pci_slot { struct pci_bus *bus; /* Bus this slot is on */ struct list_head list; /* Node in list of slots */ struct hotplug_slot *hotplug; /* Hotplug info (move here) */ - unsigned char number; /* PCI_SLOT(pci_dev->devfn) */ + unsigned char number; /* Device nr, or PCI_SLOT_ALL_DEVICES */ struct kobject kobj; }; From 8238cb69c01fe4dbb4e3be277fff3ed680ac0108 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 Feb 2026 08:08:36 -0800 Subject: [PATCH 036/165] PCI: Make reset_subordinate hotplug safe Use the slot reset method when resetting the bridge if the bus contains hot plug slots. This fixes spurious hot plug events that are triggered by the secondary bus reset that bypasses the slot's detection disabling. Resetting a bridge's subordinate bus can be done like this: # echo 1 > /sys/bus/pci/devices/0000:50:01.0/reset_subordinate Prior to this patch, an example kernel message may show something like: pcieport 0000:50:01.0: pciehp: Slot(40): Link Down With this change, the pciehp driver ignores the link event during the reset, so may show this message instead: pcieport 0000:50:01.0: pciehp: Slot(40): Link Down/Up ignored Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260217160836.2709885-4-kbusch@meta.com --- drivers/pci/pci-sysfs.c | 3 +- drivers/pci/pci.c | 125 +++++++++++++++++++++++++--------------- drivers/pci/pci.h | 2 +- 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index aaf92195da32..a2f8a5d6190f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -553,7 +553,6 @@ static ssize_t reset_subordinate_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - struct pci_bus *bus = pdev->subordinate; unsigned long val; if (!capable(CAP_SYS_ADMIN)) @@ -563,7 +562,7 @@ static ssize_t reset_subordinate_store(struct device *dev, return -EINVAL; if (val) { - int ret = pci_try_reset_bus(bus); + int ret = pci_try_reset_bridge(pdev); if (ret) return ret; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4fd61d6cacff..5984ad9ef6c4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5596,60 +5596,13 @@ static int pci_bus_reset(struct pci_bus *bus, bool probe) return ret; } -/** - * pci_bus_error_reset - reset the bridge's subordinate bus - * @bridge: The parent device that connects to the bus to reset - * - * This function will first try to reset the slots on this bus if the method is - * available. If slot reset fails or is not available, this will fall back to a - * secondary bus reset. - */ -int pci_bus_error_reset(struct pci_dev *bridge) -{ - struct pci_bus *bus = bridge->subordinate; - struct pci_slot *slot; - - if (!bus) - return -ENOTTY; - - mutex_lock(&pci_slot_mutex); - if (list_empty(&bus->slots)) - goto bus_reset; - - list_for_each_entry(slot, &bus->slots, list) - if (pci_probe_reset_slot(slot)) - goto bus_reset; - - list_for_each_entry(slot, &bus->slots, list) - if (pci_slot_reset(slot, PCI_RESET_DO_RESET)) - goto bus_reset; - - mutex_unlock(&pci_slot_mutex); - return 0; -bus_reset: - mutex_unlock(&pci_slot_mutex); - return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET); -} - -/** - * pci_probe_reset_bus - probe whether a PCI bus can be reset - * @bus: PCI bus to probe - * - * Return 0 if bus can be reset, negative if a bus reset is not supported. - */ -int pci_probe_reset_bus(struct pci_bus *bus) -{ - return pci_bus_reset(bus, PCI_RESET_PROBE); -} -EXPORT_SYMBOL_GPL(pci_probe_reset_bus); - /** * pci_try_reset_bus - Try to reset a PCI bus * @bus: top level PCI bus to reset * * Same as above except return -EAGAIN if the bus cannot be locked */ -int pci_try_reset_bus(struct pci_bus *bus) +static int pci_try_reset_bus(struct pci_bus *bus) { int rc; @@ -5669,6 +5622,82 @@ int pci_try_reset_bus(struct pci_bus *bus) return rc; } +#define PCI_RESET_RESTORE true +#define PCI_RESET_NO_RESTORE false +/** + * pci_reset_bridge - reset a bridge's subordinate bus + * @bridge: bridge that connects to the bus to reset + * @restore: when true use a reset method that invokes pci_dev_restore() post + * reset for affected devices + * + * This function will first try to reset the slots on this bus if the method is + * available. If slot reset fails or is not available, this will fall back to a + * secondary bus reset. + */ +static int pci_reset_bridge(struct pci_dev *bridge, bool restore) +{ + struct pci_bus *bus = bridge->subordinate; + struct pci_slot *slot; + + if (!bus) + return -ENOTTY; + + mutex_lock(&pci_slot_mutex); + if (list_empty(&bus->slots)) + goto bus_reset; + + list_for_each_entry(slot, &bus->slots, list) + if (pci_probe_reset_slot(slot)) + goto bus_reset; + + list_for_each_entry(slot, &bus->slots, list) { + int ret; + + if (restore) + ret = pci_try_reset_slot(slot); + else + ret = pci_slot_reset(slot, PCI_RESET_DO_RESET); + + if (ret) + goto bus_reset; + } + + mutex_unlock(&pci_slot_mutex); + return 0; +bus_reset: + mutex_unlock(&pci_slot_mutex); + + if (restore) + return pci_try_reset_bus(bus); + return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET); +} + +/** + * pci_bus_error_reset - reset the bridge's subordinate bus + * @bridge: The parent device that connects to the bus to reset + */ +int pci_bus_error_reset(struct pci_dev *bridge) +{ + return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE); +} + +int pci_try_reset_bridge(struct pci_dev *bridge) +{ + return pci_reset_bridge(bridge, PCI_RESET_RESTORE); +} + +/** + * pci_probe_reset_bus - probe whether a PCI bus can be reset + * @bus: PCI bus to probe + * + * Return 0 if bus can be reset, negative if a bus reset is not supported. + */ +int pci_probe_reset_bus(struct pci_bus *bus) +{ + return pci_bus_reset(bus, PCI_RESET_PROBE); +} +EXPORT_SYMBOL_GPL(pci_probe_reset_bus); + /** * pci_reset_bus - Try to reset a PCI bus * @pdev: top level PCI device to reset via slot/bus diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e319417da5ca..a1d2ecb56207 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -231,7 +231,7 @@ bool pci_reset_supported(struct pci_dev *dev); void pci_init_reset_methods(struct pci_dev *dev); int pci_bridge_secondary_bus_reset(struct pci_dev *dev); int pci_bus_error_reset(struct pci_dev *dev); -int pci_try_reset_bus(struct pci_bus *bus); +int pci_try_reset_bridge(struct pci_dev *bridge); struct pci_cap_saved_data { u16 cap_nr; From 7b193af58b7f65715dc19e235e03e447a454b377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 4 Mar 2026 14:21:38 +0200 Subject: [PATCH 037/165] PCI: Consolidate pci_bus/slot_lock/unlock/trylock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_bus/slot_lock/unlock/trylock() largely duplicate the bus iteration loop with variation only due to slot filter handling. The only differences in the loops is where the struct bus is found (directly in the argument vs in slot->bus) and whether slot filter is applied. Those differences are simple to handle using function parameters. Consolidate the bus iteration loop to one place by creating __pci_bus_{lock,unlock,trylock}() and call them from the non-underscore locking functions. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260304122139.1479-1-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.c | 119 ++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5984ad9ef6c4..0090b4034ec6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5292,13 +5292,21 @@ static bool pci_bus_resettable(struct pci_bus *bus) return true; } -/* Lock devices from the top of the tree down */ -static void pci_bus_lock(struct pci_bus *bus) -{ - struct pci_dev *dev; +static void pci_bus_lock(struct pci_bus *bus); +static void pci_bus_unlock(struct pci_bus *bus); +static int pci_bus_trylock(struct pci_bus *bus); + +/* Lock devices from the top of the tree down */ +static void __pci_bus_lock(struct pci_bus *bus, struct pci_slot *slot) +{ + struct pci_dev *dev, *bridge = bus->self; + + if (bridge) + pci_dev_lock(bridge); - pci_dev_lock(bus->self); list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_lock(dev->subordinate); else @@ -5307,28 +5315,34 @@ static void pci_bus_lock(struct pci_bus *bus) } /* Unlock devices from the bottom of the tree up */ -static void pci_bus_unlock(struct pci_bus *bus) +static void __pci_bus_unlock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); } /* Return 1 on successful lock, 0 on contention */ -static int pci_bus_trylock(struct pci_bus *bus) +static int __pci_bus_trylock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; - if (!pci_dev_trylock(bus->self)) + if (bridge && !pci_dev_trylock(bridge)) return 0; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) { if (!pci_bus_trylock(dev->subordinate)) goto unlock; @@ -5339,15 +5353,37 @@ static int pci_bus_trylock(struct pci_bus *bus) unlock: list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); return 0; } +/* Lock devices from the top of the tree down */ +static void pci_bus_lock(struct pci_bus *bus) +{ + __pci_bus_lock(bus, NULL); +} + +/* Unlock devices from the bottom of the tree up */ +static void pci_bus_unlock(struct pci_bus *bus) +{ + __pci_bus_unlock(bus, NULL); +} + +/* Return 1 on successful lock, 0 on contention */ +static int pci_bus_trylock(struct pci_bus *bus) +{ + return __pci_bus_trylock(bus, NULL); +} + /* Do any devices on or below this slot prevent a bus reset? */ static bool pci_slot_resettable(struct pci_slot *slot) { @@ -5370,72 +5406,19 @@ static bool pci_slot_resettable(struct pci_slot *slot) /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge) - pci_dev_lock(bridge); - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_lock(dev->subordinate); - else - pci_dev_lock(dev); - } + __pci_bus_lock(slot->bus, slot); } /* Unlock devices from the bottom of the tree up */ static void pci_slot_unlock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); + __pci_bus_unlock(slot->bus, slot); } /* Return 1 on successful lock, 0 on contention */ static int pci_slot_trylock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge && !pci_dev_trylock(bridge)) - return 0; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) { - if (!pci_bus_trylock(dev->subordinate)) - goto unlock; - } else if (!pci_dev_trylock(dev)) - goto unlock; - } - return 1; - -unlock: - list_for_each_entry_continue_reverse(dev, - &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); - return 0; + return __pci_bus_trylock(slot->bus, slot); } /* From 0111d600f0f456d09eb0ff6ac217fa5f30ae79ab Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Sun, 8 Mar 2026 23:59:30 -0500 Subject: [PATCH 038/165] PCI: of: Reduce severity of missing of_root error message Arm64 kernels are frequently built dual ACPI/DT, and then boot in ACPI mode. In this case, there won't be an of_root, except for rare DT described PCIe boards. As a result, users in the common case see this high priority worrying message, despite the machine working as expected. Reduce this message to pr_debug() to avoid unnecessary noise. Signed-off-by: Jeremy Linton Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260309045930.21531-1-jeremy.linton@arm.com --- drivers/pci/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 9f8eb5df279e..0eba56e0ebcd 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -775,7 +775,7 @@ void of_pci_make_host_bridge_node(struct pci_host_bridge *bridge) /* Check if there is a DT root node to attach the created node */ if (!of_root) { - pr_err("of_root node is NULL, cannot create PCI host bridge node\n"); + pr_debug("of_root node is NULL, cannot create PCI host bridge node\n"); return; } From 0026bb20d12531ca2925b60bf081cc8be910ccf9 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Thu, 12 Mar 2026 19:54:41 +0800 Subject: [PATCH 039/165] PCI: Use pr_warn_once() for ACS parameter parse failure When the ACS command line parameter cannot be parsed, the kernel skips applying the requested ACS override. This indicates an invalid boot parameter and should not be logged at informational level. Use pr_warn_once() so the message is surfaced as a warning while still avoiding repeated log spam during device enumeration. Signed-off-by: Richard Cheng Signed-off-by: Bjorn Helgaas Acked-by: Tushar Dave Link: https://patch.msgid.link/20260312115441.8168-1-icheng@nvidia.com --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..af735748a4a4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -949,7 +949,7 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, ret = pci_dev_str_match(dev, p, &p); if (ret < 0) { - pr_info_once("PCI: Can't parse ACS command line parameter\n"); + pr_warn_once("PCI: Can't parse ACS command line parameter\n"); break; } else if (ret == 1) { /* Found a match */ From d284389d4576e7c8040dc4cbb66876e539c6d064 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:08 +0100 Subject: [PATCH 040/165] PCI: rzg3s-host: Fix reset handling in probe error path Fix incorrect reset_control_bulk_deassert() call in the probe error path. When unwinding from a failed pci_host_probe(), the configuration resets should be asserted to restore the hardware to its initial state, not deasserted again. Fixes: 7ef502fb35b2 ("PCI: Add Renesas RZ/G3S host controller driver") Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-2-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 2809112e6317..7a80455aad36 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1589,8 +1589,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) host_probe_teardown: rzg3s_pcie_teardown_irqdomain(host); - reset_control_bulk_deassert(host->data->num_cfg_resets, - host->cfg_resets); + reset_control_bulk_assert(host->data->num_cfg_resets, host->cfg_resets); rpm_put: pm_runtime_put_sync(dev); rpm_disable: From 34735f63748daa2ea27544259c3042b4948376bf Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:09 +0100 Subject: [PATCH 041/165] PCI: rzg3s-host: Reorder reset assertion during suspend Reorder the reset assertion sequence during suspend from power_resets -> cfg_resets to cfg_resets -> power_resets. This change ensures the suspend sequence follows the reverse order of the probe/init sequence, where power_resets are deasserted first followed by cfg_resets. Additionally, this ordering is required for RZ/G3E support where cfg resets are controlled through PCIe AXI registers (offset 0x310h). According to the RZ/G3E hardware manual (Rev.1.15, section 6.6.6.1.1 "Changing the Initial Values of the Registers"), AXI register access requires ARESETn to be de-asserted and the clock to be supplied. Since ARESETn is part of power_resets, cfg_resets must be asserted before power_resets, otherwise the AXI registers become inaccessible. Fixes: 7ef502fb35b2 ("PCI: Add Renesas RZ/G3S host controller driver") Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-3-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 7a80455aad36..986f0a319b3c 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1624,31 +1624,31 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev) clk_disable_unprepare(port->refclk); - ret = reset_control_bulk_assert(data->num_power_resets, - host->power_resets); - if (ret) - goto refclk_restore; - ret = reset_control_bulk_assert(data->num_cfg_resets, host->cfg_resets); if (ret) - goto power_resets_restore; + goto refclk_restore; + + ret = reset_control_bulk_assert(data->num_power_resets, + host->power_resets); + if (ret) + goto cfg_resets_restore; ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, RZG3S_SYS_PCIE_RST_RSM_B_MASK, FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0)); if (ret) - goto cfg_resets_restore; + goto power_resets_restore; return 0; /* Restore the previous state if any error happens */ -cfg_resets_restore: - reset_control_bulk_deassert(data->num_cfg_resets, - host->cfg_resets); power_resets_restore: reset_control_bulk_deassert(data->num_power_resets, host->power_resets); +cfg_resets_restore: + reset_control_bulk_deassert(data->num_cfg_resets, + host->cfg_resets); refclk_restore: clk_prepare_enable(port->refclk); pm_runtime_resume_and_get(dev); From d17a200a747b01e05bb0fc14d62fbb5ad6653869 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:10 +0100 Subject: [PATCH 042/165] PCI: rzg3s-host: Rework inbound window algorithm for supporting RZ/G3E SoC The existing inbound window configuration algorithm has two issues that prevent proper operation on RZ/G3E: 1. Over-mapping: Using roundup_pow_of_two() on the remaining region size can result in windows that extend beyond the intended memory region. 2. Alignment violation: Addresses are only aligned to 4K regardless of the actual window size. According to the RZ/G3S HW manual (Rev.1.10, section 34.6.6.7) and RZ/G3E HW manual (Rev.1.15, section 6.6.7.6), bit carry must not occur when adding AXI Window Base and AXI Window Mask registers. This effectively requires the base address to be aligned to the window size. RZ/G3E strictly enforces these constraints and requires precise window boundaries with properly aligned addresses. Rework the algorithm to properly handle arbitrary region sizes and alignment constraints by splitting non-power-of-2 regions into multiple windows. The new approach iteratively selects the largest power-of-2 size that: - Fits within the remaining region (__fls of remaining size) - Does not exceed the natural alignment of the CPU address (__ffs) - Does not exceed the natural alignment of the PCI address (__ffs) This ensures windows never over-map beyond the intended region and satisfies the hardware requirement that base address + mask must not cause bit carry, while maintaining the 4K * 2^N byte window size constraint. The reworked algorithm is required for RZ/G3E support and remains fully compatible with RZ/G3S. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-4-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 57 +++++++++++++----------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 986f0a319b3c..872683211bab 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1271,50 +1271,55 @@ static int rzg3s_pcie_set_inbound_windows(struct rzg3s_pcie_host *host, u64 pci_addr = entry->res->start - entry->offset; u64 cpu_addr = entry->res->start; u64 cpu_end = entry->res->end; - u64 size_id = 0; int id = *index; u64 size; - while (cpu_addr < cpu_end) { + /* + * According to the RZ/G3S HW manual (Rev.1.10, section 34.6.6.7) and + * RZ/G3E HW manual (Rev.1.15, section 6.6.7.6): + * - Each window must be a single memory size of power of two + * - Mask registers must be set to (2^N - 1) + * - Bit carry must not occur when adding base and mask registers, + * meaning the base address must be aligned to the window size + * + * Split non-power-of-2 regions into multiple windows to satisfy + * these constraints without over-mapping. + */ + while (cpu_addr <= cpu_end) { + u64 remaining_size = cpu_end - cpu_addr + 1; + u64 align_limit; + if (id >= RZG3S_MAX_WINDOWS) return dev_err_probe(host->dev, -ENOSPC, "Failed to map inbound window for resource (%s)\n", entry->res->name); - size = resource_size(entry->res) - size_id; + /* Start with largest power-of-two that fits in remaining size */ + size = 1ULL << __fls(remaining_size); /* - * According to the RZ/G3S HW manual (Rev.1.10, - * section 34.3.1.71 AXI Window Mask (Lower) Registers) the min - * size is 4K. + * The "no bit carry" rule requires base addresses to be + * aligned to the window size. Find the maximum window size + * that both addresses can support based on their natural + * alignment (lowest set bit). + */ + align_limit = min(cpu_addr ? (1ULL << __ffs(cpu_addr)) : ~0ULL, + pci_addr ? (1ULL << __ffs(pci_addr)) : ~0ULL); + + size = min(size, align_limit); + + /* + * Minimum window size is 4KB. + * See RZ/G3S HW manual (Rev.1.10, section 34.3.1.71) and + * RZ/G3E HW manual (Rev.1.15, section 6.6.4.1.3.(74)). */ size = max(size, SZ_4K); - /* - * According the RZ/G3S HW manual (Rev.1.10, sections: - * - 34.3.1.69 AXI Window Base (Lower) Registers - * - 34.3.1.71 AXI Window Mask (Lower) Registers - * - 34.3.1.73 AXI Destination (Lower) Registers) - * the CPU addr, PCIe addr, size should be 4K aligned and be a - * power of 2. - */ - size = ALIGN(size, SZ_4K); - size = roundup_pow_of_two(size); - - cpu_addr = ALIGN(cpu_addr, SZ_4K); - pci_addr = ALIGN(pci_addr, SZ_4K); - - /* - * According to the RZ/G3S HW manual (Rev.1.10, section - * 34.3.1.71 AXI Window Mask (Lower) Registers) HW expects first - * 12 LSB bits to be 0xfff. Subtract 1 from size for this. - */ rzg3s_pcie_set_inbound_window(host, cpu_addr, pci_addr, size - 1, id); pci_addr += size; cpu_addr += size; - size_id = size; id++; } *index = id; From bb1b0f47f6822864c1689f46348efa42c5d4074c Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:12 +0100 Subject: [PATCH 043/165] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Fix naming properties Fix a typo in interrupt-names: "ser_cor" should be "serr_cor" (System Error Correctable). Also convert interrupt-names, clock-names, and reset-names properties from "description" to "const" to enable proper validation with dtbs_check. Fixes: e7534e790557 ("dt-bindings: PCI: Add Renesas RZ/G3S PCIe controller binding") Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Acked-by: Conor Dooley Link: https://patch.msgid.link/20260306143423.19562-6-john.madieu.xa@bp.renesas.com --- .../bindings/pci/renesas,r9a08g045-pcie.yaml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml index d668782546a2..d1eb92995e2c 100644 --- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml @@ -41,22 +41,22 @@ properties: interrupt-names: items: - - description: serr - - description: ser_cor - - description: serr_nonfatal - - description: serr_fatal - - description: axi_err - - description: inta - - description: intb - - description: intc - - description: intd - - description: msi - - description: link_bandwidth - - description: pm_pme - - description: dma - - description: pcie_evt - - description: msg - - description: all + - const: serr + - const: serr_cor + - const: serr_nonfatal + - const: serr_fatal + - const: axi_err + - const: inta + - const: intb + - const: intc + - const: intd + - const: msi + - const: link_bandwidth + - const: pm_pme + - const: dma + - const: pcie_evt + - const: msg + - const: all interrupt-controller: true @@ -67,8 +67,8 @@ properties: clock-names: items: - - description: aclk - - description: pm + - const: aclk + - const: pm resets: items: @@ -82,13 +82,13 @@ properties: reset-names: items: - - description: aresetn - - description: rst_b - - description: rst_gp_b - - description: rst_ps_b - - description: rst_rsm_b - - description: rst_cfg_b - - description: rst_load_b + - const: aresetn + - const: rst_b + - const: rst_gp_b + - const: rst_ps_b + - const: rst_rsm_b + - const: rst_cfg_b + - const: rst_load_b power-domains: maxItems: 1 From fabce18494e5a4f388c70a40fa8351c911790d8d Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:13 +0100 Subject: [PATCH 044/165] dt-bindings: PCI: renesas,r9a08g045s33-pcie: Document RZ/G3E SoC Extend the existing device tree bindings for Renesas RZ/G3S PCIe controller to include support for the RZ/G3E (renesas,r9a09g047e57-pcie) PCIe controller. The RZ/G3E PCIe controller is similar to RZ/G3S but has some key differences: - Uses a different device ID - Supports PCIe Gen3 (8.0 GT/s) link speeds - Uses a different clock naming (clkpmu vs clkl1pm) - Has a different set of interrupts, interrupt ordering, and reset signals Add device tree bindings for renesas,r9a09g047e57-pcie compatible IPs. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Rob Herring (Arm) Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-7-john.madieu.xa@bp.renesas.com --- .../bindings/pci/renesas,r9a08g045-pcie.yaml | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml index d1eb92995e2c..a67108c48feb 100644 --- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml @@ -10,17 +10,21 @@ maintainers: - Claudiu Beznea description: - Renesas RZ/G3S PCIe host controller complies with PCIe Base Specification - 4.0 and supports up to 5 GT/s (Gen2). + Renesas RZ/G3{E,S} PCIe host controllers comply with PCIe + Base Specification 4.0 and support up to 5 GT/s (Gen2) for RZ/G3S and + up to 8 GT/s (Gen3) for RZ/G3E. properties: compatible: - const: renesas,r9a08g045-pcie # RZ/G3S + enum: + - renesas,r9a08g045-pcie # RZ/G3S + - renesas,r9a09g047-pcie # RZ/G3E reg: maxItems: 1 interrupts: + minItems: 16 items: - description: System error interrupt - description: System error on correctable error interrupt @@ -38,8 +42,16 @@ properties: - description: PCIe event interrupt - description: Message interrupt - description: All interrupts + - description: Link equalization request interrupt + - description: Turn off event interrupt + - description: PMU power off interrupt + - description: D3 event function 0 interrupt + - description: D3 event function 1 interrupt + - description: Configuration PMCSR write clear function 0 interrupt + - description: Configuration PMCSR write clear function 1 interrupt interrupt-names: + minItems: 16 items: - const: serr - const: serr_cor @@ -57,20 +69,28 @@ properties: - const: pcie_evt - const: msg - const: all + - const: link_equalization_request + - const: turn_off_event + - const: pmu_poweroff + - const: d3_event_f0 + - const: d3_event_f1 + - const: cfg_pmcsr_writeclear_f0 + - const: cfg_pmcsr_writeclear_f1 interrupt-controller: true clocks: items: - description: System clock - - description: PM control clock + - description: PM control clock or clock for L1 substate handling clock-names: items: - const: aclk - - const: pm + - enum: [pm, pmu] resets: + minItems: 1 items: - description: AXI2PCIe Bridge reset - description: Data link layer/transaction layer reset @@ -81,6 +101,7 @@ properties: - description: Configuration register reset reset-names: + minItems: 1 items: - const: aresetn - const: rst_b @@ -128,7 +149,9 @@ patternProperties: const: 0x1912 device-id: - const: 0x0033 + enum: + - 0x0033 + - 0x0039 clocks: items: @@ -167,6 +190,44 @@ required: allOf: - $ref: /schemas/pci/pci-host-bridge.yaml# + - if: + properties: + compatible: + contains: + const: renesas,r9a08g045-pcie + then: + properties: + interrupts: + maxItems: 16 + interrupt-names: + maxItems: 16 + clock-names: + items: + - const: aclk + - const: pm + resets: + minItems: 7 + reset-names: + minItems: 7 + - if: + properties: + compatible: + contains: + const: renesas,r9a09g047-pcie + then: + properties: + interrupts: + minItems: 23 + interrupt-names: + minItems: 23 + clock-names: + items: + - const: aclk + - const: pmu + resets: + maxItems: 1 + reset-names: + maxItems: 1 unevaluatedProperties: false From 346dd3422ed9ff56f033726a50fad2da5677eb12 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:14 +0100 Subject: [PATCH 045/165] PCI: rzg3s-host: Make SYSC register offsets SoC-specific In preparation for adding RZ/G3E support, move the RST_RSM_B register offset and mask into a SoC-specific data structure. Compared with RZ/G3S, the RZ/G3E SYSC controls different functionalities for the PCIe controller. Make SYSC operations conditional on the presence of register offset information, allowing the driver to handle SoCs that don't use the RST_RSM_B signal. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-8-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 111 +++++++++++++++++------ 1 file changed, 85 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 872683211bab..f5a0b7c6e773 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -159,10 +159,6 @@ #define RZG3S_PCI_CFG_PCIEC 0x60 -/* System controller registers */ -#define RZG3S_SYS_PCIE_RST_RSM_B 0xd74 -#define RZG3S_SYS_PCIE_RST_RSM_B_MASK BIT(0) - /* Maximum number of windows */ #define RZG3S_MAX_WINDOWS 8 @@ -174,6 +170,44 @@ /* Timeouts experimentally determined */ #define RZG3S_REQ_ISSUE_TIMEOUT_US 2500 +/** + * struct rzg3s_sysc_function - System Controller function descriptor + * @offset: Register offset from the System Controller base address + * @mask: Bit mask for the function within the register + */ +struct rzg3s_sysc_function { + u32 offset; + u32 mask; +}; + +/** + * enum rzg3s_sysc_func_id - System controller function IDs + * @RZG3S_SYSC_FUNC_ID_RST_RSM_B: RST_RSM_B SYSC function ID + * @RZG3S_SYSC_FUNC_ID_MAX: Max SYSC function ID + */ +enum rzg3s_sysc_func_id { + RZG3S_SYSC_FUNC_ID_RST_RSM_B, + RZG3S_SYSC_FUNC_ID_MAX, +}; + +/** + * struct rzg3s_sysc_info - RZ/G3S System Controller info + * @functions: SYSC function descriptors array + */ +struct rzg3s_sysc_info { + const struct rzg3s_sysc_function functions[RZG3S_SYSC_FUNC_ID_MAX]; +}; + +/** + * struct rzg3s_sysc - RZ/G3S System Controller descriptor + * @regmap: System controller regmap + * @info: System controller info + */ +struct rzg3s_sysc { + struct regmap *regmap; + const struct rzg3s_sysc_info *info; +}; + /** * struct rzg3s_pcie_msi - RZ/G3S PCIe MSI data structure * @domain: IRQ domain @@ -203,6 +237,7 @@ struct rzg3s_pcie_host; * power-on * @cfg_resets: array with the resets that need to be de-asserted after * configuration + * @sysc_info: SYSC info * @num_power_resets: number of power resets * @num_cfg_resets: number of configuration resets */ @@ -210,6 +245,7 @@ struct rzg3s_pcie_soc_data { int (*init_phy)(struct rzg3s_pcie_host *host); const char * const *power_resets; const char * const *cfg_resets; + struct rzg3s_sysc_info sysc_info; u8 num_power_resets; u8 num_cfg_resets; }; @@ -233,7 +269,7 @@ struct rzg3s_pcie_port { * @dev: struct device * @power_resets: reset control signals that should be set after power up * @cfg_resets: reset control signals that should be set after configuration - * @sysc: SYSC regmap + * @sysc: SYSC descriptor * @intx_domain: INTx IRQ domain * @data: SoC specific data * @msi: MSI data structure @@ -248,7 +284,7 @@ struct rzg3s_pcie_host { struct device *dev; struct reset_control_bulk_data *power_resets; struct reset_control_bulk_data *cfg_resets; - struct regmap *sysc; + struct rzg3s_sysc *sysc; struct irq_domain *intx_domain; const struct rzg3s_pcie_soc_data *data; struct rzg3s_pcie_msi msi; @@ -260,6 +296,23 @@ struct rzg3s_pcie_host { #define rzg3s_msi_to_host(_msi) container_of(_msi, struct rzg3s_pcie_host, msi) +static int rzg3s_sysc_config_func(struct rzg3s_sysc *sysc, + enum rzg3s_sysc_func_id fid, u32 val) +{ + const struct rzg3s_sysc_info *info = sysc->info; + const struct rzg3s_sysc_function *functions = info->functions; + + if (fid >= RZG3S_SYSC_FUNC_ID_MAX) + return -EINVAL; + + if (!functions[fid].mask) + return 0; + + return regmap_update_bits(sysc->regmap, functions[fid].offset, + functions[fid].mask, + field_prep(functions[fid].mask, val)); +} + static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask, u32 val) { @@ -1522,6 +1575,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) struct device_node *sysc_np __free(device_node) = of_parse_phandle(np, "renesas,sysc", 0); struct rzg3s_pcie_host *host; + struct rzg3s_sysc *sysc; int ret; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host)); @@ -1533,6 +1587,13 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) host->data = device_get_match_data(dev); platform_set_drvdata(pdev, host); + host->sysc = devm_kzalloc(dev, sizeof(*host->sysc), GFP_KERNEL); + if (!host->sysc) + return -ENOMEM; + + sysc = host->sysc; + sysc->info = &host->data->sysc_info; + host->axi = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(host->axi)) return PTR_ERR(host->axi); @@ -1546,15 +1607,13 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) if (ret) return ret; - host->sysc = syscon_node_to_regmap(sysc_np); - if (IS_ERR(host->sysc)) { - ret = PTR_ERR(host->sysc); + sysc->regmap = syscon_node_to_regmap(sysc_np); + if (IS_ERR(sysc->regmap)) { + ret = PTR_ERR(sysc->regmap); goto port_refclk_put; } - ret = regmap_update_bits(host->sysc, RZG3S_SYS_PCIE_RST_RSM_B, - RZG3S_SYS_PCIE_RST_RSM_B_MASK, - FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1)); + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 1); if (ret) goto port_refclk_put; @@ -1606,9 +1665,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) * SYSC RST_RSM_B signal need to be asserted before turning off the * power to the PHY. */ - regmap_update_bits(host->sysc, RZG3S_SYS_PCIE_RST_RSM_B, - RZG3S_SYS_PCIE_RST_RSM_B_MASK, - FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0)); + rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 0); port_refclk_put: clk_put(host->port.refclk); @@ -1620,7 +1677,7 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev) struct rzg3s_pcie_host *host = dev_get_drvdata(dev); const struct rzg3s_pcie_soc_data *data = host->data; struct rzg3s_pcie_port *port = &host->port; - struct regmap *sysc = host->sysc; + struct rzg3s_sysc *sysc = host->sysc; int ret; ret = pm_runtime_put_sync(dev); @@ -1639,9 +1696,7 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev) if (ret) goto cfg_resets_restore; - ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, - RZG3S_SYS_PCIE_RST_RSM_B_MASK, - FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0)); + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 0); if (ret) goto power_resets_restore; @@ -1664,12 +1719,10 @@ static int rzg3s_pcie_resume_noirq(struct device *dev) { struct rzg3s_pcie_host *host = dev_get_drvdata(dev); const struct rzg3s_pcie_soc_data *data = host->data; - struct regmap *sysc = host->sysc; + struct rzg3s_sysc *sysc = host->sysc; int ret; - ret = regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, - RZG3S_SYS_PCIE_RST_RSM_B_MASK, - FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 1)); + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 1); if (ret) return ret; @@ -1698,9 +1751,7 @@ static int rzg3s_pcie_resume_noirq(struct device *dev) reset_control_bulk_assert(data->num_power_resets, host->power_resets); assert_rst_rsm_b: - regmap_update_bits(sysc, RZG3S_SYS_PCIE_RST_RSM_B, - RZG3S_SYS_PCIE_RST_RSM_B_MASK, - FIELD_PREP(RZG3S_SYS_PCIE_RST_RSM_B_MASK, 0)); + rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 0); return ret; } @@ -1723,6 +1774,14 @@ static const struct rzg3s_pcie_soc_data rzg3s_soc_data = { .cfg_resets = rzg3s_soc_cfg_resets, .num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets), .init_phy = rzg3s_soc_pcie_init_phy, + .sysc_info = { + .functions = { + [RZG3S_SYSC_FUNC_ID_RST_RSM_B] = { + .offset = 0xd74, + .mask = BIT(0), + }, + }, + }, }; static const struct of_device_id rzg3s_pcie_of_match[] = { From 4ec4ccdaace40397638c305c0a36b423d2142a93 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:15 +0100 Subject: [PATCH 046/165] PCI: rzg3s-host: Make configuration reset lines optional Some SoC variants such as RZ/G3E handle configuration reset control through PCIe AXI registers instead of dedicated reset lines. Make cfg_resets optional by using devm_reset_control_bulk_get_optional_exclusive() to allow SoCs to use alternative or complementary reset control mechanisms. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-9-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index f5a0b7c6e773..c818651c0b75 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1188,9 +1188,9 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host) if (ret) return ret; - return devm_reset_control_bulk_get_exclusive(host->dev, - data->num_cfg_resets, - host->cfg_resets); + return devm_reset_control_bulk_get_optional_exclusive(host->dev, + data->num_cfg_resets, + host->cfg_resets); } static int rzg3s_pcie_host_parse_port(struct rzg3s_pcie_host *host) From 5f2c4de717786150f8d6cdbdbffb986cd3c59edb Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:16 +0100 Subject: [PATCH 047/165] PCI: rzg3s-host: Add SoC-specific configuration and initialization callbacks Add optional cfg_pre_init, cfg_post_init, and cfg_deinit callbacks to handle SoC-specific configuration methods. While RZ/G3S uses the Linux reset framework with dedicated reset lines, other SoC variants like RZ/G3E control configuration resets through PCIe AXI registers. As Linux reset bulk API gracefully handles optional NULL reset lines (num_cfg_resets = 0 for RZ/G3E), the driver continues to use the standard reset framework when reset lines are available, while custom callbacks are only invoked when provided. This provides a balanced pattern where: - RZ/G3S: Uses callbacks that fall back to the reset framework - RZ/G3E: Sets num_cfg_resets=0, provides cfg_pre_init/cfg_post_init/cfg_deinit Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-10-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 61 +++++++++++++++++------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index c818651c0b75..be6b7fa1a053 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -233,6 +233,9 @@ struct rzg3s_pcie_host; /** * struct rzg3s_pcie_soc_data - SoC specific data * @init_phy: PHY initialization function + * @config_pre_init: Optional callback for SoC-specific pre-configuration + * @config_post_init: Callback for SoC-specific post-configuration + * @config_deinit: Callback for SoC-specific de-initialization * @power_resets: array with the resets that need to be de-asserted after * power-on * @cfg_resets: array with the resets that need to be de-asserted after @@ -243,6 +246,9 @@ struct rzg3s_pcie_host; */ struct rzg3s_pcie_soc_data { int (*init_phy)(struct rzg3s_pcie_host *host); + void (*config_pre_init)(struct rzg3s_pcie_host *host); + int (*config_post_init)(struct rzg3s_pcie_host *host); + int (*config_deinit)(struct rzg3s_pcie_host *host); const char * const *power_resets; const char * const *cfg_resets; struct rzg3s_sysc_info sysc_info; @@ -1109,6 +1115,18 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) return 0; } +static int rzg3s_pcie_config_post_init(struct rzg3s_pcie_host *host) +{ + return reset_control_bulk_deassert(host->data->num_cfg_resets, + host->cfg_resets); +} + +static int rzg3s_pcie_config_deinit(struct rzg3s_pcie_host *host) +{ + return reset_control_bulk_assert(host->data->num_cfg_resets, + host->cfg_resets); +} + static void rzg3s_pcie_irq_init(struct rzg3s_pcie_host *host) { /* @@ -1257,22 +1275,26 @@ static int rzg3s_pcie_host_init(struct rzg3s_pcie_host *host) u32 val; int ret; + /* SoC-specific pre-configuration */ + if (host->data->config_pre_init) + host->data->config_pre_init(host); + /* Initialize the PCIe related registers */ ret = rzg3s_pcie_config_init(host); if (ret) - return ret; + goto config_deinit; ret = rzg3s_pcie_host_init_port(host); if (ret) - return ret; + goto config_deinit; /* Initialize the interrupts */ rzg3s_pcie_irq_init(host); - ret = reset_control_bulk_deassert(host->data->num_cfg_resets, - host->cfg_resets); + /* SoC-specific post-configuration */ + ret = host->data->config_post_init(host); if (ret) - goto disable_port_refclk; + goto config_deinit_and_refclk; /* Wait for link up */ ret = readl_poll_timeout(host->axi + RZG3S_PCI_PCSTAT1, val, @@ -1281,18 +1303,20 @@ static int rzg3s_pcie_host_init(struct rzg3s_pcie_host *host) PCIE_LINK_WAIT_SLEEP_MS * MILLI * PCIE_LINK_WAIT_MAX_RETRIES); if (ret) - goto cfg_resets_deassert; + goto config_deinit_post; val = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2); dev_info(host->dev, "PCIe link status [0x%x]\n", val); return 0; -cfg_resets_deassert: - reset_control_bulk_assert(host->data->num_cfg_resets, - host->cfg_resets); -disable_port_refclk: +config_deinit_post: + host->data->config_deinit(host); +config_deinit_and_refclk: clk_disable_unprepare(host->port.refclk); +config_deinit: + if (host->data->config_pre_init) + host->data->config_deinit(host); return ret; } @@ -1653,7 +1677,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) host_probe_teardown: rzg3s_pcie_teardown_irqdomain(host); - reset_control_bulk_assert(host->data->num_cfg_resets, host->cfg_resets); + host->data->config_deinit(host); rpm_put: pm_runtime_put_sync(dev); rpm_disable: @@ -1686,15 +1710,15 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev) clk_disable_unprepare(port->refclk); - ret = reset_control_bulk_assert(data->num_cfg_resets, - host->cfg_resets); + /* SoC-specific de-initialization */ + ret = data->config_deinit(host); if (ret) goto refclk_restore; ret = reset_control_bulk_assert(data->num_power_resets, host->power_resets); if (ret) - goto cfg_resets_restore; + goto config_reinit; ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 0); if (ret) @@ -1706,9 +1730,10 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev) power_resets_restore: reset_control_bulk_deassert(data->num_power_resets, host->power_resets); -cfg_resets_restore: - reset_control_bulk_deassert(data->num_cfg_resets, - host->cfg_resets); +config_reinit: + if (data->config_pre_init) + data->config_pre_init(host); + data->config_post_init(host); refclk_restore: clk_prepare_enable(port->refclk); pm_runtime_resume_and_get(dev); @@ -1773,6 +1798,8 @@ static const struct rzg3s_pcie_soc_data rzg3s_soc_data = { .num_power_resets = ARRAY_SIZE(rzg3s_soc_power_resets), .cfg_resets = rzg3s_soc_cfg_resets, .num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets), + .config_post_init = rzg3s_pcie_config_post_init, + .config_deinit = rzg3s_pcie_config_deinit, .init_phy = rzg3s_soc_pcie_init_phy, .sysc_info = { .functions = { From 1e75d2e9a0e018b53f06dcc2e9345ac10f1aa174 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:17 +0100 Subject: [PATCH 048/165] PCI: rzg3s-host: Explicitly set class code for RZ/G3E compatibility Program the class code register explicitly during PCIe configuration initialization. RZ/G3E requires this register to be set, while RZ/G3S has these values as hardware defaults. This configuration is harmless for RZ/G3S where these match the hardware defaults, and necessary for RZ/G3E to properly identify the device as a PCI bridge. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-11-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index be6b7fa1a053..d8a92ad12437 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1081,6 +1081,7 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) { struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); + u32 mask = GENMASK(31, 8); struct resource_entry *ft; struct resource *bus; u8 subordinate_bus; @@ -1104,6 +1105,13 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L); writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U); + /* + * Explicitly program class code. RZ/G3E requires this configuration. + * Harmless for RZ/G3S where this matches the hardware default. + */ + rzg3s_pcie_update_bits(host->pcie, PCI_CLASS_REVISION, mask, + field_prep(mask, PCI_CLASS_BRIDGE_PCI_NORMAL)); + /* Disable access control to the CFGU */ writel_relaxed(0, host->axi + RZG3S_PCI_PERM); From 5e9a5af5c9a2797a5e41e8ee565ce8108588f956 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:18 +0100 Subject: [PATCH 049/165] PCI: rzg3s-host: Add PCIe Gen3 (8.0 GT/s) link speed support Extend the link speed configuration to support Gen3 (8.0 GT/s) in addition to Gen2 (5.0 GT/s). This is required for RZ/G3E PCIe host support, which is Gen3 capable. Instead of relying on DT max-link-speed for configuration, read the hardware capabilities from the PCI_EXP_LNKCAP register to determine the maximum supported speed. The DT max-link-speed property is now only used as an optional limit when explicitly specified, which aligns with PCIe subsystem expectations. Signed-off-by: John Madieu Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-12-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index d8a92ad12437..182f135fd5e3 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -1004,8 +1004,9 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) { u32 remote_supported_link_speeds, max_supported_link_speeds; u32 cs2, tmp, pcie_cap = RZG3S_PCI_CFG_PCIEC; - u32 cur_link_speed, link_speed; + u32 cur_link_speed, link_speed, hw_max_speed; u8 ltssm_state_l0 = 0xc; + u32 lnkcap; int ret; u16 ls; @@ -1025,7 +1026,22 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) ls = readw_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKSTA); cs2 = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2); - switch (pcie_link_speed[host->max_link_speed]) { + /* Read hardware supported link speed from Link Capabilities Register */ + lnkcap = readl_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKCAP); + hw_max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, lnkcap); + + /* + * Use DT max-link-speed only as a limit. If specified and lower + * than hardware capability, cap to that value. + */ + if (host->max_link_speed > 0 && host->max_link_speed < hw_max_speed) + hw_max_speed = host->max_link_speed; + + switch (pcie_link_speed[hw_max_speed]) { + case PCIE_SPEED_8_0GT: + max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_8_0GB - 1, 0); + link_speed = PCI_EXP_LNKCTL2_TLS_8_0GT; + break; case PCIE_SPEED_5_0GT: max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_5_0GB - 1, 0); link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT; @@ -1041,10 +1057,10 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) remote_supported_link_speeds &= max_supported_link_speeds; /* - * Return if max link speed is already set or the connected device + * Return if target link speed is already set or the connected device * doesn't support it. */ - if (cur_link_speed == host->max_link_speed || + if (cur_link_speed == hw_max_speed || remote_supported_link_speeds != max_supported_link_speeds) return 0; @@ -1632,8 +1648,6 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) host->pcie = host->axi + RZG3S_PCI_CFG_BASE; host->max_link_speed = of_pci_get_max_link_speed(np); - if (host->max_link_speed < 0) - host->max_link_speed = 2; ret = rzg3s_pcie_host_parse_port(host); if (ret) From 8197ec49a2062185f6bd432a19969ce0b3752e94 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Fri, 6 Mar 2026 15:34:19 +0100 Subject: [PATCH 050/165] PCI: rzg3s-host: Add support for RZ/G3E PCIe controller Add support for the PCIe controller found in RZ/G3E SoCs to the existing RZ/G3S PCIe host controller driver. The RZ/G3E PCIe controller is similar to the RZ/G3S, with the following key differences: - Supports PCIe Gen3 (8.0 GT/s) link speeds alongside Gen2 (5.0 GT/s) - Uses a different reset control mechanism via AXI registers instead of the Linux reset framework - Requires specific SYSC configuration for link state control and Root Complex mode selection Signed-off-by: John Madieu [mani: added a readl_relaxed() before fsleep() to flush previous write] Signed-off-by: Manivannan Sadhasivam Tested-by: Lad Prabhakar # RZ/V2N EVK Tested-by: Claudiu Beznea Reviewed-by: Claudiu Beznea Link: https://patch.msgid.link/20260306143423.19562-13-john.madieu.xa@bp.renesas.com --- drivers/pci/controller/pcie-rzg3s-host.c | 93 ++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 182f135fd5e3..bfc210e696ed 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -111,6 +111,15 @@ #define RZG3S_PCI_PERM_CFG_HWINIT_EN BIT(2) #define RZG3S_PCI_PERM_PIPE_PHY_REG_EN BIT(1) +#define RZG3S_PCI_RESET 0x310 +#define RZG3S_PCI_RESET_RST_OUT_B BIT(6) +#define RZG3S_PCI_RESET_RST_PS_B BIT(5) +#define RZG3S_PCI_RESET_RST_LOAD_B BIT(4) +#define RZG3S_PCI_RESET_RST_CFG_B BIT(3) +#define RZG3S_PCI_RESET_RST_RSM_B BIT(2) +#define RZG3S_PCI_RESET_RST_GP_B BIT(1) +#define RZG3S_PCI_RESET_RST_B BIT(0) + #define RZG3S_PCI_MSIRE(id) (0x600 + (id) * 0x10) #define RZG3S_PCI_MSIRE_ENA BIT(0) @@ -183,10 +192,14 @@ struct rzg3s_sysc_function { /** * enum rzg3s_sysc_func_id - System controller function IDs * @RZG3S_SYSC_FUNC_ID_RST_RSM_B: RST_RSM_B SYSC function ID + * @RZG3S_SYSC_FUNC_ID_L1_ALLOW: L1 allow SYSC function ID + * @RZG3S_SYSC_FUNC_ID_MODE: Mode SYSC function ID * @RZG3S_SYSC_FUNC_ID_MAX: Max SYSC function ID */ enum rzg3s_sysc_func_id { RZG3S_SYSC_FUNC_ID_RST_RSM_B, + RZG3S_SYSC_FUNC_ID_L1_ALLOW, + RZG3S_SYSC_FUNC_ID_MODE, RZG3S_SYSC_FUNC_ID_MAX, }; @@ -1151,6 +1164,45 @@ static int rzg3s_pcie_config_deinit(struct rzg3s_pcie_host *host) host->cfg_resets); } +static void rzg3e_pcie_config_pre_init(struct rzg3s_pcie_host *host) +{ + u32 mask = RZG3S_PCI_RESET_RST_LOAD_B | RZG3S_PCI_RESET_RST_CFG_B; + + /* De-assert LOAD_B and CFG_B */ + rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_RESET, mask, mask); +} + +static int rzg3e_pcie_config_deinit(struct rzg3s_pcie_host *host) +{ + writel_relaxed(0, host->axi + RZG3S_PCI_RESET); + return 0; +} + +static int rzg3e_pcie_config_post_init(struct rzg3s_pcie_host *host) +{ + u32 mask = RZG3S_PCI_RESET_RST_PS_B | RZG3S_PCI_RESET_RST_GP_B | + RZG3S_PCI_RESET_RST_B; + + /* De-assert PS_B, GP_B, RST_B */ + rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_RESET, mask, mask); + + /* Flush deassert */ + readl_relaxed(host->axi + RZG3S_PCI_RESET); + + /* + * According to the RZ/G3E HW manual (Rev.1.15, Table 6.6-130 + * Initialization Procedure (RC)), hardware requires >= 500us delay + * before final reset deassert. + */ + fsleep(500); + + /* De-assert OUT_B and RSM_B */ + mask = RZG3S_PCI_RESET_RST_OUT_B | RZG3S_PCI_RESET_RST_RSM_B; + rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_RESET, mask, mask); + + return 0; +} + static void rzg3s_pcie_irq_init(struct rzg3s_pcie_host *host) { /* @@ -1312,6 +1364,12 @@ static int rzg3s_pcie_host_init(struct rzg3s_pcie_host *host) if (ret) goto config_deinit; + /* Enable ASPM L1 transition for SoCs that use it */ + ret = rzg3s_sysc_config_func(host->sysc, + RZG3S_SYSC_FUNC_ID_L1_ALLOW, 1); + if (ret) + goto config_deinit_and_refclk; + /* Initialize the interrupts */ rzg3s_pcie_irq_init(host); @@ -1659,6 +1717,11 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) goto port_refclk_put; } + /* Put controller in RC mode */ + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_MODE, 1); + if (ret) + goto port_refclk_put; + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 1); if (ret) goto port_refclk_put; @@ -1769,6 +1832,10 @@ static int rzg3s_pcie_resume_noirq(struct device *dev) struct rzg3s_sysc *sysc = host->sysc; int ret; + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_MODE, 1); + if (ret) + return ret; + ret = rzg3s_sysc_config_func(sysc, RZG3S_SYSC_FUNC_ID_RST_RSM_B, 1); if (ret) return ret; @@ -1833,11 +1900,37 @@ static const struct rzg3s_pcie_soc_data rzg3s_soc_data = { }, }; +static const char * const rzg3e_soc_power_resets[] = { "aresetn" }; + +static const struct rzg3s_pcie_soc_data rzg3e_soc_data = { + .power_resets = rzg3e_soc_power_resets, + .num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets), + .config_pre_init = rzg3e_pcie_config_pre_init, + .config_post_init = rzg3e_pcie_config_post_init, + .config_deinit = rzg3e_pcie_config_deinit, + .sysc_info = { + .functions = { + [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = { + .offset = 0x1020, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_MODE] = { + .offset = 0x1024, + .mask = BIT(0), + }, + }, + }, +}; + static const struct of_device_id rzg3s_pcie_of_match[] = { { .compatible = "renesas,r9a08g045-pcie", .data = &rzg3s_soc_data, }, + { + .compatible = "renesas,r9a09g047-pcie", + .data = &rzg3e_soc_data, + }, {} }; From 0834d6f4abd0ca35b5706d267a6e4b78303a95de Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:29 +0100 Subject: [PATCH 051/165] PCI: endpoint: Do not mark the BAR succeeding a 64-bit BAR as BAR_RESERVED A BAR that can only be configured as a 64-bit BAR by an EPC driver is marked as such using the "only_64bit" flag. Currently, the documentation says that an EPC driver should explicitly mark the BAR succeeding an "only_64bit" BAR as BAR_RESERVED. However, a 64-bit BAR will always take up two BARs. It is thus redundant to mark both BARs. pci_epc_get_next_free_bar() already skips the BAR succeeding a "only_64bit" BAR, regardless if the succeeding BAR is marked as BAR_RESERVED or not. Thus, drop the BAR_RESERVED for a BAR succeeding a "only_64bit" BAR. No functional changes. Suggested-by: Manivannan Sadhasivam Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260312130229.2282001-13-cassel@kernel.org --- drivers/pci/controller/dwc/pci-layerscape-ep.c | 2 -- drivers/pci/controller/dwc/pcie-keembay.c | 3 --- drivers/pci/controller/dwc/pcie-qcom-ep.c | 2 -- drivers/pci/controller/dwc/pcie-tegra194.c | 1 - drivers/pci/controller/dwc/pcie-uniphier-ep.c | 5 ----- drivers/pci/controller/pcie-rcar-ep.c | 3 --- include/linux/pci-epc.h | 3 +-- 7 files changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index a4a800699f89..79d226e0cc80 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -251,9 +251,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) pci->ops = pcie->drvdata->dw_pcie_ops; ls_epc->bar[BAR_2].only_64bit = true; - ls_epc->bar[BAR_3].type = BAR_RESERVED; ls_epc->bar[BAR_4].only_64bit = true; - ls_epc->bar[BAR_5].type = BAR_RESERVED; ls_epc->linkup_notifier = true; pcie->pci = pci; diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 2666a9c3d67e..7cf2c312ecec 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -313,11 +313,8 @@ static const struct pci_epc_features keembay_pcie_epc_features = { .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .only_64bit = true, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .only_64bit = true, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, .align = SZ_16K, }; diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 18460f01b2c6..ffb4409c0468 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -850,9 +850,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = { .msi_capable = true, .align = SZ_4K, .bar[BAR_0] = { .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .only_64bit = true, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, }; static const struct pci_epc_features * diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 06571d806ab3..f1f70fb824b2 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1993,7 +1993,6 @@ static const struct pci_epc_features tegra_pcie_epc_features = { .msi_capable = true, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_RESERVED, }, diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index d52753060970..b7020131f626 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -426,9 +426,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .msix_capable = false, .align = 1 << 16, .bar[BAR_0] = { .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .only_64bit = true, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_RESERVED, }, .bar[BAR_5] = { .type = BAR_RESERVED, }, }, @@ -445,11 +443,8 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = { .msix_capable = false, .align = 1 << 12, .bar[BAR_0] = { .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .only_64bit = true, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .only_64bit = true, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, }, }; diff --git a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c index 657875ef4657..c2da8ac1f2e8 100644 --- a/drivers/pci/controller/pcie-rcar-ep.c +++ b/drivers/pci/controller/pcie-rcar-ep.c @@ -440,13 +440,10 @@ static const struct pci_epc_features rcar_pcie_epc_features = { /* use 64-bit BARs so mark BAR[1,3,5] as reserved */ .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = 128, .only_64bit = true, }, - .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = 256, .only_64bit = true, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256, .only_64bit = true, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, }; static const struct pci_epc_features* diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index c021c7af175f..c981ea7d52c0 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -206,8 +206,7 @@ enum pci_epc_bar_type { * @fixed_size: the fixed size, only applicable if type is BAR_FIXED_MASK. * @only_64bit: if true, an EPF driver is not allowed to choose if this BAR * should be configured as 32-bit or 64-bit, the EPF driver must - * configure this BAR as 64-bit. Additionally, the BAR succeeding - * this BAR must be set to type BAR_RESERVED. + * configure this BAR as 64-bit. * * only_64bit should not be set on a BAR of type BAR_RESERVED. * (If BARx is a 64-bit BAR that an EPF driver is not allowed to From 27ce1d8ecb9b9ae025b9e9e199845624bc950998 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Thu, 12 Mar 2026 14:02:30 +0100 Subject: [PATCH 052/165] PCI: endpoint: Allow only_64bit on BAR_RESERVED Remove the documentation that forbids setting only_64bit on a BAR of type BAR_RESERVED. When a reserved BAR is 64-bit by default, setting only_64bit is the most accurate description. If we later add support to disable a reserved BAR (e.g. disable_bar() for BARs that were never set via set_bar()), the implementation will need to clear the adjacent BAR (upper 32 bits) as well; having only_64bit set documents that requirement. Signed-off-by: Manikanta Maddireddy Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260312130229.2282001-14-cassel@kernel.org --- include/linux/pci-epc.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index c981ea7d52c0..5c59f5606869 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -207,11 +207,6 @@ enum pci_epc_bar_type { * @only_64bit: if true, an EPF driver is not allowed to choose if this BAR * should be configured as 32-bit or 64-bit, the EPF driver must * configure this BAR as 64-bit. - * - * only_64bit should not be set on a BAR of type BAR_RESERVED. - * (If BARx is a 64-bit BAR that an EPF driver is not allowed to - * touch, then both BARx and BARx+1 must be set to type - * BAR_RESERVED.) */ struct pci_epc_bar_desc { enum pci_epc_bar_type type; From f51644eb40a73677fcd0c92d8174eddde5d0be0e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 12 Mar 2026 14:02:31 +0100 Subject: [PATCH 053/165] PCI: endpoint: Describe reserved subregions within BARs Some endpoint controllers expose platform-owned, fixed register windows within a BAR that EPF drivers must not reprogram (e.g. a BAR marked BAR_RESERVED). Even in that case, EPF drivers may need to reference a well-defined subset of that BAR, e.g. to reuse an integrated DMA controller MMIO window as a doorbell target. Introduce struct pci_epc_bar_rsvd_region and extend struct pci_epc_bar_desc so EPC drivers can advertise such fixed subregions in a controller-agnostic way. No functional change for existing users. Signed-off-by: Koichiro Den Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Manikanta Maddireddy Tested-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-15-cassel@kernel.org --- include/linux/pci-epc.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 5c59f5606869..ebcdf70aa9b9 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -200,6 +200,30 @@ enum pci_epc_bar_type { BAR_RESERVED, }; +/** + * enum pci_epc_bar_rsvd_region_type - type of a fixed subregion behind a BAR + * @PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO: Integrated DMA controller MMIO window + * + * BARs marked BAR_RESERVED are owned by the SoC/EPC hardware and must not be + * reprogrammed by EPF drivers. Some of them still expose fixed subregions that + * EPFs may want to reference (e.g. embedded doorbell fallback). + */ +enum pci_epc_bar_rsvd_region_type { + PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO = 0, +}; + +/** + * struct pci_epc_bar_rsvd_region - fixed subregion behind a BAR + * @type: reserved region type + * @offset: offset within the BAR aperture + * @size: size of the reserved region + */ +struct pci_epc_bar_rsvd_region { + enum pci_epc_bar_rsvd_region_type type; + resource_size_t offset; + resource_size_t size; +}; + /** * struct pci_epc_bar_desc - hardware description for a BAR * @type: the type of the BAR @@ -207,11 +231,15 @@ enum pci_epc_bar_type { * @only_64bit: if true, an EPF driver is not allowed to choose if this BAR * should be configured as 32-bit or 64-bit, the EPF driver must * configure this BAR as 64-bit. + * @nr_rsvd_regions: number of fixed subregions described for BAR_RESERVED + * @rsvd_regions: fixed subregions behind BAR_RESERVED */ struct pci_epc_bar_desc { enum pci_epc_bar_type type; u64 fixed_size; bool only_64bit; + u8 nr_rsvd_regions; + const struct pci_epc_bar_rsvd_region *rsvd_regions; }; /** From 489d3abb4117bdf4af8296c69f09493e21b70c28 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 12 Mar 2026 14:02:32 +0100 Subject: [PATCH 054/165] PCI: dw-rockchip: Describe RK3588 BAR4 DMA ctrl window On RK3588 PCIe3_4L in EP mode, the integrated DMA controller registers are permanently mapped to BAR4 and must not be repurposed by EPF drivers. When the remote peer needs to access these registers, it must use the fixed BAR4 window instead of creating another inbound mapping in a different BAR. Mixing the fixed window with an additional mapping can lead to incorrect behavior. Advertise the DMA controller MMIO window as a reserved BAR subregion so EPF drivers can reuse it safely. Signed-off-by: Koichiro Den Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Koichiro Den Reviewed-by: Manikanta Maddireddy Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-16-cassel@kernel.org --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 5b17da63151d..ecc28093c589 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -403,6 +403,15 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { .bar[BAR_5] = { .type = BAR_RESIZABLE, }, }; +static const struct pci_epc_bar_rsvd_region rk3588_bar4_rsvd[] = { + { + /* DMA_CAP (BAR4: DMA Port Logic Structure) */ + .type = PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO, + .offset = 0x0, + .size = 0x2000, + }, +}; + /* * BAR4 on rk3588 exposes the ATU Port Logic Structure to the host regardless of * iATU settings for BAR4. This means that BAR4 cannot be used by an EPF driver, @@ -420,7 +429,11 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { .bar[BAR_1] = { .type = BAR_RESIZABLE, }, .bar[BAR_2] = { .type = BAR_RESIZABLE, }, .bar[BAR_3] = { .type = BAR_RESIZABLE, }, - .bar[BAR_4] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { + .type = BAR_RESERVED, + .nr_rsvd_regions = ARRAY_SIZE(rk3588_bar4_rsvd), + .rsvd_regions = rk3588_bar4_rsvd, + }, .bar[BAR_5] = { .type = BAR_RESIZABLE, }, }; From 33642e9e36dc084e4fc9245a266c9843bc8303b9 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:33 +0100 Subject: [PATCH 055/165] PCI: endpoint: Introduce pci_epc_bar_type BAR_DISABLED Add a pci_epc_bar_type BAR_DISABLED to more clearly differentiate from BAR_RESERVED. This BAR type will only be used to describe a BAR that the EPC driver should disable, and will thus never be available to an EPF driver. (Unlike BAR_RESERVED, which will never be disabled by default by an EPC driver.) Co-developed-by: Manikanta Maddireddy Signed-off-by: Manikanta Maddireddy Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Koichiro Den Tested-by: Manikanta Maddireddy Reviewed-by: Frank Li Reviewed-by: Manikanta Maddireddy Link: https://patch.msgid.link/20260312130229.2282001-17-cassel@kernel.org --- drivers/pci/endpoint/pci-epc-core.c | 5 +++-- include/linux/pci-epc.h | 10 +++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e546b3dbb240..6c3c58185fc5 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -103,8 +103,9 @@ enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features bar++; for (i = bar; i < PCI_STD_NUM_BARS; i++) { - /* If the BAR is not reserved, return it. */ - if (epc_features->bar[i].type != BAR_RESERVED) + /* If the BAR is not reserved or disabled, return it. */ + if (epc_features->bar[i].type != BAR_RESERVED && + epc_features->bar[i].type != BAR_DISABLED) return i; } diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index ebcdf70aa9b9..334c2b7578d0 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -191,13 +191,21 @@ struct pci_epc { * @BAR_RESIZABLE: The BAR implements the PCI-SIG Resizable BAR Capability. * NOTE: An EPC driver can currently only set a single supported * size. - * @BAR_RESERVED: The BAR should not be touched by an EPF driver. + * @BAR_RESERVED: Used for HW-backed BARs (e.g. MSI-X table, DMA regs). The BAR + * should not be disabled by an EPC driver. The BAR should not be + * reprogrammed by an EPF driver. An EPF driver is allowed to + * disable the BAR if absolutely necessary. (However, right now + * there is no EPC operation to disable a BAR that has not been + * programmed using pci_epc_set_bar().) + * @BAR_DISABLED: The BAR should be disabled by an EPC driver. The BAR will be + * unavailable to an EPF driver. */ enum pci_epc_bar_type { BAR_PROGRAMMABLE = 0, BAR_FIXED, BAR_RESIZABLE, BAR_RESERVED, + BAR_DISABLED, }; /** From 5a95fecbdd4216d0451c9e7e332ffd0b6e239f0e Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:34 +0100 Subject: [PATCH 056/165] PCI: dwc: Replace certain BAR_RESERVED with BAR_DISABLED in glue drivers Most DWC based EPC glue drivers that have BARs marked as BAR_RESERVED in epc_features also call dw_pcie_ep_reset_bar() for these reserved BARs in ep->ops->init(). (The only exception is pci-keystone.c.) An EPF driver will be able to get/enable BARs that have been disabled/reset using dw_pcie_ep_reset_bar(), except if the BAR is marked as BAR_RESERVED (see pci_epc_get_next_free_bar()). Thus, all EPC drivers that have BARs marked as BAR_RESERVED in epc_features and call dw_pcie_ep_reset_bar(), should really have these BARs marked as BAR_DISABLED. If dw_pcie_ep_reset_bar() is not called by the glue driver, the BARs are kept as BAR_RESERVED. No EPC drivers outside drivers/pci/controllers/dwc mark their BARs as BAR_RESERVED, so there is nothing to do in non-DWC based EPC drivers. Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Manikanta Maddireddy Tested-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-18-cassel@kernel.org --- drivers/pci/controller/dwc/pci-imx6.c | 12 ++++++------ drivers/pci/controller/dwc/pci-keystone.c | 12 ++++++++++++ drivers/pci/controller/dwc/pcie-rcar-gen4.c | 6 +++--- drivers/pci/controller/dwc/pcie-tegra194.c | 8 ++++---- drivers/pci/controller/dwc/pcie-uniphier-ep.c | 4 ++-- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index a5b8d0b71677..ec1e3557ca53 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1433,19 +1433,19 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features imx8m_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .msi_capable = true, - .bar[BAR_1] = { .type = BAR_RESERVED, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_1] = { .type = BAR_DISABLED, }, + .bar[BAR_3] = { .type = BAR_DISABLED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = SZ_256, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, + .bar[BAR_5] = { .type = BAR_DISABLED, }, .align = SZ_64K, }; static const struct pci_epc_features imx8q_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .msi_capable = true, - .bar[BAR_1] = { .type = BAR_RESERVED, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, + .bar[BAR_1] = { .type = BAR_DISABLED, }, + .bar[BAR_3] = { .type = BAR_DISABLED, }, + .bar[BAR_5] = { .type = BAR_DISABLED, }, .align = SZ_64K, }; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 20fa4dadb82a..278d2dba1db0 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -933,6 +933,18 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = { DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, + /* + * TODO: This driver is the only DWC glue driver that had BAR_RESERVED + * BARs, but did not call dw_pcie_ep_reset_bar() for the reserved BARs. + * + * To not change the existing behavior, these BARs were not migrated to + * BAR_DISABLED. If this driver wants the BAR_RESERVED BARs to be + * disabled, it should migrate them to BAR_DISABLED. + * + * If they actually should be enabled, then the driver must also define + * what is behind these reserved BARs, see the definition of struct + * pci_epc_bar_rsvd_region. + */ .bar[BAR_0] = { .type = BAR_RESERVED, }, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_2] = { .type = BAR_RESIZABLE, }, diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index a6912e85e4dd..9dd05bac22b9 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -422,10 +422,10 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features rcar_gen4_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .msi_capable = true, - .bar[BAR_1] = { .type = BAR_RESERVED, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_1] = { .type = BAR_DISABLED, }, + .bar[BAR_3] = { .type = BAR_DISABLED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256 }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, + .bar[BAR_5] = { .type = BAR_DISABLED, }, .align = SZ_1M, }; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index f1f70fb824b2..61b9771004da 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1993,10 +1993,10 @@ static const struct pci_epc_features tegra_pcie_epc_features = { .msi_capable = true, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, .only_64bit = true, }, - .bar[BAR_2] = { .type = BAR_RESERVED, }, - .bar[BAR_3] = { .type = BAR_RESERVED, }, - .bar[BAR_4] = { .type = BAR_RESERVED, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .type = BAR_DISABLED, }, + .bar[BAR_3] = { .type = BAR_DISABLED, }, + .bar[BAR_4] = { .type = BAR_DISABLED, }, + .bar[BAR_5] = { .type = BAR_DISABLED, }, .align = SZ_64K, }; diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index b7020131f626..d6ec6823e180 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -427,8 +427,8 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .align = 1 << 16, .bar[BAR_0] = { .only_64bit = true, }, .bar[BAR_2] = { .only_64bit = true, }, - .bar[BAR_4] = { .type = BAR_RESERVED, }, - .bar[BAR_5] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { .type = BAR_DISABLED, }, + .bar[BAR_5] = { .type = BAR_DISABLED, }, }, }; From 0f08179c8c29bc5ab23906d2ab5409d98a4ca110 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:35 +0100 Subject: [PATCH 057/165] PCI: dwc: Disable BARs in common code instead of in each glue driver The current EPC core design relies on an EPC driver disabling all BARs by default. An EPF driver will then enable the BARs that it wants to enabled. This design is there because there is no epc->ops->disable_bar(). (There is a epc->ops->clear_bar(), but that is only to disable a BAR that has been enabled using epc->ops->set_bar() first.) By default, an EPF driver will not be able to get/enable BARs that are marked as BAR_RESERVED or BAR_DISABLED (see pci_epc_get_next_free_bar()). Since the current EPC code design requires an EPC driver to disable all BARs by default, move this to DWC common code from each glue driver. BAR_RESERVED BARs are not disabled by default because these BARs are hardware backed, and should only be disabled explicitly by an EPF driver if absolutely necessary for the EPF driver to function correctly. (This is similar to how e.g. NVMe may have vendor specific BARs outside of the mandatory BAR0 which contains the NVMe registers.) Note that there is currently no EPC operation to disable a BAR that has not first been programmed using pci_epc_set_bar(). If an EPF driver ever wants to disable a BAR marked as BAR_RESERVED, a disable_bar() operation would have to be added first. No functional changes intended. Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Manikanta Maddireddy Tested-by: Koichiro Den Reviewed-by: Manikanta Maddireddy Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-19-cassel@kernel.org --- drivers/pci/controller/dwc/pci-dra7xx.c | 4 ---- drivers/pci/controller/dwc/pci-imx6.c | 10 -------- .../pci/controller/dwc/pci-layerscape-ep.c | 4 ---- drivers/pci/controller/dwc/pcie-artpec6.c | 4 ---- .../pci/controller/dwc/pcie-designware-ep.c | 24 +++++++++++++++++++ .../pci/controller/dwc/pcie-designware-plat.c | 10 -------- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 8 +------ drivers/pci/controller/dwc/pcie-qcom-ep.c | 10 -------- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 10 -------- drivers/pci/controller/dwc/pcie-stm32-ep.c | 10 -------- drivers/pci/controller/dwc/pcie-tegra194.c | 10 -------- drivers/pci/controller/dwc/pcie-uniphier-ep.c | 10 -------- 12 files changed, 25 insertions(+), 89 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index d5d26229063f..cd904659c321 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -378,10 +378,6 @@ static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - enum pci_barno bar; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); dra7xx_pcie_enable_wrapper_interrupts(dra7xx); } diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index ec1e3557ca53..f5fe5cfc46c7 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1401,15 +1401,6 @@ static const struct dw_pcie_ops dw_pcie_ops = { .stop_link = imx_pcie_stop_link, }; -static void imx_pcie_ep_init(struct dw_pcie_ep *ep) -{ - enum pci_barno bar; - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, unsigned int type, u16 interrupt_num) { @@ -1478,7 +1469,6 @@ imx_pcie_ep_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .init = imx_pcie_ep_init, .raise_irq = imx_pcie_ep_raise_irq, .get_features = imx_pcie_ep_get_features, }; diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 79d226e0cc80..8936975ff104 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -152,15 +152,11 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci); struct dw_pcie_ep_func *ep_func; - enum pci_barno bar; ep_func = dw_pcie_ep_get_func_from_ep(ep, 0); if (!ep_func) return; - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); - pcie->ls_epc->msi_capable = ep_func->msi_cap ? true : false; pcie->ls_epc->msix_capable = ep_func->msix_cap ? true : false; } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index e994b75986c3..55cb957ae1f3 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -340,15 +340,11 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - enum pci_barno bar; artpec6_pcie_assert_core_reset(artpec6_pcie); artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); artpec6_pcie_wait_for_phy(artpec6_pcie); - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); } static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 295076cf70de..386bfb7b2bf6 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -1114,6 +1114,28 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) dw_pcie_dbi_ro_wr_dis(pci); } +static void dw_pcie_ep_disable_bars(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_epc_bar_type bar_type; + enum pci_barno bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + bar_type = dw_pcie_ep_get_bar_type(ep, bar); + + /* + * Reserved BARs should not get disabled by default. All other + * BAR types are disabled by default. + * + * This is in line with the current EPC core design, where all + * BARs are disabled by default, and then the EPF driver enables + * the BARs it wishes to use. + */ + if (bar_type != BAR_RESERVED) + dw_pcie_ep_reset_bar(pci, bar); + } +} + /** * dw_pcie_ep_init_registers - Initialize DWC EP specific registers * @ep: DWC EP device @@ -1196,6 +1218,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ep->ops->init) ep->ops->init(ep); + dw_pcie_ep_disable_bars(ep); + /* * PCIe r6.0, section 7.9.15 states that for endpoints that support * PTM, this capability structure is required in exactly one diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 8530746ec5cb..d103ab759c4e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -32,15 +32,6 @@ struct dw_plat_pcie_of_data { static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = { }; -static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, unsigned int type, u16 interrupt_num) { @@ -73,7 +64,6 @@ dw_plat_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .init = dw_plat_pcie_ep_init, .raise_irq = dw_plat_pcie_ep_raise_irq, .get_features = dw_plat_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index ecc28093c589..8db27199cfa6 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -361,13 +361,9 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; rockchip_pcie_enable_l0s(pci); rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep); - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); }; static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -415,9 +411,7 @@ static const struct pci_epc_bar_rsvd_region rk3588_bar4_rsvd[] = { /* * BAR4 on rk3588 exposes the ATU Port Logic Structure to the host regardless of * iATU settings for BAR4. This means that BAR4 cannot be used by an EPF driver, - * so mark it as RESERVED. (rockchip_pcie_ep_init() will disable all BARs by - * default.) If the host could write to BAR4, the iATU settings (for all other - * BARs) would be overwritten, resulting in (all other BARs) no longer working. + * so mark it as RESERVED. */ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { DWC_EPC_COMMON_FEATURES, diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index ffb4409c0468..8e8c58a42bc3 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -859,17 +859,7 @@ qcom_pcie_epc_get_features(struct dw_pcie_ep *pci_ep) return &qcom_pcie_epc_features; } -static void qcom_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static const struct dw_pcie_ep_ops pci_ep_ops = { - .init = qcom_pcie_ep_init, .raise_irq = qcom_pcie_ep_raise_irq, .get_features = qcom_pcie_epc_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 9dd05bac22b9..1198ddc1752c 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -386,15 +386,6 @@ static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN); } -static void rcar_gen4_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar) { writel(0, rcar->base + PCIEDMAINTSTSEN); @@ -449,7 +440,6 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, static const struct dw_pcie_ep_ops pcie_ep_ops = { .pre_init = rcar_gen4_pcie_ep_pre_init, - .init = rcar_gen4_pcie_ep_init, .raise_irq = rcar_gen4_pcie_ep_raise_irq, .get_features = rcar_gen4_pcie_ep_get_features, .get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset, diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c index c1944b40ce02..a7988dff1045 100644 --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c @@ -28,15 +28,6 @@ struct stm32_pcie { unsigned int perst_irq; }; -static void stm32_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static int stm32_pcie_start_link(struct dw_pcie *pci) { struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); @@ -82,7 +73,6 @@ stm32_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops stm32_pcie_ep_ops = { - .init = stm32_pcie_ep_init, .raise_irq = stm32_pcie_raise_irq, .get_features = stm32_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 61b9771004da..6881f0b94c73 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1923,15 +1923,6 @@ static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg) return IRQ_HANDLED; } -static void tegra_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - dw_pcie_ep_reset_bar(pci, bar); -}; - static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq) { /* Tegra194 supports only INTA */ @@ -2007,7 +1998,6 @@ tegra_pcie_ep_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .init = tegra_pcie_ep_init, .raise_irq = tegra_pcie_ep_raise_irq, .get_features = tegra_pcie_ep_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index d6ec6823e180..89fb78200222 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -203,15 +203,6 @@ static void uniphier_pcie_stop_link(struct dw_pcie *pci) uniphier_pcie_ltssm_enable(priv, false); } -static void uniphier_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - static int uniphier_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -283,7 +274,6 @@ uniphier_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = { - .init = uniphier_pcie_ep_init, .raise_irq = uniphier_pcie_ep_raise_irq, .get_features = uniphier_pcie_get_features, }; From 50a1fd6e5ee3fd93e1a5a49fb9c76ca44ac13b8b Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:36 +0100 Subject: [PATCH 058/165] PCI: endpoint: pci-epf-test: Advertise reserved BARs Advertise reserved BARs as reserved in the Capabilities register, such that the host side driver will be able to skip reserved BARs. Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Koichiro Den Tested-by: Manikanta Maddireddy Reviewed-by: Manikanta Maddireddy Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-20-cassel@kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 6030ae1373b1..14e61ebe1f11 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -65,6 +65,12 @@ #define CAP_INTX BIT(3) #define CAP_SUBRANGE_MAPPING BIT(4) #define CAP_DYNAMIC_INBOUND_MAPPING BIT(5) +#define CAP_BAR0_RESERVED BIT(6) +#define CAP_BAR1_RESERVED BIT(7) +#define CAP_BAR2_RESERVED BIT(8) +#define CAP_BAR3_RESERVED BIT(9) +#define CAP_BAR4_RESERVED BIT(10) +#define CAP_BAR5_RESERVED BIT(11) #define PCI_EPF_TEST_BAR_SUBRANGE_NSUB 2 @@ -1112,6 +1118,24 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf) epf_test->epc_features->subrange_mapping) caps |= CAP_SUBRANGE_MAPPING; + if (epf_test->epc_features->bar[BAR_0].type == BAR_RESERVED) + caps |= CAP_BAR0_RESERVED; + + if (epf_test->epc_features->bar[BAR_1].type == BAR_RESERVED) + caps |= CAP_BAR1_RESERVED; + + if (epf_test->epc_features->bar[BAR_2].type == BAR_RESERVED) + caps |= CAP_BAR2_RESERVED; + + if (epf_test->epc_features->bar[BAR_3].type == BAR_RESERVED) + caps |= CAP_BAR3_RESERVED; + + if (epf_test->epc_features->bar[BAR_4].type == BAR_RESERVED) + caps |= CAP_BAR4_RESERVED; + + if (epf_test->epc_features->bar[BAR_5].type == BAR_RESERVED) + caps |= CAP_BAR5_RESERVED; + reg->caps = cpu_to_le32(caps); } From c3f33af67e6458f4e49d016429d7aeef3c8b8399 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:37 +0100 Subject: [PATCH 059/165] misc: pci_endpoint_test: Give reserved BARs a distinct error code Give reserved BARs a distinct error code, such that the pci_endpoint_test selftest will be able to skip test cases that are run against reserved BARs. Signed-off-by: Niklas Cassel [mani: Used __fls(CAP_BAR0_RESERVED) instead of PCI_ENDPOINT_CAP_BAR0_RESERVED_BIT] Signed-off-by: Manivannan Sadhasivam Tested-by: Manikanta Maddireddy Tested-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-21-cassel@kernel.org --- drivers/misc/pci_endpoint_test.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 93cd57d20881..55e128ed82f0 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -85,6 +85,12 @@ #define CAP_INTX BIT(3) #define CAP_SUBRANGE_MAPPING BIT(4) #define CAP_DYNAMIC_INBOUND_MAPPING BIT(5) +#define CAP_BAR0_RESERVED BIT(6) +#define CAP_BAR1_RESERVED BIT(7) +#define CAP_BAR2_RESERVED BIT(8) +#define CAP_BAR3_RESERVED BIT(9) +#define CAP_BAR4_RESERVED BIT(10) +#define CAP_BAR5_RESERVED BIT(11) #define PCI_ENDPOINT_TEST_DB_BAR 0x34 #define PCI_ENDPOINT_TEST_DB_OFFSET 0x38 @@ -276,6 +282,11 @@ static int pci_endpoint_test_request_irq(struct pci_endpoint_test *test) return ret; } +static bool bar_is_reserved(struct pci_endpoint_test *test, enum pci_barno bar) +{ + return test->ep_caps & BIT(bar + __fls(CAP_BAR0_RESERVED)); +} + static const u32 bar_test_pattern[] = { 0xA0A0A0A0, 0xA1A1A1A1, @@ -404,7 +415,7 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test) /* Write all BARs in order (without reading). */ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) - if (test->bar[bar]) + if (test->bar[bar] && !bar_is_reserved(test, bar)) pci_endpoint_test_bars_write_bar(test, bar); /* @@ -414,7 +425,7 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test) * (Reading back the BAR directly after writing can not detect this.) */ for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { - if (test->bar[bar]) { + if (test->bar[bar] && !bar_is_reserved(test, bar)) { ret = pci_endpoint_test_bars_read_bar(test, bar); if (ret) return ret; @@ -1143,6 +1154,11 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, if (is_am654_pci_dev(pdev) && bar == BAR_0) goto ret; + if (bar_is_reserved(test, bar)) { + ret = -ENOBUFS; + goto ret; + } + if (cmd == PCITEST_BAR) ret = pci_endpoint_test_bar(test, bar); else From e022f0c72c7f67fe79de03e71d839418073490a5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 12 Mar 2026 14:02:38 +0100 Subject: [PATCH 060/165] selftests: pci_endpoint: Skip reserved BARs Running a test against a reserved BAR will result in the pci-epf-test driver returning -ENOBUFS. Make sure that the pci_endpoint_test selftest will return skip instead of failure or success for reserved BARs. Signed-off-by: Niklas Cassel Signed-off-by: Manivannan Sadhasivam Tested-by: Manikanta Maddireddy Tested-by: Koichiro Den Reviewed-by: Manikanta Maddireddy Reviewed-by: Frank Li Link: https://patch.msgid.link/20260312130229.2282001-22-cassel@kernel.org --- tools/testing/selftests/pci_endpoint/pci_endpoint_test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index e0dbbb2af8c7..c417fb3a198b 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -67,6 +67,8 @@ TEST_F(pci_ep_bar, BAR_TEST) pci_ep_ioctl(PCITEST_BAR, variant->barno); if (ret == -ENODATA) SKIP(return, "BAR is disabled"); + if (ret == -ENOBUFS) + SKIP(return, "BAR is reserved"); EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno); } @@ -84,6 +86,8 @@ TEST_F(pci_ep_bar, BAR_SUBRANGE_TEST) SKIP(return, "BAR is test register space"); if (ret == -EOPNOTSUPP) SKIP(return, "Subrange map is not supported"); + if (ret == -ENOBUFS) + SKIP(return, "BAR is reserved"); EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno); } From a1ed752bc7cb77b740cee671567d9508ae74becd Mon Sep 17 00:00:00 2001 From: Sizhe Liu Date: Wed, 11 Mar 2026 14:52:11 -0500 Subject: [PATCH 061/165] PCI/DPC: Hold pci_dev reference during error recovery The AER and EDR error handling paths hold a reference on the pci_dev during recovery. Hold a reference during the DPC recovery path as well. Signed-off-by: Sizhe Liu [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas https://patch.msgid.link/20260214081130.1878424-1-liusizhe5@huawei.com --- drivers/pci/pcie/dpc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index fc18349614d7..f028bc795f19 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -372,11 +372,13 @@ static irqreturn_t dpc_handler(int irq, void *context) return IRQ_HANDLED; } + pci_dev_get(pdev); dpc_process_error(pdev); /* We configure DPC so it only triggers on ERR_FATAL */ pcie_do_recovery(pdev, pci_channel_io_frozen, dpc_reset_link); + pci_dev_put(pdev); return IRQ_HANDLED; } From 702c1d56c7177a0481abd2814bab9495f1150967 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Feb 2026 19:08:01 +0530 Subject: [PATCH 062/165] PCI/CXL: Hide SBR from reset_methods if masked by CXL Per CXL r3.1, sec 8.1.5.2, the Secondary Bus Reset (SBR) bit in the Bridge Control register of a CXL port has no effect unless the "Unmask SBR" bit in the Port Control Extensions Register is set. After b1956e2d0713 ("PCI/CXL: Fail bus reset if upstream CXL Port has SBR masked"), Linux checks the "Unmask SBR" bit in pci_reset_bus_function(). But when probe==true, it previously returned 0, incorrectly indicating that SBR is a viable reset method for the device. As a result, "bus" is listed in the device's "reset_method" attribute even though the hardware is incapable of performing it. If a user writes "bus" to "reset_method" or triggers a reset that falls back to SBR, the operation fails with "write error: Inappropriate ioctl for device". If the link is operating in CXL mode (pcie_is_cxl()), return -ENOTTY immediately unless "Unmask SBR" is set, regardless of the probe argument. This ensures that "bus" is not advertised in "reset_methods" when the hardware prevents it, improving clarity for users and aligning the sysfs capability report with actual hardware behavior. Signed-off-by: Vidya Sagar [bhelgaas: commit log, use pcie_is_cxl()] Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260225133801.30231-1-vidyas@nvidia.com --- drivers/pci/pci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0090b4034ec6..3536c5ab8118 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4914,12 +4914,8 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe) * If "dev" is below a CXL port that has SBR control masked, SBR * won't do anything, so return error. */ - if (bridge && cxl_sbr_masked(bridge)) { - if (probe) - return 0; - + if (bridge && pcie_is_cxl(bridge) && cxl_sbr_masked(bridge)) return -ENOTTY; - } rc = pci_dev_reset_iommu_prepare(dev); if (rc) { From c4cac4a15c6e7a6f9517a2ddc9dc8d7d0d1aa11c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 17 Mar 2026 14:33:25 +0100 Subject: [PATCH 063/165] PCI: pnv_php: Simplify with scoped for each OF child loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260317133322.266102-7-krzysztof.kozlowski@oss.qualcomm.com --- drivers/pci/hotplug/pnv_php.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 5c020831e318..ff92a5c301b8 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -215,24 +215,19 @@ static void pnv_php_reverse_nodes(struct device_node *parent) static int pnv_php_populate_changeset(struct of_changeset *ocs, struct device_node *dn) { - struct device_node *child; - int ret = 0; + int ret; - for_each_child_of_node(dn, child) { + for_each_child_of_node_scoped(dn, child) { ret = of_changeset_attach_node(ocs, child); - if (ret) { - of_node_put(child); - break; - } + if (ret) + return ret; ret = pnv_php_populate_changeset(ocs, child); - if (ret) { - of_node_put(child); - break; - } + if (ret) + return ret; } - return ret; + return 0; } static void *pnv_php_add_one_pdn(struct device_node *dn, void *data) From 79253d6fe1cc80938160be2625d270fe5a4252ee Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 17 Mar 2026 14:33:26 +0100 Subject: [PATCH 064/165] PCI: rpaphp: Simplify with scoped for each OF child loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Helgaas Reviewed-by: Jonathan Cameron Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260317133322.266102-8-krzysztof.kozlowski@oss.qualcomm.com --- drivers/pci/hotplug/rpaphp_slot.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 33ca19200c1b..67362e5b9971 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -82,7 +82,6 @@ EXPORT_SYMBOL_GPL(rpaphp_deregister_slot); int rpaphp_register_slot(struct slot *slot) { struct hotplug_slot *php_slot = &slot->hotplug_slot; - struct device_node *child; u32 my_index; int retval; int slotno = -1; @@ -97,11 +96,10 @@ int rpaphp_register_slot(struct slot *slot) return -EAGAIN; } - for_each_child_of_node(slot->dn, child) { + for_each_child_of_node_scoped(slot->dn, child) { retval = of_property_read_u32(child, "ibm,my-drc-index", &my_index); if (my_index == slot->index) { slotno = PCI_SLOT(PCI_DN(child)->devfn); - of_node_put(child); break; } } From ff124bbbca1d3a07fa1392ffdbbdeece71f68ece Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 18 Mar 2026 12:10:10 -0500 Subject: [PATCH 065/165] PCI/pwrctrl: generic: Rename pci-pwrctrl-slot as generic The driver is pretty generic and would fit for either PCI Slots or endpoints connected to PCI ports, so rename the driver and module as pci-pwrctrl-generic. Suggested-by: Manivannan Sadhasivam Signed-off-by: Neil Armstrong Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260220-topic-sm8650-ayaneo-pocket-s2-base-v5-3-1ad79caa1efa@linaro.org --- drivers/pci/controller/dwc/Kconfig | 4 ++-- drivers/pci/pwrctrl/Kconfig | 13 +++++++------ drivers/pci/pwrctrl/Makefile | 4 ++-- drivers/pci/pwrctrl/{slot.c => generic.c} | 0 4 files changed, 11 insertions(+), 10 deletions(-) rename drivers/pci/pwrctrl/{slot.c => generic.c} (100%) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d0aa031397fa..4bd36e133ca6 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -309,7 +309,7 @@ config PCIE_QCOM select CRC8 select PCIE_QCOM_COMMON select PCI_HOST_COMMON - select PCI_PWRCTRL_SLOT + select PCI_PWRCTRL_GENERIC help Say Y here to enable PCIe controller support on Qualcomm SoCs. The PCIe controller uses the DesignWare core plus Qualcomm-specific @@ -431,7 +431,7 @@ config PCIE_SPACEMIT_K1 depends on ARCH_SPACEMIT || COMPILE_TEST depends on HAS_IOMEM select PCIE_DW_HOST - select PCI_PWRCTRL_SLOT + select PCI_PWRCTRL_GENERIC default ARCH_SPACEMIT help Enables support for the DesignWare based PCIe controller in diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig index cd3aa15bad00..9eec767cda86 100644 --- a/drivers/pci/pwrctrl/Kconfig +++ b/drivers/pci/pwrctrl/Kconfig @@ -11,17 +11,18 @@ config PCI_PWRCTRL_PWRSEQ select POWER_SEQUENCING select PCI_PWRCTRL -config PCI_PWRCTRL_SLOT - tristate "PCI Power Control driver for PCI slots" +config PCI_PWRCTRL_GENERIC + tristate "Generic PCI Power Control driver for PCI slots and endpoints" select POWER_SEQUENCING select PCI_PWRCTRL help - Say Y here to enable the PCI Power Control driver to control the power - state of PCI slots. + Say Y here to enable the generic PCI Power Control driver to control + the power state of PCI slots and endpoints. This is a generic driver that controls the power state of different - PCI slots. The voltage regulators powering the rails of the PCI slots - are expected to be defined in the devicetree node of the PCI bridge. + PCI slots and endpoints. The voltage regulators powering the rails + of the PCI slots or endpoints are expected to be defined in the + devicetree node of the PCI bridge or endpoint. config PCI_PWRCTRL_TC9563 tristate "PCI Power Control driver for TC9563 PCIe switch" diff --git a/drivers/pci/pwrctrl/Makefile b/drivers/pci/pwrctrl/Makefile index 13b02282106c..f6bb4fb9a410 100644 --- a/drivers/pci/pwrctrl/Makefile +++ b/drivers/pci/pwrctrl/Makefile @@ -5,7 +5,7 @@ pci-pwrctrl-core-y := core.o obj-$(CONFIG_PCI_PWRCTRL_PWRSEQ) += pci-pwrctrl-pwrseq.o -obj-$(CONFIG_PCI_PWRCTRL_SLOT) += pci-pwrctrl-slot.o -pci-pwrctrl-slot-y := slot.o +obj-$(CONFIG_PCI_PWRCTRL_GENERIC) += pci-pwrctrl-generic.o +pci-pwrctrl-generic-y := generic.o obj-$(CONFIG_PCI_PWRCTRL_TC9563) += pci-pwrctrl-tc9563.o diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/generic.c similarity index 100% rename from drivers/pci/pwrctrl/slot.c rename to drivers/pci/pwrctrl/generic.c From 0862e2b06cd88491e2e854a24219219b8aa6e0b5 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 20 Feb 2026 16:04:41 +0100 Subject: [PATCH 066/165] PCI/pwrctrl: generic: Simplify dev_err_probe() usage dev_err_probe() returns the error code passed to it, so this: dev_err_probe(dev, ret, "Failed to get slot regulators\n"); return ret; is equivalent to this: return dev_err_probe(dev, ret, "Failed to get slot regulators\n"); Simplify by using the latter. Suggested-by: Bartosz Golaszewski Signed-off-by: Neil Armstrong Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260220-topic-sm8650-ayaneo-pocket-s2-base-v5-2-1ad79caa1efa@linaro.org --- drivers/pci/pwrctrl/generic.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pwrctrl/generic.c b/drivers/pci/pwrctrl/generic.c index 082af81efe25..f03debabbc73 100644 --- a/drivers/pci/pwrctrl/generic.c +++ b/drivers/pci/pwrctrl/generic.c @@ -88,18 +88,15 @@ static int slot_pwrctrl_probe(struct platform_device *pdev) ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &slot->supplies); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to get slot regulators\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get slot regulators\n"); slot->num_supplies = ret; slot->clk = devm_clk_get_optional(dev, NULL); - if (IS_ERR(slot->clk)) { + if (IS_ERR(slot->clk)) return dev_err_probe(dev, PTR_ERR(slot->clk), "Failed to enable slot clock\n"); - } skip_resources: slot->pwrctrl.power_on = slot_pwrctrl_power_on; From bd3622e5b8e0650669332d9e10244a704560cbfe Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 20 Feb 2026 16:04:43 +0100 Subject: [PATCH 067/165] PCI/pwrctrl: generic: Add UPD720201/UPD720202 USB 3.0 xHCI Host Controller support Enable the generic pwrctrl driver to control power of PCIe UPD720201/UPD720202 USB 3.0 xHCI Host Controller. Signed-off-by: Neil Armstrong Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260220-topic-sm8650-ayaneo-pocket-s2-base-v5-4-1ad79caa1efa@linaro.org --- drivers/pci/pwrctrl/generic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/pwrctrl/generic.c b/drivers/pci/pwrctrl/generic.c index f03debabbc73..fcedd435f723 100644 --- a/drivers/pci/pwrctrl/generic.c +++ b/drivers/pci/pwrctrl/generic.c @@ -119,6 +119,10 @@ static const struct of_device_id slot_pwrctrl_of_match[] = { { .compatible = "pciclass,0604", }, + /* Renesas UPD720201/UPD720202 USB 3.0 xHCI Host Controller */ + { + .compatible = "pci1912,0014", + }, { } }; MODULE_DEVICE_TABLE(of, slot_pwrctrl_of_match); From 249c48a55856245d5f6dcfd53e1e15fb5d0c467b Mon Sep 17 00:00:00 2001 From: Senchuan Zhang Date: Fri, 27 Feb 2026 19:17:32 +0800 Subject: [PATCH 068/165] dt-bindings: PCI: eswin: Add ESWIN PCIe Root Complex Add Device Tree binding documentation for the ESWIN PCIe Root Complex. The Root Complex is based on Synopsys Designware PCIe IP. Signed-off-by: Yu Ning Signed-off-by: Yanghui Ou Signed-off-by: Senchuan Zhang [mani: Renamed 'EIC7700' to 'ESWIN'] Signed-off-by: Manivannan Sadhasivam [bhelgaas: add driver tag in subject] Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260227111732.1979-1-zhangsenchuan@eswincomputing.com --- .../devicetree/bindings/pci/eswin,pcie.yaml | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/eswin,pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/eswin,pcie.yaml b/Documentation/devicetree/bindings/pci/eswin,pcie.yaml new file mode 100644 index 000000000000..057e1f363dde --- /dev/null +++ b/Documentation/devicetree/bindings/pci/eswin,pcie.yaml @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/eswin,pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN PCIe Root Complex + +maintainers: + - Yu Ning + - Senchuan Zhang + - Yanghui Ou + +description: + ESWIN SoCs PCIe Root Complex is based on the Synopsys DesignWare PCIe IP. + +properties: + compatible: + const: eswin,eic7700-pcie + + reg: + maxItems: 3 + + reg-names: + items: + - const: dbi + - const: config + - const: elbi + + ranges: + maxItems: 3 + + '#interrupt-cells': + const: 1 + + interrupt-names: + items: + - const: msi + - const: inta + - const: intb + - const: intc + - const: intd + + interrupt-map: + maxItems: 4 + + interrupt-map-mask: + items: + - const: 0 + - const: 0 + - const: 0 + - const: 7 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: mstr + - const: dbi + - const: phy_reg + - const: aux + + resets: + maxItems: 2 + + reset-names: + items: + - const: dbi + - const: pwr + +patternProperties: + "^pcie@": + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + num-lanes: + maximum: 4 + + resets: + maxItems: 1 + + reset-names: + items: + - const: perst + + required: + - reg + - ranges + - num-lanes + - resets + - reset-names + + unevaluatedProperties: false + +required: + - compatible + - reg + - ranges + - interrupts + - interrupt-names + - interrupt-map-mask + - interrupt-map + - '#interrupt-cells' + - clocks + - clock-names + - resets + - reset-names + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@54000000 { + compatible = "eswin,eic7700-pcie"; + reg = <0x0 0x54000000 0x0 0x4000000>, + <0x0 0x40000000 0x0 0x800000>, + <0x0 0x50000000 0x0 0x100000>; + reg-names = "dbi", "config", "elbi"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges = <0x01000000 0x0 0x40800000 0x0 0x40800000 0x0 0x800000>, + <0x02000000 0x0 0x41000000 0x0 0x41000000 0x0 0xf000000>, + <0x43000000 0x80 0x00000000 0x80 0x00000000 0x2 0x00000000>; + bus-range = <0x00 0xff>; + clocks = <&clock 144>, + <&clock 145>, + <&clock 146>, + <&clock 147>; + clock-names = "mstr", "dbi", "phy_reg", "aux"; + resets = <&reset 97>, + <&reset 98>; + reset-names = "dbi", "pwr"; + interrupts = <220>, <179>, <180>, <181>, <182>, <183>, <184>, <185>, <186>; + interrupt-names = "msi", "inta", "intb", "intc", "intd"; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &plic 179>, + <0x0 0x0 0x0 0x2 &plic 180>, + <0x0 0x0 0x0 0x3 &plic 181>, + <0x0 0x0 0x0 0x4 &plic 182>; + device_type = "pci"; + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + device_type = "pci"; + num-lanes = <4>; + resets = <&reset 99>; + reset-names = "perst"; + }; + }; + }; From b593c26d081a4fbeabd34badb0a9e9a971a79cb4 Mon Sep 17 00:00:00 2001 From: Senchuan Zhang Date: Fri, 27 Feb 2026 19:18:08 +0800 Subject: [PATCH 069/165] PCI: eswin: Add ESWIN PCIe Root Complex driver Add driver for the ESWIN PCIe Root Complex based on the DesignWare PCIe core, IP revision 5.96a. The PCIe Gen.3 Root Complex supports data rate of 8 GT/s and x4 lanes, with INTx and MSI interrupt capability. Signed-off-by: Yu Ning Signed-off-by: Yanghui Ou Signed-off-by: Senchuan Zhang [mani: renamed "EIC7700" to "ESWIN", added maintainers entry, removed async probe] Signed-off-by: Manivannan Sadhasivam [bhelgaas: add driver tag in subject] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260227111808.1996-1-zhangsenchuan@eswincomputing.com --- MAINTAINERS | 7 + drivers/pci/controller/dwc/Kconfig | 10 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-eswin.c | 408 ++++++++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-eswin.c diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..eef3a86d734f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20494,6 +20494,13 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Odd Fixes F: drivers/pci/controller/pci-thunder-* +PCIE DRIVER FOR ESWIN +M: Senchuan Zhang +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/eswin,pcie.yaml +F: drivers/pci/controller/dwc/pcie-eswin.c + PCIE DRIVER FOR HISILICON M: Zhou Wang L: linux-pci@vger.kernel.org diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d0aa031397fa..1b7ecd555e86 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -93,6 +93,16 @@ config PCIE_BT1 Enables support for the PCIe controller in the Baikal-T1 SoC to work in host mode. It's based on the Synopsys DWC PCIe v4.60a IP-core. +config PCIE_ESWIN + tristate "ESWIN PCIe controller" + depends on ARCH_ESWIN || COMPILE_TEST + depends on PCI_MSI + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support for the ESWIN SoCs. + The PCIe controller in ESWIN SoCs is based on DesignWare hardware, and + works only in host mode. + config PCI_IMX6 bool diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 67ba59c02038..83558e0f2a61 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o obj-$(CONFIG_PCIE_AMD_MDB) += pcie-amd-mdb.o obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o +obj-$(CONFIG_PCIE_ESWIN) += pcie-eswin.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c new file mode 100644 index 000000000000..2845832b3824 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-eswin.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESWIN PCIe Root Complex driver + * + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. + * + * Authors: Yu Ning + * Senchuan Zhang + * Yanghui Ou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* ELBI registers */ +#define PCIEELBI_CTRL0_OFFSET 0x0 +#define PCIEELBI_STATUS0_OFFSET 0x100 + +/* LTSSM register fields */ +#define PCIEELBI_APP_LTSSM_ENABLE BIT(5) + +/* APP_HOLD_PHY_RST register fields */ +#define PCIEELBI_APP_HOLD_PHY_RST BIT(6) + +/* PM_SEL_AUX_CLK register fields */ +#define PCIEELBI_PM_SEL_AUX_CLK BIT(16) + +/* DEV_TYPE register fields */ +#define PCIEELBI_CTRL0_DEV_TYPE GENMASK(3, 0) + +/* Vendor and device ID value */ +#define PCI_VENDOR_ID_ESWIN 0x1fe1 +#define PCI_DEVICE_ID_ESWIN_EIC7700 0x2030 + +#define ESWIN_NUM_RSTS ARRAY_SIZE(eswin_pcie_rsts) + +static const char * const eswin_pcie_rsts[] = { + "pwr", + "dbi", +}; + +struct eswin_pcie_data { + bool skip_l23; +}; + +struct eswin_pcie_port { + struct list_head list; + struct reset_control *perst; + int num_lanes; +}; + +struct eswin_pcie { + struct dw_pcie pci; + struct clk_bulk_data *clks; + struct reset_control_bulk_data resets[ESWIN_NUM_RSTS]; + struct list_head ports; + const struct eswin_pcie_data *data; + int num_clks; +}; + +#define to_eswin_pcie(x) dev_get_drvdata((x)->dev) + +static int eswin_pcie_start_link(struct dw_pcie *pci) +{ + u32 val; + + /* Enable LTSSM */ + val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + val |= PCIEELBI_APP_LTSSM_ENABLE; + writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + + return 0; +} + +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); + + return val & PCI_EXP_LNKSTA_DLLLA; +} + +static int eswin_pcie_perst_reset(struct eswin_pcie_port *port, + struct eswin_pcie *pcie) +{ + int ret; + + ret = reset_control_assert(port->perst); + if (ret) { + dev_err(pcie->pci.dev, "Failed to assert PERST#\n"); + return ret; + } + + /* Ensure that PERST# has been asserted for at least 100 ms */ + msleep(PCIE_T_PVPERL_MS); + + ret = reset_control_deassert(port->perst); + if (ret) { + dev_err(pcie->pci.dev, "Failed to deassert PERST#\n"); + return ret; + } + + return 0; +} + +static void eswin_pcie_assert(struct eswin_pcie *pcie) +{ + struct eswin_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + reset_control_assert(port->perst); + reset_control_bulk_assert(ESWIN_NUM_RSTS, pcie->resets); +} + +static int eswin_pcie_parse_port(struct eswin_pcie *pcie, + struct device_node *node) +{ + struct device *dev = pcie->pci.dev; + struct eswin_pcie_port *port; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->perst = of_reset_control_get_exclusive(node, "perst"); + if (IS_ERR(port->perst)) { + dev_err(dev, "Failed to get PERST# reset\n"); + return PTR_ERR(port->perst); + } + + /* + * TODO: Since the Root Port node is separated out by pcie devicetree, + * the DWC core initialization code can't parse the num-lanes attribute + * in the Root Port. Before entering the DWC core initialization code, + * the platform driver code parses the Root Port node. The ESWIN only + * supports one Root Port node, and the num-lanes attribute is suitable + * for the case of one Root Port. + */ + if (!of_property_read_u32(node, "num-lanes", &port->num_lanes)) + pcie->pci.num_lanes = port->num_lanes; + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + return 0; +} + +static int eswin_pcie_parse_ports(struct eswin_pcie *pcie) +{ + struct eswin_pcie_port *port, *tmp; + struct device *dev = pcie->pci.dev; + int ret; + + for_each_available_child_of_node_scoped(dev->of_node, of_port) { + ret = eswin_pcie_parse_port(pcie, of_port); + if (ret) + goto err_port; + } + + return 0; + +err_port: + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + reset_control_put(port->perst); + list_del(&port->list); + } + + return ret; +} + +static int eswin_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct eswin_pcie *pcie = to_eswin_pcie(pci); + struct eswin_pcie_port *port, *tmp; + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) + return ret; + + /* + * The PWR and DBI reset signals are respectively used to reset the + * PCIe controller and the DBI register. + * + * The PERST# signal is a reset signal that simultaneously controls the + * PCIe controller, PHY, and Endpoint. Before configuring the PHY, the + * PERST# signal must first be deasserted. + * + * The external reference clock is supplied simultaneously to the PHY + * and EP. When the PHY is configurable, the entire chip already has + * stable power and reference clock. The PHY will be ready within 20ms + * after writing app_hold_phy_rst register bit of ELBI register space. + */ + ret = reset_control_bulk_deassert(ESWIN_NUM_RSTS, pcie->resets); + if (ret) { + dev_err(pcie->pci.dev, "Failed to deassert resets\n"); + goto err_deassert; + } + + /* Configure Root Port type */ + val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + val &= ~PCIEELBI_CTRL0_DEV_TYPE; + val |= FIELD_PREP(PCIEELBI_CTRL0_DEV_TYPE, PCI_EXP_TYPE_ROOT_PORT); + writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + + list_for_each_entry(port, &pcie->ports, list) { + ret = eswin_pcie_perst_reset(port, pcie); + if (ret) + goto err_perst; + } + + /* Configure app_hold_phy_rst */ + val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + val &= ~PCIEELBI_APP_HOLD_PHY_RST; + writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET); + + /* The maximum waiting time for the clock switch lock is 20ms */ + ret = readl_poll_timeout(pci->elbi_base + PCIEELBI_STATUS0_OFFSET, val, + !(val & PCIEELBI_PM_SEL_AUX_CLK), 1000, + 20000); + if (ret) { + dev_err(pci->dev, "Timeout waiting for PM_SEL_AUX_CLK ready\n"); + goto err_phy_init; + } + + /* + * Configure ESWIN VID:DID for Root Port as the default values are + * invalid. + */ + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, PCI_VENDOR_ID_ESWIN); + dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, PCI_DEVICE_ID_ESWIN_EIC7700); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; + +err_phy_init: + list_for_each_entry(port, &pcie->ports, list) + reset_control_assert(port->perst); +err_perst: + reset_control_bulk_assert(ESWIN_NUM_RSTS, pcie->resets); +err_deassert: + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + reset_control_put(port->perst); + list_del(&port->list); + } + + return ret; +} + +static void eswin_pcie_host_deinit(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct eswin_pcie *pcie = to_eswin_pcie(pci); + + eswin_pcie_assert(pcie); + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); +} + +static void eswin_pcie_pme_turn_off(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct eswin_pcie *pcie = to_eswin_pcie(pci); + + /* + * The ESWIN EIC7700 SoC lacks hardware support for the L2/L3 low-power + * link states. It cannot enter the L2/L3 Ready state through the + * PME_Turn_Off/PME_To_Ack handshake protocol. To avoid this problem, + * the skip_l23_ready has been set. + */ + pp->skip_l23_ready = pcie->data->skip_l23; +} + +static const struct dw_pcie_host_ops eswin_pcie_host_ops = { + .init = eswin_pcie_host_init, + .deinit = eswin_pcie_host_deinit, + .pme_turn_off = eswin_pcie_pme_turn_off, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = eswin_pcie_start_link, + .link_up = eswin_pcie_link_up, +}; + +static int eswin_pcie_probe(struct platform_device *pdev) +{ + const struct eswin_pcie_data *data; + struct eswin_pcie_port *port, *tmp; + struct device *dev = &pdev->dev; + struct eswin_pcie *pcie; + struct dw_pcie *pci; + int ret, i; + + data = of_device_get_match_data(dev); + if (!data) + return dev_err_probe(dev, -ENODATA, "No platform data\n"); + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + INIT_LIST_HEAD(&pcie->ports); + + pci = &pcie->pci; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + pci->pp.ops = &eswin_pcie_host_ops; + pcie->data = data; + + pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); + if (pcie->num_clks < 0) + return dev_err_probe(dev, pcie->num_clks, + "Failed to get pcie clocks\n"); + + for (i = 0; i < ESWIN_NUM_RSTS; i++) + pcie->resets[i].id = eswin_pcie_rsts[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, ESWIN_NUM_RSTS, + pcie->resets); + if (ret) + return dev_err_probe(dev, ret, "Failed to get resets\n"); + + ret = eswin_pcie_parse_ports(pcie); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse Root Port\n"); + + platform_set_drvdata(pdev, pcie); + + pm_runtime_no_callbacks(dev); + devm_pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_pm_runtime_put; + + ret = dw_pcie_host_init(&pci->pp); + if (ret) { + dev_err(dev, "Failed to init host\n"); + goto err_init; + } + + return 0; + +err_pm_runtime_put: + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + reset_control_put(port->perst); + list_del(&port->list); + } +err_init: + pm_runtime_put(dev); + + return ret; +} + +static int eswin_pcie_suspend_noirq(struct device *dev) +{ + struct eswin_pcie *pcie = dev_get_drvdata(dev); + + return dw_pcie_suspend_noirq(&pcie->pci); +} + +static int eswin_pcie_resume_noirq(struct device *dev) +{ + struct eswin_pcie *pcie = dev_get_drvdata(dev); + + return dw_pcie_resume_noirq(&pcie->pci); +} + +static DEFINE_NOIRQ_DEV_PM_OPS(eswin_pcie_pm, eswin_pcie_suspend_noirq, + eswin_pcie_resume_noirq); + +static const struct eswin_pcie_data eswin_eic7700_data = { + .skip_l23 = true, +}; + +static const struct of_device_id eswin_pcie_of_match[] = { + { .compatible = "eswin,eic7700-pcie", .data = &eswin_eic7700_data }, + {} +}; + +static struct platform_driver eswin_pcie_driver = { + .probe = eswin_pcie_probe, + .driver = { + .name = "eswin-pcie", + .of_match_table = eswin_pcie_of_match, + .suppress_bind_attrs = true, + .pm = &eswin_pcie_pm, + }, +}; +builtin_platform_driver(eswin_pcie_driver); + +MODULE_DESCRIPTION("ESWIN PCIe Root Complex driver"); +MODULE_AUTHOR("Yu Ning "); +MODULE_AUTHOR("Senchuan Zhang "); +MODULE_AUTHOR("Yanghui Ou "); +MODULE_LICENSE("GPL"); From ab4a4043db1fcc4fd4c5745c5be8caf053502e29 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 2 Mar 2026 17:46:48 -0800 Subject: [PATCH 070/165] PCI: mediatek: Fix possible truncation in mtk_pcie_parse_port() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by the W=1 warning below, content of the 'name' variable might get truncated with the existing size of 10 bytes. Though it is not practically possible to exceed the 10 bytes size, increase it to 20 to silence the warning for a clean W=1 build: $ make W=1 drivers/pci/controller/pcie-mediatek.o CALL scripts/checksyscalls.sh DESCEND objtool INSTALL libsubcmd_headers CC drivers/pci/controller/pcie-mediatek.o drivers/pci/controller/pcie-mediatek.c: In function ‘mtk_pcie_parse_port’: drivers/pci/controller/pcie-mediatek.c:963:43: error: ‘%d’ directive output may be truncated writing between 1 and 10 bytes into a region of size 6 [-Werror=format-truncation=] 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~ drivers/pci/controller/pcie-mediatek.c:963:38: note: directive argument in the range [0, 2147483647] 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~~~~~~~ drivers/pci/controller/pcie-mediatek.c:963:9: note: ‘snprintf’ output between 6 and 15 bytes into a destination of size 10 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ryder Lee [mani: commit log] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/b835e360b42c5e0994f9301a34dbdf140a8d3ef5.1772493898.git.ryder.lee@mediatek.com --- drivers/pci/controller/pcie-mediatek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 5defa5cc4c2b..75722524fe74 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -953,7 +953,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); - char name[10]; + char name[20]; int err; port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); From f761e0deb4d9ec9d485b9d353aba1a344bc57689 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 11 Feb 2026 01:03:15 +0900 Subject: [PATCH 071/165] PCI: dwc: rcar-gen4: Mark BAR0 and BAR2 as Resizable BARs in endpoint mode R-Car Gen4 (S4) endpoint controller implements the PCIe Resizable BAR capability for BAR0 and BAR2. Advertise them as BAR_RESIZABLE so that EPF requested BAR sizes are reflected to the host. Signed-off-by: Koichiro Den [commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260210160315.2272930-1-den@valinux.co.jp --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index a6912e85e4dd..3d4a889e38cc 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -422,7 +422,9 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features rcar_gen4_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .msi_capable = true, + .bar[BAR_0] = { .type = BAR_RESIZABLE, }, .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .type = BAR_RESIZABLE, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256 }, .bar[BAR_5] = { .type = BAR_RESERVED, }, From 13f55a7ca773c731a1e645934c1ae48577f48785 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 6 Mar 2026 00:10:50 +0900 Subject: [PATCH 072/165] PCI: dwc: rcar-gen4: Change EPC BAR alignment to 4K as per the documentation R-Car S4 Series (R8A779F[4-7]*) EP controller uses a 4K minimum iATU region size (CX_ATU_MIN_REGION_SIZE = 4K) as per R19UH0161EJ0130 Rev.1.30. Also, the controller itself can only be configured in the range 4 KB to 64 KB, so the current 1 MB alignment requirement is incorrect. Hence, change the alignment to the min size 4K as per the documentation. This also fixes needless unusability of BAR4 on this platform when the target address is fixed, such as for doorbell targets. Fixes: e311b3834dfa ("PCI: rcar-gen4: Add endpoint mode support") Signed-off-by: Koichiro Den [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260305151050.1834007-1-den@valinux.co.jp --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 3d4a889e38cc..396ef9432299 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -428,7 +428,7 @@ static const struct pci_epc_features rcar_gen4_pcie_epc_features = { .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256 }, .bar[BAR_5] = { .type = BAR_RESERVED, }, - .align = SZ_1M, + .align = SZ_4K, }; static const struct pci_epc_features* From 94c62cf0698c0edbf1f118dd80ea19d0ff5d834e Mon Sep 17 00:00:00 2001 From: Gary Yang Date: Fri, 13 Mar 2026 19:49:13 +0800 Subject: [PATCH 073/165] dt-bindings: PCI: cix,sky1-pcie-host: Add power-domains The Sky1 PCIe controller resides in a dedicated power domain managed via SCMI. Add the power-domains property to the binding to allow describing this dependency. Signed-off-by: Gary Yang Signed-off-by: Manivannan Sadhasivam Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260313114914.1564115-2-gary.yang@cixtech.com --- Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml index b910a42e0843..d55d165f1e94 100644 --- a/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml @@ -38,6 +38,9 @@ properties: ranges: maxItems: 3 + power-domains: + maxItems: 1 + required: - compatible - ranges From 72e76b63d6ff6d1f96acccbfc6c118656f63e66a Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 24 Mar 2026 00:41:28 +0800 Subject: [PATCH 074/165] PCI: sky1: Fix missing cleanup of ECAM config on probe failure When devm_kzalloc() for reg_off fails, the code returns -ENOMEM without freeing pcie->cfg, which was allocated earlier by pci_ecam_create(). Add the missing pci_ecam_free() call to properly release the allocated ECAM configuration window on this error path. Fixes: a0d9f2c08f45 ("PCI: sky1: Add PCIe host support for CIX Sky1") Signed-off-by: Felix Gu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260324-sky1-v1-1-6a00cb2776b6@gmail.com --- drivers/pci/controller/cadence/pci-sky1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pci-sky1.c b/drivers/pci/controller/cadence/pci-sky1.c index d8c216dc120d..9853a9c82c0e 100644 --- a/drivers/pci/controller/cadence/pci-sky1.c +++ b/drivers/pci/controller/cadence/pci-sky1.c @@ -176,8 +176,10 @@ static int sky1_pcie_probe(struct platform_device *pdev) cdns_pcie->is_rc = 1; reg_off = devm_kzalloc(dev, sizeof(*reg_off), GFP_KERNEL); - if (!reg_off) + if (!reg_off) { + pci_ecam_free(pcie->cfg); return -ENOMEM; + } reg_off->ip_reg_bank_offset = SKY1_IP_REG_BANK; reg_off->ip_cfg_ctrl_reg_offset = SKY1_IP_CFG_CTRL_REG_BANK; From 2a93c9851b2bb38614fadd84aa674b7a5c8181c6 Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:34 +0900 Subject: [PATCH 075/165] PCI/VGA: Pass vga_get_uninterruptible() errors to userspace If VGA routing cannot be established, vga_get_uninterruptible() returns an error and does not increment the lock count. Return the error to the caller. Return before incrementing uc->io_cnt/mem_cnt so vga_arb_release() won't call vga_put() when userspace closes the handle. Signed-off-by: Simon Richter [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-2-Simon.Richter@hogyros.de --- drivers/pci/vgaarb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c index d9383bf541e7..22b2b6ebdefd 100644 --- a/drivers/pci/vgaarb.c +++ b/drivers/pci/vgaarb.c @@ -1134,6 +1134,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user *buf, char kbuf[64], *curr_pos; size_t remaining = count; + int err; int ret_val; int i; @@ -1165,7 +1166,11 @@ static ssize_t vga_arb_write(struct file *file, const char __user *buf, goto done; } - vga_get_uninterruptible(pdev, io_state); + err = vga_get_uninterruptible(pdev, io_state); + if (err) { + ret_val = err; + goto done; + } /* Update the client's locks lists */ for (i = 0; i < MAX_USER_CARDS; i++) { From 94555ea9a0488c5c1bba90242cfa149a84a8928d Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:35 +0900 Subject: [PATCH 076/165] PCI/VGA: Pass errors from pci_set_vga_state() up pci_set_vga_state() returns an error code, which so far has been ignored by the only caller, __vga_tryget(), so forward it to the caller. As the return type of __vga_tryget() is a pointer, wrap the error in ERR_PTR(). Signed-off-by: Simon Richter Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-3-Simon.Richter@hogyros.de --- drivers/pci/vgaarb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c index 22b2b6ebdefd..c360eee11dd9 100644 --- a/drivers/pci/vgaarb.c +++ b/drivers/pci/vgaarb.c @@ -215,6 +215,7 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, struct vga_device *conflict; unsigned int pci_bits; u32 flags = 0; + int err; /* * Account for "normal" resources to lock. If we decode the legacy, @@ -307,7 +308,9 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, if (change_bridge) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; - pci_set_vga_state(conflict->pdev, false, pci_bits, flags); + err = pci_set_vga_state(conflict->pdev, false, pci_bits, flags); + if (err) + return ERR_PTR(err); conflict->owns &= ~match; /* If we disabled normal decoding, reflect it in owns */ @@ -337,7 +340,9 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, if (wants & VGA_RSRC_LEGACY_MASK) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; - pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); + err = pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); + if (err) + return ERR_PTR(err); vgadev->owns |= wants; lock_them: @@ -455,6 +460,10 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) } conflict = __vga_tryget(vgadev, rsrc); spin_unlock_irqrestore(&vga_lock, flags); + if (IS_ERR(conflict)) { + rc = PTR_ERR(conflict); + break; + } if (conflict == NULL) break; From 98752193de5b3306b6815cba5a217337c2a93876 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:53 +0800 Subject: [PATCH 077/165] PCI: mediatek-gen3: Clean up mtk_pcie_parse_port() with dev_err_probe() mtk_pcie_parse_port() in the pcie-mediatek-gen driver has a bunch of if (err) { dev_err(dev, "error message\n"); return err; # or goto } patterns. Simplify these with dev_err_probe(). The system also gains proper deferred probe messages that can be seen in: /sys/kernel/debug/devices_deferred Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260324052002.4072430-2-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 36 ++++++--------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 75ddb8bee168..1939cac995b5 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -876,10 +876,8 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) if (!regs) return -EINVAL; pcie->base = devm_ioremap_resource(dev, regs); - if (IS_ERR(pcie->base)) { - dev_err(dev, "failed to map register base\n"); - return PTR_ERR(pcie->base); - } + if (IS_ERR(pcie->base)) + return dev_err_probe(dev, PTR_ERR(pcie->base), "failed to map register base\n"); pcie->reg_base = regs->start; @@ -888,34 +886,20 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) ret = devm_reset_control_bulk_get_optional_shared(dev, num_resets, pcie->phy_resets); - if (ret) { - dev_err(dev, "failed to get PHY bulk reset\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to get PHY bulk reset\n"); pcie->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac"); - if (IS_ERR(pcie->mac_reset)) { - ret = PTR_ERR(pcie->mac_reset); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get MAC reset\n"); - - return ret; - } + if (IS_ERR(pcie->mac_reset)) + return dev_err_probe(dev, PTR_ERR(pcie->mac_reset), "failed to get MAC reset\n"); pcie->phy = devm_phy_optional_get(dev, "pcie-phy"); - if (IS_ERR(pcie->phy)) { - ret = PTR_ERR(pcie->phy); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get PHY\n"); - - return ret; - } + if (IS_ERR(pcie->phy)) + return dev_err_probe(dev, PTR_ERR(pcie->phy), "failed to get PHY\n"); pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); - if (pcie->num_clks < 0) { - dev_err(dev, "failed to get clocks\n"); - return pcie->num_clks; - } + if (pcie->num_clks < 0) + return dev_err_probe(dev, pcie->num_clks, "failed to get clocks\n"); ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes); if (ret == 0) { From eddbac092e5f1edcf502056348d98acb811dbb31 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:54 +0800 Subject: [PATCH 078/165] PCI: mediatek-gen3: Move mtk_pcie_setup_irq() out of mtk_pcie_setup() mtk_pcie_setup_irq() sets up the IRQ domains for PCI INTx and MSI, and chains them to the controller's interrupt. It doesn't touch the PCIe controller itself. Move mtk_pcie_setup_irq() out of mtk_pcie_setup(), do it earlier so there's nothing to clean up if it fails, and add an error message if it does fail. Reorder mtk_pcie_irq_teardown() in the remove callback to follow. Also create an error path in the probe function. Suggested-by: Bjorn Helgaas Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260324052002.4072430-3-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 1939cac995b5..04ae195d36c2 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1152,10 +1152,6 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) if (err) goto err_setup; - err = mtk_pcie_setup_irq(pcie); - if (err) - goto err_setup; - return 0; err_setup: @@ -1181,21 +1177,28 @@ static int mtk_pcie_probe(struct platform_device *pdev) pcie->soc = device_get_match_data(dev); platform_set_drvdata(pdev, pcie); + err = mtk_pcie_setup_irq(pcie); + if (err) + return dev_err_probe(dev, err, "Failed to setup IRQ domains\n"); + err = mtk_pcie_setup(pcie); if (err) - return err; + goto err_tear_down_irq; host->ops = &mtk_pcie_ops; host->sysdata = pcie; err = pci_host_probe(host); - if (err) { - mtk_pcie_irq_teardown(pcie); - mtk_pcie_power_down(pcie); - return err; - } + if (err) + goto err_power_down_pcie; return 0; + +err_power_down_pcie: + mtk_pcie_power_down(pcie); +err_tear_down_irq: + mtk_pcie_irq_teardown(pcie); + return err; } static void mtk_pcie_remove(struct platform_device *pdev) @@ -1208,8 +1211,8 @@ static void mtk_pcie_remove(struct platform_device *pdev) pci_remove_root_bus(host->bus); pci_unlock_rescan_remove(); - mtk_pcie_irq_teardown(pcie); mtk_pcie_power_down(pcie); + mtk_pcie_irq_teardown(pcie); } static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie) From cf417e61fc860c496eff37b87e06e8e107d343b5 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:55 +0800 Subject: [PATCH 079/165] PCI: mediatek-gen3: Move controller setup steps before PERST# control Setting up the translation windows and enabling MSI involve only the controller, not the link or any downstream device. These can be done before the device is enabled. Move these steps before the existing PERST# control and waiting for the link to come up. This provides a cleaner separation of controller vs device setup. This also allows the later commits that split out PERST# control and add device power control to have cleaner teardown. This change only moves code. No functional change is expected. Suggested-by: Bjorn Helgaas Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260324052002.4072430-4-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 50 ++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 04ae195d36c2..1b6290f2c360 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -464,6 +464,31 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) val |= PCIE_DISABLE_DVFSRC_VLT_REQ; writel_relaxed(val, pcie->base + PCIE_MISC_CTRL_REG); + mtk_pcie_enable_msi(pcie); + + /* Set PCIe translation windows */ + resource_list_for_each_entry(entry, &host->windows) { + struct resource *res = entry->res; + unsigned long type = resource_type(res); + resource_size_t cpu_addr; + resource_size_t pci_addr; + resource_size_t size; + + if (type == IORESOURCE_IO) + cpu_addr = pci_pio_to_address(res->start); + else if (type == IORESOURCE_MEM) + cpu_addr = res->start; + else + continue; + + pci_addr = res->start - entry->offset; + size = resource_size(res); + err = mtk_pcie_set_trans_table(pcie, cpu_addr, pci_addr, size, + type, &table_index); + if (err) + return err; + } + /* * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal * causing occasional PCIe link down. In order to overcome the issue, @@ -510,31 +535,6 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) return err; } - mtk_pcie_enable_msi(pcie); - - /* Set PCIe translation windows */ - resource_list_for_each_entry(entry, &host->windows) { - struct resource *res = entry->res; - unsigned long type = resource_type(res); - resource_size_t cpu_addr; - resource_size_t pci_addr; - resource_size_t size; - - if (type == IORESOURCE_IO) - cpu_addr = pci_pio_to_address(res->start); - else if (type == IORESOURCE_MEM) - cpu_addr = res->start; - else - continue; - - pci_addr = res->start - entry->offset; - size = resource_size(res); - err = mtk_pcie_set_trans_table(pcie, cpu_addr, pci_addr, size, - type, &table_index); - if (err) - return err; - } - return 0; } From 867054421c3b94a70ddf51db658e992f19382c2f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:56 +0800 Subject: [PATCH 080/165] PCI: mediatek-gen3: Add error path for resume driver callbacks The resume callback currently does teardown in the conditional block directly. This is going to get ugly when the pwrctrl calls are added. Move the teardown to a proper error cleanup path. Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260324052002.4072430-5-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 1b6290f2c360..22a16e4ebc76 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1304,14 +1304,16 @@ static int mtk_pcie_resume_noirq(struct device *dev) return err; err = mtk_pcie_startup_port(pcie); - if (err) { - mtk_pcie_power_down(pcie); - return err; - } + if (err) + goto err_power_down; mtk_pcie_irq_restore(pcie); return 0; + +err_power_down: + mtk_pcie_power_down(pcie); + return err; } static const struct dev_pm_ops mtk_pcie_pm_ops = { From 69b8d3ccf73222518ce13fe4965c93c9d13ba5d7 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:57 +0800 Subject: [PATCH 081/165] PCI: mediatek-gen3: Split out device power helpers In preparation for adding full power on/off control with the pwrctrl API, split out the existing code that only partially deals with device power sequencing into separate helper functions. The existing code only handles PERST#. This is purely moving code around, and brings no functional changes. Signed-off-by: Chen-Yu Tsai [mani: moved the 'err' variable to next commit] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260324052002.4072430-6-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 86 ++++++++++++--------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 22a16e4ebc76..436dbd925181 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -403,6 +403,53 @@ static void mtk_pcie_enable_msi(struct mtk_gen3_pcie *pcie) writel_relaxed(val, pcie->base + PCIE_INT_ENABLE_REG); } +static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) +{ + u32 val; + + /* + * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal + * causing occasional PCIe link down. In order to overcome the issue, + * PCIE_RSTB signals are not asserted/released at this stage and the + * PCIe block is reset using en7523_reset_assert() and + * en7581_pci_enable(). + */ + if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { + /* Assert all reset signals */ + val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); + val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | + PCIE_PE_RSTB; + writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + + /* + * Described in PCIe CEM specification revision 6.0. + * + * The deassertion of PERST# should be delayed 100ms (TPVPERL) + * for the power and clock to become stable. + */ + msleep(PCIE_T_PVPERL_MS); + + /* De-assert reset signals */ + val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | + PCIE_PE_RSTB); + writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + } + + return 0; +} + +static void mtk_pcie_devices_power_down(struct mtk_gen3_pcie *pcie) +{ + u32 val; + + if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { + /* Assert the PERST# pin */ + val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); + val |= PCIE_PE_RSTB; + writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + } +} + static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) { struct resource_entry *entry; @@ -489,33 +536,9 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) return err; } - /* - * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal - * causing occasional PCIe link down. In order to overcome the issue, - * PCIE_RSTB signals are not asserted/released at this stage and the - * PCIe block is reset using en7523_reset_assert() and - * en7581_pci_enable(). - */ - if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { - /* Assert all reset signals */ - val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); - val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | - PCIE_PE_RSTB; - writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); - - /* - * Described in PCIe CEM specification revision 6.0. - * - * The deassertion of PERST# should be delayed 100ms (TPVPERL) - * for the power and clock to become stable. - */ - msleep(PCIE_T_PVPERL_MS); - - /* De-assert reset signals */ - val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | - PCIE_PE_RSTB); - writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); - } + err = mtk_pcie_devices_power_up(pcie); + if (err) + return err; /* Check if the link is up or not */ err = readl_poll_timeout(pcie->base + PCIE_LINK_STATUS_REG, val, @@ -1270,7 +1293,6 @@ static int mtk_pcie_suspend_noirq(struct device *dev) { struct mtk_gen3_pcie *pcie = dev_get_drvdata(dev); int err; - u32 val; /* Trigger link to L2 state */ err = mtk_pcie_turn_off_link(pcie); @@ -1279,13 +1301,7 @@ static int mtk_pcie_suspend_noirq(struct device *dev) return err; } - if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { - /* Assert the PERST# pin */ - val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); - val |= PCIE_PE_RSTB; - writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); - } - + mtk_pcie_devices_power_down(pcie); dev_dbg(pcie->dev, "entered L2 states successfully"); mtk_pcie_irq_save(pcie); From 55482b95792bdaceb513da8a66a911da85d812cb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:58 +0800 Subject: [PATCH 082/165] PCI: mediatek-gen3: Disable device if further setup fails If further setup fails after the device is powered on and link training succeeds, we want to place the device back in a quiescence state to avoid unintended activity and save power. This also helps with power state tracking and balancing once pwrctrl API is integrated. Power down the device in the error paths of mtk_pcie_startup_port() and mtk_pcie_probe(). Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260324052002.4072430-7-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 436dbd925181..aa14b021834f 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -555,10 +555,14 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) dev_err(pcie->dev, "PCIe link down, current LTSSM state: %s (%#x)\n", ltssm_state, val); - return err; + goto err_power_down_device; } return 0; + +err_power_down_device: + mtk_pcie_devices_power_down(pcie); + return err; } #define MTK_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ @@ -1218,6 +1222,7 @@ static int mtk_pcie_probe(struct platform_device *pdev) return 0; err_power_down_pcie: + mtk_pcie_devices_power_down(pcie); mtk_pcie_power_down(pcie); err_tear_down_irq: mtk_pcie_irq_teardown(pcie); From 1a152e21940a18caf7c9d0d753a46d2ffa3f5010 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 13:19:59 +0800 Subject: [PATCH 083/165] PCI: mediatek-gen3: Integrate new pwrctrl API With the new PCI pwrctrl API and PCI slot binding and power drivers, we now have a way to describe and power up WiFi/BT adapters connected through a PCIe or M.2 slot, or populated onto the mainboard itself. The latter case has the adapter layout or design copied verbatim, replacing the slot with direct connections. Integrate the PCI pwrctrl API into the PCIe driver, so that power is properly enabled before PCIe link training is done, allowing the card to successfully be detected. Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Bartosz Golaszewski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260324052002.4072430-8-wenst@chromium.org --- drivers/pci/controller/Kconfig | 1 + drivers/pci/controller/pcie-mediatek-gen3.c | 39 ++++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 5aaed8ac6e44..686349e09cd3 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -222,6 +222,7 @@ config PCIE_MEDIATEK_GEN3 depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST depends on PCI_MSI select IRQ_MSI_LIB + select PCI_PWRCTRL_GENERIC help Adds support for PCIe Gen3 MAC controller for MediaTek SoCs. This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed, diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index aa14b021834f..a94fdbaf47fe 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -405,6 +406,7 @@ static void mtk_pcie_enable_msi(struct mtk_gen3_pcie *pcie) static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) { + int err; u32 val; /* @@ -420,15 +422,23 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB; writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + } - /* - * Described in PCIe CEM specification revision 6.0. - * - * The deassertion of PERST# should be delayed 100ms (TPVPERL) - * for the power and clock to become stable. - */ - msleep(PCIE_T_PVPERL_MS); + err = pci_pwrctrl_power_on_devices(pcie->dev); + if (err) { + dev_err(pcie->dev, "Failed to power on devices: %pe\n", ERR_PTR(err)); + return err; + } + /* + * Described in PCIe CEM specification revision 6.0. + * + * The deassertion of PERST# should be delayed 100ms (TPVPERL) + * for the power and clock to become stable. + */ + msleep(PCIE_T_PVPERL_MS); + + if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { /* De-assert reset signals */ val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB); @@ -448,6 +458,8 @@ static void mtk_pcie_devices_power_down(struct mtk_gen3_pcie *pcie) val |= PCIE_PE_RSTB; writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); } + + pci_pwrctrl_power_off_devices(pcie->dev); } static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) @@ -1208,9 +1220,15 @@ static int mtk_pcie_probe(struct platform_device *pdev) if (err) return dev_err_probe(dev, err, "Failed to setup IRQ domains\n"); + err = pci_pwrctrl_create_devices(pcie->dev); + if (err) { + goto err_tear_down_irq; + dev_err_probe(dev, err, "failed to create pwrctrl devices\n"); + } + err = mtk_pcie_setup(pcie); if (err) - goto err_tear_down_irq; + goto err_destroy_pwrctrl; host->ops = &mtk_pcie_ops; host->sysdata = pcie; @@ -1224,6 +1242,9 @@ static int mtk_pcie_probe(struct platform_device *pdev) err_power_down_pcie: mtk_pcie_devices_power_down(pcie); mtk_pcie_power_down(pcie); +err_destroy_pwrctrl: + if (err != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(pcie->dev); err_tear_down_irq: mtk_pcie_irq_teardown(pcie); return err; @@ -1239,7 +1260,9 @@ static void mtk_pcie_remove(struct platform_device *pdev) pci_remove_root_bus(host->bus); pci_unlock_rescan_remove(); + pci_pwrctrl_power_off_devices(pcie->dev); mtk_pcie_power_down(pcie); + pci_pwrctrl_destroy_devices(pcie->dev); mtk_pcie_irq_teardown(pcie); } From 5f73cf1db829c21b7fd44a8d2587cd395b1b2d76 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Sat, 28 Feb 2026 16:09:25 +0800 Subject: [PATCH 084/165] PCI: imx6: Skip waiting for L2/L3 Ready on i.MX6SX On i.MX6SX, the LTSSM registers become inaccessible after the PME_Turn_Off message is sent to the link. So there is no way to verify whether the link has entered L2/L3 Ready state or not. Hence, set IMX_PCIE_FLAG_SKIP_L23_READY flag for i.MX6SX SoC to skip the L2/L3 Ready state polling and let the DWC core wait for 10ms after sending the PME_Turn_Off message as per the PCIe spec r6.0, sec 5.3.3.2.1. Fixes: a528d1a72597 ("PCI: imx6: Use DWC common suspend resume method") Signed-off-by: Richard Zhu [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260228080925.1558395-1-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 81a7093494c8..3dfc7bcf53d8 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1876,6 +1876,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX6SX, .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, From ff5387d4f0798cf1d1a4f548427bd9142cf35b66 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 5 Mar 2026 12:42:34 +0530 Subject: [PATCH 085/165] PCI: endpoint: Print the EPF name in the error log of pci_epf_make() Merely printing the error log without the actual EPF name will not give much clue to the users about the failure. Hence, print the EPF name also. Suggested-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260305071236.23792-1-mani@kernel.org --- drivers/pci/endpoint/pci-ep-cfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 076d115db02b..a19db22b7991 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -625,7 +625,8 @@ static struct config_group *pci_epf_make(struct config_group *group, epf = pci_epf_create(epf_name); if (IS_ERR(epf)) { err = PTR_ERR(epf); - pr_err("failed to create endpoint function device: %d\n", err); + pr_err("failed to create endpoint function device (%s): %d\n", + epf_name, err); goto free_name; } From 396d44dcaf8d367ed15ecec30e2f69c62f93306c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 5 Mar 2026 12:42:35 +0530 Subject: [PATCH 086/165] PCI: endpoint: Improve error messages Include errno in the error prints and also use dev_err() where applicable. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260305071236.23792-2-mani@kernel.org --- drivers/pci/endpoint/pci-ep-cfs.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index a19db22b7991..590e5bde57ef 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -565,7 +565,8 @@ static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) if (IS_ERR(group)) { dev_err(&epf_group->epf->dev, - "failed to create epf type specific attributes\n"); + "failed to create epf type specific attributes: %pe\n", + group); return; } @@ -578,13 +579,17 @@ static void pci_epf_cfs_add_sub_groups(struct pci_epf_group *epf_group) group = pci_ep_cfs_add_primary_group(epf_group); if (IS_ERR(group)) { - pr_err("failed to create 'primary' EPC interface\n"); + dev_err(&epf_group->epf->dev, + "failed to create 'primary' EPC interface: %pe\n", + group); return; } group = pci_ep_cfs_add_secondary_group(epf_group); if (IS_ERR(group)) { - pr_err("failed to create 'secondary' EPC interface\n"); + dev_err(&epf_group->epf->dev, + "failed to create 'secondary' EPC interface: %pe\n", + group); return; } @@ -675,8 +680,8 @@ struct config_group *pci_ep_cfs_add_epf_group(const char *name) group = configfs_register_default_group(functions_group, name, &pci_epf_group_type); if (IS_ERR(group)) - pr_err("failed to register configfs group for %s function\n", - name); + pr_err("failed to register configfs group for %s function: %pe\n", + name, group); return group; } From 192b8a1bf81c17852b6cf540c95b0a3bcc9c58c4 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Fri, 6 Mar 2026 10:12:47 +0800 Subject: [PATCH 087/165] PCI: imx6: Change imx_pcie_deassert_core_reset() return type to void The function imx_pcie_deassert_core_reset() always returns 0 and the return value is not used meaningfully by its callers. Change the return type from int to void to simplify the code and remove unnecessary error handling paths. No functional change intended. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Reviewed-by: Richard Zhu Link: https://patch.msgid.link/20260306021247.991976-1-sherry.sun@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 3dfc7bcf53d8..fc652d15a054 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -906,7 +906,7 @@ static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie) gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); } -static int imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) +static void imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) { reset_control_deassert(imx_pcie->pciephy_reset); @@ -920,8 +920,6 @@ static int imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ msleep(100); } - - return 0; } static int imx_pcie_wait_for_speed_change(struct imx_pcie *imx_pcie) @@ -1292,11 +1290,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) /* Make sure that PCIe LTSSM is cleared */ imx_pcie_ltssm_disable(dev); - ret = imx_pcie_deassert_core_reset(imx_pcie); - if (ret < 0) { - dev_err(dev, "pcie deassert core reset failed: %d\n", ret); - goto err_phy_off; - } + imx_pcie_deassert_core_reset(imx_pcie); if (imx_pcie->drvdata->wait_pll_lock) { ret = imx_pcie->drvdata->wait_pll_lock(imx_pcie); @@ -1613,9 +1607,7 @@ static int imx_pcie_resume_noirq(struct device *dev) ret = imx_pcie->drvdata->enable_ref_clk(imx_pcie, true); if (ret) return ret; - ret = imx_pcie_deassert_core_reset(imx_pcie); - if (ret) - return ret; + imx_pcie_deassert_core_reset(imx_pcie); /* * Using PCIE_TEST_PD seems to disable MSI and powers down the From 180ea823bb45eb71dd5ed0dc0b78633accd21096 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Fri, 6 Mar 2026 11:04:56 +0800 Subject: [PATCH 088/165] PCI: imx6: Separate PERST# assertion from core reset functions The imx_pcie_assert_core_reset() and imx_pcie_deassert_core_reset() functions are primarily intended to reset the RC controller itself, not the remote PCIe endpoint devices. However, the PERST# GPIO control was previously embedded within these functions, which conflates two distinct reset operations. Move the PERST# GPIO handling into a dedicated function imx_pcie_assert_perst(). This makes the code more maintainable and prepares for parsing the reset-gpios property according to the new Root Port DT binding in subsequent patches. No functional change is intended. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Reviewed-by: Richard Zhu Link: https://patch.msgid.link/20260306030456.1032815-1-sherry.sun@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 29 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index fc652d15a054..24c373020adc 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -901,9 +901,6 @@ static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie) if (imx_pcie->drvdata->core_reset) imx_pcie->drvdata->core_reset(imx_pcie, true); - - /* Some boards don't have PCIe reset GPIO. */ - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); } static void imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) @@ -912,14 +909,6 @@ static void imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) if (imx_pcie->drvdata->core_reset) imx_pcie->drvdata->core_reset(imx_pcie, false); - - /* Some boards don't have PCIe reset GPIO. */ - if (imx_pcie->reset_gpiod) { - msleep(100); - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); - /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ - msleep(100); - } } static int imx_pcie_wait_for_speed_change(struct imx_pcie *imx_pcie) @@ -1231,6 +1220,19 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge, imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); } +static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert) +{ + if (assert) { + gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); + } else { + if (imx_pcie->reset_gpiod) { + msleep(PCIE_T_PVPERL_MS); + gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); + msleep(PCIE_RESET_CONFIG_WAIT_MS); + } + } +} + static int imx_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -1253,6 +1255,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); if (imx_pcie->drvdata->init_phy) imx_pcie->drvdata->init_phy(imx_pcie); @@ -1291,6 +1294,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) imx_pcie_ltssm_disable(dev); imx_pcie_deassert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, false); if (imx_pcie->drvdata->wait_pll_lock) { ret = imx_pcie->drvdata->wait_pll_lock(imx_pcie); @@ -1587,6 +1591,7 @@ static int imx_pcie_suspend_noirq(struct device *dev) * clock which saves some power. */ imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); imx_pcie->drvdata->enable_ref_clk(imx_pcie, false); } else { return dw_pcie_suspend_noirq(imx_pcie->pci); @@ -1608,6 +1613,7 @@ static int imx_pcie_resume_noirq(struct device *dev) if (ret) return ret; imx_pcie_deassert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, false); /* * Using PCIE_TEST_PD seems to disable MSI and powers down the @@ -1845,6 +1851,7 @@ static void imx_pcie_shutdown(struct platform_device *pdev) /* bring down link, so bootloader gets clean state in case of reboot */ imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); } static const struct imx_pcie_drvdata drvdata[] = { From 698dab284b9d1c8aace73af8aaf0cfb74fc8ce92 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 7 Mar 2026 10:01:52 +0800 Subject: [PATCH 089/165] PCI: dwc: Expose PCIe event counters for groups 5 to 7 over debugfs Extend the DesignWare PCIe controller's debugfs statistical counter interface with event definitions from groups 5 through 7 as documented in the DWC PCIe Databook (version 6.30a, section 3.8.2.3, Tables 3-59, 3-60, 3-61). These counters provide visibility into Layer1 non-error events (link state transitions,ASPM, L1 substates), Layer2 DLLP exchanges, and Layer3 TLP transactions. The counters are exposed under the debugfs statistical counter directory, allowing users to monitor link behavior and diagnose PCIe protocol issues more effectively. Co-developed-by: Cheng Xin Signed-off-by: Cheng Xin Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Shawn Lin Link: https://patch.msgid.link/20260307020152.1224518-1-18255117159@163.com --- .../controller/dwc/pcie-designware-debugfs.c | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 0d1340c9b364..2efddb21b2b2 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -131,13 +131,16 @@ static const u32 err_inj_type_mask[] = { * supported in DWC RAS DES * @name: Name of the error counter * @group_no: Group number that the event belongs to. The value can range - * from 0 to 4 + * from 0 to 7 * @event_no: Event number of the particular event. The value ranges are: * Group 0: 0 - 10 * Group 1: 5 - 13 * Group 2: 0 - 7 * Group 3: 0 - 5 * Group 4: 0 - 1 + * Group 5: 0 - 13 + * Group 6: 0 - 6 + * Group 7: 0 - 25 */ struct dwc_pcie_event_counter { const char *name; @@ -181,6 +184,53 @@ static const struct dwc_pcie_event_counter event_list[] = { {"completion_timeout", 0x3, 0x5}, {"ebuf_skp_add", 0x4, 0x0}, {"ebuf_skp_del", 0x4, 0x1}, + {"l0_to_recovery_entry", 0x5, 0x0}, + {"l1_to_recovery_entry", 0x5, 0x1}, + {"tx_l0s_entry", 0x5, 0x2}, + {"rx_l0s_entry", 0x5, 0x3}, + {"aspm_l1_reject", 0x5, 0x4}, + {"l1_entry", 0x5, 0x5}, + {"l1_cpm", 0x5, 0x6}, + {"l1.1_entry", 0x5, 0x7}, + {"l1.2_entry", 0x5, 0x8}, + {"l1_short_duration", 0x5, 0x9}, + {"l1.2_abort", 0x5, 0xa}, + {"l2_entry", 0x5, 0xb}, + {"speed_change", 0x5, 0xc}, + {"link_width_change", 0x5, 0xd}, + {"tx_ack_dllp", 0x6, 0x0}, + {"tx_update_fc_dllp", 0x6, 0x1}, + {"rx_ack_dllp", 0x6, 0x2}, + {"rx_update_fc_dllp", 0x6, 0x3}, + {"rx_nullified_tlp", 0x6, 0x4}, + {"tx_nullified_tlp", 0x6, 0x5}, + {"rx_duplicate_tlp", 0x6, 0x6}, + {"tx_memory_write", 0x7, 0x0}, + {"tx_memory_read", 0x7, 0x1}, + {"tx_configuration_write", 0x7, 0x2}, + {"tx_configuration_read", 0x7, 0x3}, + {"tx_io_write", 0x7, 0x4}, + {"tx_io_read", 0x7, 0x5}, + {"tx_completion_without_data", 0x7, 0x6}, + {"tx_completion_w_data", 0x7, 0x7}, + {"tx_message_tlp_pcie_vc_only", 0x7, 0x8}, + {"tx_atomic", 0x7, 0x9}, + {"tx_tlp_with_prefix", 0x7, 0xa}, + {"rx_memory_write", 0x7, 0xb}, + {"rx_memory_read", 0x7, 0xc}, + {"rx_configuration_write", 0x7, 0xd}, + {"rx_configuration_read", 0x7, 0xe}, + {"rx_io_write", 0x7, 0xf}, + {"rx_io_read", 0x7, 0x10}, + {"rx_completion_without_data", 0x7, 0x11}, + {"rx_completion_w_data", 0x7, 0x12}, + {"rx_message_tlp_pcie_vc_only", 0x7, 0x13}, + {"rx_atomic", 0x7, 0x14}, + {"rx_tlp_with_prefix", 0x7, 0x15}, + {"tx_ccix_tlp", 0x7, 0x16}, + {"rx_ccix_tlp", 0x7, 0x17}, + {"tx_deferrable_memory_write_tlp", 0x7, 0x18}, + {"rx_deferrable_memory_write_tlp", 0x7, 0x19}, }; static ssize_t lane_detect_read(struct file *file, char __user *buf, From 33a76fc3c3e61386524479b99f35423bd3d9a895 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Sat, 14 Mar 2026 07:26:34 +0530 Subject: [PATCH 090/165] PCI: qcom: Advertise Hotplug Slot Capability with no Command Completion support Qcom PCIe Root Ports advertise hotplug capability in hardware, but do not support hotplug command completion. As a result, the hotplug commands issued by the pciehp driver never gets completion notification, leading to repeated timeout warnings and multi-second delays during boot and suspend/resume. Commit a54db86ddc153 ("PCI: qcom: Do not advertise hotplug capability for IPs v2.7.0 and v1.9.0") mistakenly assumed that the Root Ports doesn't support Hotplug due to timeouts and disabled the Hotplug functionality altogether. But the Root Ports does support reporting Hotplug events like DL_Up/Down events. So to fix the command completion timeout issues, just set the No Command Completed Support (NCCS) bit and enable Hotplug in Slot Capability field back. Fixes: a54db86ddc153 ("PCI: qcom: Do not advertise hotplug capability for IPs v2.7.0 and v1.9.0") Signed-off-by: Krishna Chaitanya Chundru [mani: renamed function, commit log and added comment] Signed-off-by: Manivannan Sadhasivam Tested-by: Konrad Dybcio # Hamoa CRD, tunneled link Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260314-hotplug-v1-1-96ac87d93867@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 67a16af69ddc..9fdfc88ac151 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -350,15 +350,20 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci) dw_pcie_dbi_ro_wr_dis(pci); } -static void qcom_pcie_clear_hpc(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); + /* + * Qcom PCIe Root Ports do not support generating command completion + * 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 &= ~PCI_EXP_SLTCAP_HPC; + val |= PCI_EXP_SLTCAP_NCCS; writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP); dw_pcie_dbi_ro_wr_dis(pci); @@ -558,7 +563,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) writel(CFG_BRIDGE_SB_INIT, pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1); - qcom_pcie_clear_hpc(pcie->pci); + qcom_pcie_set_slot_nccs(pcie->pci); return 0; } @@ -638,7 +643,7 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); } - qcom_pcie_clear_hpc(pcie->pci); + qcom_pcie_set_slot_nccs(pcie->pci); return 0; } @@ -731,7 +736,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) val |= EN; writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); - qcom_pcie_clear_hpc(pcie->pci); + qcom_pcie_set_slot_nccs(pcie->pci); return 0; } @@ -1037,7 +1042,7 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) writel(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN, pcie->parf + PARF_NO_SNOOP_OVERRIDE); - qcom_pcie_clear_hpc(pcie->pci); + qcom_pcie_set_slot_nccs(pcie->pci); return 0; } From 99d986686331ba3fd58dffb312e282d2bf81ee72 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sun, 15 Mar 2026 23:53:51 +0800 Subject: [PATCH 091/165] PCI: sky1: Use boolean true for is_rc field The is_rc field in struct cdns_pcie is of type bool. Replace the integer assignment (1) with the boolean literal true to improve code clarity and maintain consistency with the type definition. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260315155351.127078-1-18255117159@163.com --- drivers/pci/controller/cadence/pci-sky1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pci-sky1.c b/drivers/pci/controller/cadence/pci-sky1.c index 9853a9c82c0e..cd55c64e58a9 100644 --- a/drivers/pci/controller/cadence/pci-sky1.c +++ b/drivers/pci/controller/cadence/pci-sky1.c @@ -173,7 +173,7 @@ static int sky1_pcie_probe(struct platform_device *pdev) cdns_pcie->ops = &sky1_pcie_ops; cdns_pcie->reg_base = pcie->reg_base; cdns_pcie->msg_res = pcie->msg_res; - cdns_pcie->is_rc = 1; + cdns_pcie->is_rc = true; reg_off = devm_kzalloc(dev, sizeof(*reg_off), GFP_KERNEL); if (!reg_off) { From 28d20b0d895849ce3fe8c6f77baffc08886c2157 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:18 +0800 Subject: [PATCH 092/165] PCI: Add pcie_get_link_speed() helper for safe array access The pcie_link_speed[] array is indexed by PCIe generation numbers (1 = 2.5 GT/s, 2 = 5 GT/s, ...). Several drivers use it directly, which can lead to out-of-bounds accesses if an invalid generation number is used. Introduce a helper function pcie_get_link_speed() that returns the pci_bus_speed value for a given generation number, or PCI_SPEED_UNKNOWN if the generation is out of range. This will allow us to safely handle invalid values after the range check is removed from of_pci_get_max_link_speed(). Signed-off-by: Hans Zhang <18255117159@163.com> [mani: Fixed kernel-doc] Signed-off-by: Manivannan Sadhasivam Acked-by: Bjorn Helgaas Link: https://patch.msgid.link/20260313165522.123518-2-18255117159@163.com --- drivers/pci/pci.h | 2 ++ drivers/pci/probe.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..409aca7d737a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -108,6 +108,8 @@ struct pcie_tlp_log; PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) extern const unsigned char pcie_link_speed[]; +unsigned char pcie_get_link_speed(unsigned int speed); + extern bool pci_early_dump; extern struct mutex pci_rescan_remove_lock; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bccc7a4bdd79..f85da50a3d83 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -783,6 +783,22 @@ const unsigned char pcie_link_speed[] = { }; EXPORT_SYMBOL_GPL(pcie_link_speed); +/** + * pcie_get_link_speed - Get speed value from PCIe generation number + * @speed: PCIe speed (1-based: 1 = 2.5GT, 2 = 5GT, ...) + * + * Returns the speed value (e.g., PCIE_SPEED_2_5GT) if @speed is valid, + * otherwise returns PCI_SPEED_UNKNOWN. + */ +unsigned char pcie_get_link_speed(unsigned int speed) +{ + if (speed >= ARRAY_SIZE(pcie_link_speed)) + return PCI_SPEED_UNKNOWN; + + return pcie_link_speed[speed]; +} +EXPORT_SYMBOL_GPL(pcie_get_link_speed); + const char *pci_speed_string(enum pci_bus_speed speed) { /* Indexed by the pci_bus_speed enum */ From 0f34f647f19465b475b13e889dcb33526e166d24 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:19 +0800 Subject: [PATCH 093/165] PCI: dwc: Use pcie_get_link_speed() helper for safe array access Replace direct indexing of pcie_link_speed[] with the new helper pcie_get_link_speed() in all DesignWare core and glue drivers. This ensures that out-of-range generation numbers do not cause out-of-bounds accesses when the helper returns PCI_SPEED_UNKNOWN, and prepares for the removal of the range check in of_pci_get_max_link_speed(). The actual validation of the "max-link-speed" DT property (e.g., fallback to a safe default and warning) is added in subsequent patches for each driver that reads the property. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-3-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/dwc/pcie-designware.c | 2 +- drivers/pci/controller/dwc/pcie-qcom-common.c | 2 +- drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 ++-- drivers/pci/controller/dwc/pcie-qcom.c | 6 +++--- drivers/pci/controller/dwc/pcie-tegra194.c | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 6ae6189e9b8a..0e05c5280344 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1081,7 +1081,7 @@ static void dw_pcie_program_presets(struct dw_pcie_rp *pp, enum pci_bus_speed sp static void dw_pcie_config_presets(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - enum pci_bus_speed speed = pcie_link_speed[pci->max_link_speed]; + enum pci_bus_speed speed = pcie_get_link_speed(pci->max_link_speed); /* * Lane equalization settings need to be applied for all data rates the diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 5741c09dde7f..06792ba92aa7 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -861,7 +861,7 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci) ctrl2 = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2); ctrl2 &= ~PCI_EXP_LNKCTL2_TLS; - switch (pcie_link_speed[pci->max_link_speed]) { + switch (pcie_get_link_speed(pci->max_link_speed)) { case PCIE_SPEED_2_5GT: link_speed = PCI_EXP_LNKCTL2_TLS_2_5GT; break; diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 01c5387e53bf..5aa73c628737 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -22,7 +22,7 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci) * applied. */ - for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) { + for (speed = PCIE_SPEED_8_0GT; speed <= pcie_get_link_speed(pci->max_link_speed); speed++) { if (speed > PCIE_SPEED_32_0GT) { dev_warn(dev, "Skipped equalization settings for unsupported data rate\n"); break; diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 18460f01b2c6..4b7184d4a6fa 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -152,7 +152,7 @@ #define WAKE_DELAY_US 2000 /* 2 ms */ #define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ - Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed))) #define to_pcie_ep(x) dev_get_drvdata((x)->dev) @@ -531,7 +531,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) qcom_pcie_common_set_equalization(pci); - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) + if (pcie_get_link_speed(pci->max_link_speed) == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); /* diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 67a16af69ddc..5c7c105bb745 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -170,7 +170,7 @@ #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) #define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ - Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed))) struct qcom_pcie_resources_1_0_0 { struct clk_bulk_data *clks; @@ -320,7 +320,7 @@ static int qcom_pcie_start_link(struct dw_pcie *pci) qcom_pcie_common_set_equalization(pci); - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) + if (pcie_get_link_speed(pci->max_link_speed) == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); /* Enable Link Training state machine */ @@ -1579,7 +1579,7 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie) ret); } } else if (pcie->use_pm_opp) { - freq_mbps = pcie_dev_speed_mbps(pcie_link_speed[speed]); + freq_mbps = pcie_dev_speed_mbps(pcie_get_link_speed(speed)); if (freq_mbps < 0) return; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 06571d806ab3..47f08adfbd79 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -310,7 +310,7 @@ static void tegra_pcie_icc_set(struct tegra_pcie_dw *pcie) speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, val); width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val); - val = width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]); + val = width * PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed)); if (icc_set_bw(pcie->icc_path, Mbps_to_icc(val), 0)) dev_err(pcie->dev, "can't set bw[%u]\n", val); From 126d04398cd4e29743828c38c21f39f46fc2e004 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:20 +0800 Subject: [PATCH 094/165] PCI: j721e: Validate max-link-speed from DT Use the new pcie_get_link_speed() helper to validate the value read from the "max-link-speed" DT property. If the value is missing or invalid, fall back to Gen2 (speed = 2). This prepares for the removal of the range check in of_pci_get_max_link_speed(). Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-4-18255117159@163.com --- drivers/pci/controller/cadence/pci-j721e.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 6f2501479c70..bfdfe98d5aba 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -202,7 +202,8 @@ static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, int ret; link_speed = of_pci_get_max_link_speed(np); - if (link_speed < 2) + if ((link_speed < 2) || + (pcie_get_link_speed(link_speed) == PCI_SPEED_UNKNOWN)) link_speed = 2; val = link_speed - 1; From 03f920936977b5133a0e57a7c9fdeb4e584eeb06 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:21 +0800 Subject: [PATCH 095/165] PCI: controller: Validate max-link-speed Add validation for the "max-link-speed" DT property in three more drivers, using the pcie_get_link_speed() helper. - brcmstb: If the value is missing or invalid, fall back to no limitation (pcie->gen = 0). Fix the previous incorrect logic. - mediatek-gen3: If the value is missing or invalid, use the maximum speed supported by the controller. - rzg3s-host: If the value is missing or invalid, fall back to Gen2. This ensures that all users of of_pci_get_max_link_speed() are ready for the removal of the central range check. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260313165522.123518-5-18255117159@163.com --- drivers/pci/controller/pcie-brcmstb.c | 5 +++-- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- drivers/pci/controller/pcie-rzg3s-host.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 062f55690012..714bcab97b60 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1442,7 +1442,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); dev_info(dev, "link up, %s x%u %s\n", - pci_speed_string(pcie_link_speed[cls]), nlw, + pci_speed_string(pcie_get_link_speed(cls)), nlw, ssc_good ? "(SSC)" : "(!SSC)"); return 0; @@ -2072,7 +2072,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) return PTR_ERR(pcie->clk); ret = of_pci_get_max_link_speed(np); - pcie->gen = (ret < 0) ? 0 : ret; + if (pcie_get_link_speed(ret) == PCI_SPEED_UNKNOWN) + pcie->gen = 0; pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 75ddb8bee168..3b903ef7d3cf 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1150,7 +1150,7 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) return err; err = of_pci_get_max_link_speed(pcie->dev->of_node); - if (err) { + if (pcie_get_link_speed(err) != PCI_SPEED_UNKNOWN) { /* Get the maximum speed supported by the controller */ max_speed = mtk_pcie_get_controller_max_link_speed(pcie); diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 2809112e6317..00a11f986117 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -966,7 +966,7 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) ls = readw_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKSTA); cs2 = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2); - switch (pcie_link_speed[host->max_link_speed]) { + switch (pcie_get_link_speed(host->max_link_speed)) { case PCIE_SPEED_5_0GT: max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_5_0GB - 1, 0); link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT; From c8b610341914cb62899e31b41f724b5c4831a645 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:22 +0800 Subject: [PATCH 096/165] PCI: of: Remove max-link-speed generation validation The of_pci_get_max_link_speed() function currently validates the "max-link-speed" DT property to be in the range 1..4 (Gen1..Gen4). This imposes a maintenance burden because each new PCIe generation would require updating this validation. Remove the range check so the function returns the raw property value (or a negative error code if the property is missing or malformed). Since the callers are now validating the returned speed against the range they support, this check can now be safely removed. Removing the validation from this common function also allows future PCIe generations to be supported without modifying drivers/pci/of.c. Signed-off-by: Hans Zhang <18255117159@163.com> [mani: commit log and kernel doc fix] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-6-18255117159@163.com --- drivers/pci/of.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 9f8eb5df279e..c216701c75dd 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -875,24 +875,19 @@ EXPORT_SYMBOL_GPL(of_pci_supply_present); * of_pci_get_max_link_speed - Find the maximum link speed of the given device node. * @node: Device tree node with the maximum link speed information. * - * This function will try to find the limitation of link speed by finding - * a property called "max-link-speed" of the given device node. + * This function will try to read the "max-link-speed" property of the given + * device tree node. It does NOT validate the value of the property. * - * Return: - * * > 0 - On success, a maximum link speed. - * * -EINVAL - Invalid "max-link-speed" property value, or failure to access - * the property of the device tree node. - * - * Returns the associated max link speed from DT, or a negative value if the - * required property is not found or is invalid. + * Return: Maximum link speed value on success, errno on failure. */ int of_pci_get_max_link_speed(struct device_node *node) { u32 max_link_speed; + int ret; - if (of_property_read_u32(node, "max-link-speed", &max_link_speed) || - max_link_speed == 0 || max_link_speed > 4) - return -EINVAL; + ret = of_property_read_u32(node, "max-link-speed", &max_link_speed); + if (ret) + return ret; return max_link_speed; } From 92427ab4378faa168d6953d0f8574b8fc1edcc14 Mon Sep 17 00:00:00 2001 From: Ahmed Naseef Date: Thu, 12 Mar 2026 16:53:32 +0000 Subject: [PATCH 097/165] PCI: Prevent assignment to unsupported bridge windows Previously, pci_read_bridge_io() and pci_read_bridge_mmio_pref() unconditionally set resource type flags (IORESOURCE_IO or IORESOURCE_MEM | IORESOURCE_PREFETCH) when reading bridge window registers. For windows that are not implemented in hardware, this may cause the allocator to assign space for a window that doesn't exist. For example, the EcoNET EN7528 SoC Root Port doesn't support the prefetchable window, but since a downstream device had a prefetchable BAR, the allocator mistakenly assigned a prefetchable window: pci 0001:00:01.0: [14c3:0811] type 01 class 0x060400 PCIe Root Port pci 0001:00:01.0: PCI bridge to [bus 01-ff] pci 0001:00:01.0: bridge window [mem 0x28000000-0x280fffff]: assigned pci 0001:00:01.0: bridge window [mem 0x28100000-0x282fffff pref]: assigned pci 0001:01:00.0: BAR 0 [mem 0x28100000-0x281fffff 64bit pref]: assigned pci_read_bridge_windows() already detects unsupported windows by testing register writability and sets dev->io_window/pref_window accordingly. Check dev->io_window/pref_window so we don't set the resource flags for unsupported windows, which prevents the allocator from assigning space to them. After this commit, the prefetchable BAR is correctly allocated from the non-prefetchable window: pci 0001:00:01.0: bridge window [mem 0x28000000-0x281fffff]: assigned pci 0001:01:00.0: BAR 0 [mem 0x28000000-0x280fffff 64bit pref]: assigned Suggested-by: Bjorn Helgaas Link: https://lore.kernel.org/all/20260113210259.GA715789@bhelgaas/ Signed-off-by: Ahmed Naseef Signed-off-by: Caleb James DeLisle Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260312165332.569772-4-cjd@cjdns.fr --- drivers/pci/probe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bccc7a4bdd79..4eacb741b4ec 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -395,6 +395,9 @@ static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res, unsigned long io_mask, io_granularity, base, limit; struct pci_bus_region region; + if (!dev->io_window) + return; + io_mask = PCI_IO_RANGE_MASK; io_granularity = 0x1000; if (dev->io_window_1k) { @@ -465,6 +468,9 @@ static void pci_read_bridge_mmio_pref(struct pci_dev *dev, struct resource *res, pci_bus_addr_t base, limit; struct pci_bus_region region; + if (!dev->pref_window) + return; + pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; From dc4b4d04e1caa3552f000d84d832779ebe51b093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Thu, 19 Feb 2026 17:39:51 +0200 Subject: [PATCH 098/165] PCI: Prevent shrinking bridge window from its required size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steve reported an eGPU (either Radeon Instinct MI50 32GB or NVIDIA 3080 10GB) connected via Thunderbolt was not assigned sufficient BAR space in v6.11, so the amdgpu and nvidia drivers were unable to initialize the device. pci_bridge_distribute_available_resources() -> ... -> adjust_bridge_window() is called between __pci_bus_size_bridges() and assigning the resources. Since the commit 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") adjust_bridge_window() can also shrink the bridge window. The shrunken size, however, conflicts with what __pci_bus_size_bridges() -> pbus_size_mem() calculated as the required bridge window size. By shrinking the size, adjust_bridge_window() prevents the rest of the resource fitting algorithm from working as intended. Resource fitting logic is expecting assignment failures when bridge windows need resizing, but there are cases where failures are no longer happening after the commit 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary"). The commit 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") justifies the change by the extra reservation made due to hpmemsize parameter, however, the kernel code contradicts that statement. (For simplicity, finer-grained hpmmiosize and hpmmiopref parameters that can be used to the same effect as hpmemsize are ignored in this description.) pbus_size_mem() calls calculate_memsize() twice. First with add_size=0 to find out the minimal required resource size. The second call occurs with add_size=hpmemsize (effectively) but the result does not directly affect the resource size only resulting in an entry on the realloc_head list (a.k.a. add_list). Yet, adjust_bridge_window() directly changes the resource size which does not include what is reserved due to hpmemsize. Also, if the required size for the bridge window exceeds hpmemsize, the parameter does not have any effect even on the second size calculation made by pbus_size_mem(); from calculate_memsize(): size = max(size, add_size) + children_add_size; The commit ae4611f1d7e9 ("PCI: Set resource size directly in adjust_bridge_window()") that precedes the commit 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") is also related to causing this problem. Its changelog explicitly states adjust_bridge_window() wants to "guarantee" allocation success. Guaranteed allocations, however, are incompatible with how the other parts of the resource fitting algorithm work. The given justification fails to explain why guaranteed allocations at this stage are required nor why forcing window to a smaller value than what was calculated by pbus_size_mem() is correct. While the change might have worked by chance in some test scenario, too small bridge window does not "guarantee" success from the point of view of the endpoint device resource assignments. No issue is mentioned within the changelog so it's unclear if the change was made to fix some observed issue nor and what that issue was. The unwanted shrinking of a bridge window occurs, e.g., when a device with large BARs such as eGPU is attached using Thunderbolt and the Root Port holds less than enough resource space for the eGPU. The GPU resources are in order of GBs and the default hotplug allocation is a mere 2MB (DEFAULT_HOTPLUG_MMIO_PREF_SIZE). The problem is illustrated by this log (filtered to the relevant content only): pci 0000:00:07.0: PCI bridge to [bus 03-2c] pci 0000:00:07.0: bridge window [mem 0x6000000000-0x601bffffff 64bit pref] pci 0000:03:00.0: PCI bridge to [bus 00] pci 0000:03:00.0: bridge window [mem 0x00000000-0x000fffff 64bit pref] pci 0000:03:00.0: bridge configuration invalid ([bus 00-00]), reconfiguring pci 0000:03:00.0: PCI bridge to [bus 04-2c] pcieport 0000:00:07.0: Assigned bridge window [mem 0x6000000000-0x601bffffff 64bit pref] to [bus 03-2c] cannot fit 0xc00000000 required for 0000:03:00.0 bridging to [bus 04-2c] pci 0000:03:00.0: bridge window [mem 0x800000000-0x10003fffff 64bit pref] to [bus 04-2c] add_size 100000 add_align 100000 pcieport 0000:00:07.0: distributing available resources pci 0000:03:00.0: bridge window [mem 0x800000000-0x10003fffff 64bit pref] shrunken by 0x00000007e4400000 pci 0000:03:00.0: bridge window [mem 0x6000000000-0x601bffffff 64bit pref]: assigned The initial size of the Root Port's window is 448MB (0x601bffffff - 0x6000000000). __pci_bus_size_bridges() -> pbus_size_mem() calculates the required size to be 32772 MB (0x10003fffff - 0x800000000) which would fit the eGPU resources. adjust_bridge_window() then shrinks the bridge window down to what is guaranteed to fit into the Root Port's bridge window. The bridge window for 03:00.0 is also eliminated from the add_list (a.k.a. realloc_head) list by adjust_bridge_window(). After adjustment, the resources are assigned and as the bridge window for 03:00.0 is assigned successfully, no failure is recorded. Without a failure, no attempt to resize the window of the Root Port is required. The end result is eGPU not having large enough resources to work. The commit 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") also claims nested bridge windows are sized the same, which is false. pbus_size_mem() calculates the size for the parent bridge window by summing all the downstream resources so the resource fitting calculates larger bridge window for the parent to accommodate the childen. That is, hpmemsize does not result the same size for the case where there are nested bridge windows. In order to fix the most immediate problem, don't shrink the resource size in adjust_bridge_window() as hpmemsize had nothing to do with it. When considering add_size, only reduce it up to what is added due to hpmemsize (if required size is larger than hpmemsize, the parameter has no impact, see calculate_memsize()). Unfortunately, if the tail of the bridge window was aligned in calculate_memsize() from below hpmemsize to above it, the size check will falsely match but the check at least errs to the side of caution. There's not enough information available in adjust_bridge_window() to know the calculated size precisely. This is not exactly a revert of the commits e4611f1d7e9 ("PCI: Set resource size directly in adjust_bridge_window()") and 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") as shrinking still remains in place but is implemented differently, and the end result behaves very differently. It is possible that those two commits fixed some other issue that is not described with enough detail in the changelog and undoing parts of them results in another regression due to behavioral change. Nonetheless, as described above, the solution by those two commits was flawed and the issue, if one exists, should be solved in a way that is compatible with the rest of the resource fitting algorithm instead of working against it. Besides shrinking, the case where adjust_bridge_window() expands the bridge window is likely somewhat wrong as well because it removes the entry from add_list (a.k.a. realloc_head), but it is less damaging as that only impacts optional resources and may have no impact if expanding by hpmemsize is larger than what add_size was. Fixing it is left as further work. Fixes: 948675736a77 ("PCI: Allow adjust_bridge_window() to shrink resource if necessary") Fixes: ae4611f1d7e9 ("PCI: Set resource size directly in adjust_bridge_window()") Reported-by: Steve Oswald Closes: https://lore.kernel.org/linux-pci/CAN95MYEaO8QYYL=5cN19nv_qDGuuP5QOD17pD_ed6a7UqFVZ-g@mail.gmail.com/ Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260219153951.68869-1-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-bus.c | 42 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 61f769aaa2f6..1f87b018799f 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1837,6 +1837,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, resource_size_t new_size) { resource_size_t add_size, size = resource_size(res); + struct pci_dev_resource *dev_res; if (resource_assigned(res)) return; @@ -1849,9 +1850,46 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, &add_size); } else if (new_size < size) { + int idx = pci_resource_num(bridge, res); + + /* + * hpio/mmio/mmioprefsize hasn't been included at all? See the + * add_size param at the callsites of calculate_memsize(). + */ + if (!add_list) + return; + + /* Only shrink if the hotplug extra relates to window size. */ + switch (idx) { + case PCI_BRIDGE_IO_WINDOW: + if (size > pci_hotplug_io_size) + return; + break; + case PCI_BRIDGE_MEM_WINDOW: + if (size > pci_hotplug_mmio_size) + return; + break; + case PCI_BRIDGE_PREF_MEM_WINDOW: + if (size > pci_hotplug_mmio_pref_size) + return; + break; + default: + break; + } + + dev_res = res_to_dev_res(add_list, res); add_size = size - new_size; - pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res, - &add_size); + if (add_size < dev_res->add_size) { + dev_res->add_size -= add_size; + pci_dbg(bridge, "bridge window %pR optional size shrunken by %pa\n", + res, &add_size); + } else { + pci_dbg(bridge, "bridge window %pR optional size removed\n", + res); + pci_dev_res_remove_from_list(add_list, res); + } + return; + } else { return; } From 1ee4716a5a28eaef81ae1f280d983258bee49623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 13 Mar 2026 10:45:50 +0200 Subject: [PATCH 099/165] PCI: Fix premature removal from realloc_head list during resource assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reassign_resources_sorted() checks for two things: a) Resource assignment failures for mandatory resources by checking if the resource remains unassigned, which are known to always repeat, and does not attempt to assign them again. b) That resource is not among the ones being processed/assigned at this stage, leading to skip processing such resources in reassign_resources_sorted() as well (resource assignment progresses one PCI hierarchy level at a time). The problem here is that a) is checked before b), but b) also implies the resource is not being assigned yet, making also a) true. As a) only skips resource assignment but still removes the resource from realloc_head, the later stages that would need to process the information in realloc_head cannot obtain the optional size information anymore. This leads to considering only non-optional part for bridge windows deeper in the PCI hierarchy. This problem has been observed during rescan (add_size is not considered while attempting assignment for 0000:e2:00.0 indicating the corresponding entry was removed from realloc_head while processing resource assignments for 0000:e1): pci_bus 0000:e1: scanning bus ... pci 0000:e3:01.0: bridge window [mem 0x800000000-0x1000ffffff 64bit pref] to [bus e4] add_size 60c000000 add_align 800000000 pci 0000:e3:01.0: bridge window [mem 0x00100000-0x000fffff] to [bus e4] add_size 200000 add_align 200000 pci 0000:e3:02.0: disabling bridge window [mem 0x00000000-0x000fffff 64bit pref] to [bus e5] (unused) pci 0000:e2:00.0: bridge window [mem 0x800000000-0x1000ffffff 64bit pref] to [bus e3-e5] add_size 60c000000 add_align 800000000 pci 0000:e2:00.0: bridge window [mem 0x00100000-0x001fffff] to [bus e3-e5] add_size 200000 add_align 200000 pcieport 0000:e1:02.0: bridge window [io size 0x2000]: can't assign; no space pcieport 0000:e1:02.0: bridge window [io size 0x2000]: failed to assign pcieport 0000:e1:02.0: bridge window [io 0x1000-0x2fff]: resource restored pcieport 0000:e1:02.0: bridge window [io 0x1000-0x2fff]: resource restored pcieport 0000:e1:02.0: bridge window [io size 0x2000]: can't assign; no space pcieport 0000:e1:02.0: bridge window [io size 0x2000]: failed to assign pci 0000:e2:00.0: bridge window [mem 0x28f000000000-0x28f800ffffff 64bit pref]: assigned Fixes: 96336ec70264 ("PCI: Perform reset_resource() and build fail list in sync") Reported-by: Peter Nisbet Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Peter Nisbet Link: https://patch.msgid.link/20260313084551.1934-1-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1f87b018799f..9506845c112c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -434,6 +434,10 @@ static void reassign_resources_sorted(struct list_head *realloc_head, dev = add_res->dev; idx = pci_resource_num(dev, res); + /* Skip this resource if not found in head list */ + if (!res_to_dev_res(head, res)) + continue; + /* * Skip resource that failed the earlier assignment and is * not optional as it would just fail again. @@ -442,10 +446,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, !pci_resource_is_optional(dev, idx)) goto out; - /* Skip this resource if not found in head list */ - if (!res_to_dev_res(head, res)) - continue; - res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; From edfaa81d5da5fbfe3c73fece3ca0417a04cc4ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:24 +0200 Subject: [PATCH 100/165] resource: Add __resource_contains_unbound() for internal contains checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __find_resource_space() currently uses resource_contains() but for tentative resources that are not yet crafted into the resource tree. As resource_contains() checks that IORESOURCE_UNSET is not set for either of the resources, the caller has to hack around this problem by clearing the IORESOURCE_UNSET flag (essentially lying to resource_contains()). Instead of the hack, introduce __resource_contains_unbound() for cases like this. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-2-ilpo.jarvinen@linux.intel.com --- include/linux/ioport.h | 20 +++++++++++++++++--- kernel/resource.c | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 5533a5debf3f..19d5e04564d9 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -304,14 +304,28 @@ static inline unsigned long resource_ext_type(const struct resource *res) { return res->flags & IORESOURCE_EXT_TYPE_BITS; } -/* True iff r1 completely contains r2 */ -static inline bool resource_contains(const struct resource *r1, const struct resource *r2) + +/* + * For checking if @r1 completely contains @r2 for resources that have real + * addresses but are not yet crafted into the resource tree. Normally + * resource_contains() should be used instead of this function as it checks + * also IORESOURCE_UNSET flag. + */ +static inline bool __resource_contains_unbound(const struct resource *r1, + const struct resource *r2) { if (resource_type(r1) != resource_type(r2)) return false; + + return r1->start <= r2->start && r1->end >= r2->end; +} +/* True iff r1 completely contains r2 */ +static inline bool resource_contains(const struct resource *r1, const struct resource *r2) +{ if (r1->flags & IORESOURCE_UNSET || r2->flags & IORESOURCE_UNSET) return false; - return r1->start <= r2->start && r1->end >= r2->end; + + return __resource_contains_unbound(r1, r2); } /* True if any part of r1 overlaps r2 */ diff --git a/kernel/resource.c b/kernel/resource.c index bb966699da31..1e2f1dfc0edd 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -754,7 +754,7 @@ static int __find_resource_space(struct resource *root, struct resource *old, /* Check for overflow after ALIGN() */ avail.start = ALIGN(tmp.start, constraint->align); avail.end = tmp.end; - avail.flags = new->flags & ~IORESOURCE_UNSET; + avail.flags = new->flags; if (avail.start >= tmp.start) { alloc.flags = avail.flags; if (alignf) { @@ -765,7 +765,7 @@ static int __find_resource_space(struct resource *root, struct resource *old, } alloc.end = alloc.start + size - 1; if (alloc.start <= alloc.end && - resource_contains(&avail, &alloc)) { + __resource_contains_unbound(&avail, &alloc)) { new->start = alloc.start; new->end = alloc.end; return 0; From f699bcc8bcdf99565928a7b1fc7ee656f6c81815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:25 +0200 Subject: [PATCH 101/165] resource: Pass full extent of empty space to resource_alignf callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __find_resource_space() calculates the full extent of empty space but only passes the aligned space to resource_alignf callback. In some situations, the callback may choose take advantage of the free space before the requested alignment. Pass the full extent of the calculated empty space to resource_alignf callback as an additional parameter. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-3-ilpo.jarvinen@linux.intel.com --- arch/alpha/kernel/pci.c | 1 + arch/arm/kernel/bios32.c | 4 +++- arch/m68k/kernel/pcibios.c | 4 +++- arch/mips/pci/pci-generic.c | 3 ++- arch/mips/pci/pci-legacy.c | 1 + arch/parisc/kernel/pci.c | 4 +++- arch/powerpc/kernel/pci-common.c | 4 +++- arch/s390/pci/pci.c | 1 + arch/sh/drivers/pci/pci.c | 4 +++- arch/x86/pci/i386.c | 3 ++- arch/xtensa/kernel/pci.c | 1 + drivers/pci/setup-res.c | 3 ++- drivers/pcmcia/rsrc_nonstatic.c | 3 ++- include/linux/ioport.h | 2 ++ include/linux/pci.h | 7 ++++--- kernel/resource.c | 3 ++- 16 files changed, 35 insertions(+), 13 deletions(-) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 51a8a4c4572a..11df411b1d18 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -125,6 +125,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final); resource_size_t pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index b5793e8fbdc1..5b9b4fcd0e54 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -560,7 +560,9 @@ char * __init pcibios_setup(char *str) * which might be mirrored at 0x0100-0x03ff.. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, + resource_size_t align) { struct pci_dev *dev = data; resource_size_t start = res->start; diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c index e6ab3f9ff5d8..1415f6e4e5ce 100644 --- a/arch/m68k/kernel/pcibios.c +++ b/arch/m68k/kernel/pcibios.c @@ -27,7 +27,9 @@ * which might be mirrored at 0x0100-0x03ff.. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, + resource_size_t align) { resource_size_t start = res->start; diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c index d2d68bac3d25..f4957c26efc7 100644 --- a/arch/mips/pci/pci-generic.c +++ b/arch/mips/pci/pci-generic.c @@ -22,7 +22,8 @@ * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; resource_size_t start = res->start; diff --git a/arch/mips/pci/pci-legacy.c b/arch/mips/pci/pci-legacy.c index d04b7c1294b6..817e97402afe 100644 --- a/arch/mips/pci/pci-legacy.c +++ b/arch/mips/pci/pci-legacy.c @@ -52,6 +52,7 @@ unsigned long pci_address_to_pio(phys_addr_t address) */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index cf285b17a5ae..f99b20795d5a 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -196,7 +196,9 @@ void __ref pcibios_init_bridge(struct pci_dev *dev) * than res->start. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t alignment) + const struct resource *empty_res, + resource_size_t size, + resource_size_t alignment) { resource_size_t mask, align, start = res->start; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index a7a2fb605971..e7bfa15da043 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1132,7 +1132,9 @@ static int skip_isa_ioresource_align(struct pci_dev *dev) * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, + resource_size_t align) { struct pci_dev *dev = data; resource_size_t start = res->start; diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 2a430722cbe4..39bd2adfc240 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -266,6 +266,7 @@ static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len) } resource_size_t pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align) { diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index a3903304f33f..7a0522316ee3 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -168,7 +168,9 @@ subsys_initcall(pcibios_init); * modulo 0x400. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, + resource_size_t align) { struct pci_dev *dev = data; struct pci_channel *hose = dev->sysdata; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index c4ec39ad276b..6fbd4b34c3f7 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -153,7 +153,8 @@ skip_isa_ioresource_align(struct pci_dev *dev) { */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; resource_size_t start = res->start; diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 62c900e400d6..64ccb7e0d92f 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -39,6 +39,7 @@ */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index bb2aef373d6f..c375e255c509 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -251,10 +251,11 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, */ resource_size_t __weak pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align) { - return res->start; + return res->start; } static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 0679dd434719..949e69921fe9 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -602,7 +602,8 @@ static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data, static resource_size_t pcmcia_align(void *align_data, const struct resource *res, - resource_size_t size, resource_size_t align) + const struct resource *empty_res, + resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; struct resource_map *m; diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 19d5e04564d9..3c73c9c0d4f7 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -202,6 +202,7 @@ enum { * typedef resource_alignf - Resource alignment callback * @data: Private data used by the callback * @res: Resource candidate range (an empty resource space) + * @empty_res: Empty resource range without alignment applied * @size: The minimum size of the empty space * @align: Alignment from the constraints * @@ -212,6 +213,7 @@ enum { */ typedef resource_size_t (*resource_alignf)(void *data, const struct resource *res, + const struct resource *empty_res, resource_size_t size, resource_size_t align); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1c270f1d5123..ac332ff9da9f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1206,9 +1206,10 @@ int __must_check pcibios_enable_device(struct pci_dev *, int mask); char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -resource_size_t pcibios_align_resource(void *, const struct resource *, - resource_size_t, - resource_size_t); +resource_size_t pcibios_align_resource(void *data, const struct resource *res, + const struct resource *empty_res, + resource_size_t size, + resource_size_t align); /* Generic PCI functions used internally */ diff --git a/kernel/resource.c b/kernel/resource.c index 1e2f1dfc0edd..1b8d3101bdc6 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -759,7 +759,8 @@ static int __find_resource_space(struct resource *root, struct resource *old, alloc.flags = avail.flags; if (alignf) { alloc.start = alignf(constraint->alignf_data, - &avail, size, constraint->align); + &avail, &tmp, + size, constraint->align); } else { alloc.start = avail.start; } From 66475b5dc4e4f88f1ed6f403067e08bd90286af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:26 +0200 Subject: [PATCH 102/165] resource: Rename 'tmp' variable to 'full_avail' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __find_resource_space() has variable called 'tmp'. Rename it to 'full_avail' to better indicate its purpose. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-4-ilpo.jarvinen@linux.intel.com --- kernel/resource.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/kernel/resource.c b/kernel/resource.c index 1b8d3101bdc6..8c5fcb30fc33 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -727,39 +727,39 @@ static int __find_resource_space(struct resource *root, struct resource *old, struct resource_constraint *constraint) { struct resource *this = root->child; - struct resource tmp = *new, avail, alloc; + struct resource full_avail = *new, avail, alloc; resource_alignf alignf = constraint->alignf; - tmp.start = root->start; + full_avail.start = root->start; /* * Skip past an allocated resource that starts at 0, since the assignment - * of this->start - 1 to tmp->end below would cause an underflow. + * of this->start - 1 to full_avail->end below would cause an underflow. */ if (this && this->start == root->start) { - tmp.start = (this == old) ? old->start : this->end + 1; + full_avail.start = (this == old) ? old->start : this->end + 1; this = this->sibling; } for(;;) { if (this) - tmp.end = (this == old) ? this->end : this->start - 1; + full_avail.end = (this == old) ? this->end : this->start - 1; else - tmp.end = root->end; + full_avail.end = root->end; - if (tmp.end < tmp.start) + if (full_avail.end < full_avail.start) goto next; - resource_clip(&tmp, constraint->min, constraint->max); - arch_remove_reservations(&tmp); + resource_clip(&full_avail, constraint->min, constraint->max); + arch_remove_reservations(&full_avail); /* Check for overflow after ALIGN() */ - avail.start = ALIGN(tmp.start, constraint->align); - avail.end = tmp.end; + avail.start = ALIGN(full_avail.start, constraint->align); + avail.end = full_avail.end; avail.flags = new->flags; - if (avail.start >= tmp.start) { + if (avail.start >= full_avail.start) { alloc.flags = avail.flags; if (alignf) { alloc.start = alignf(constraint->alignf_data, - &avail, &tmp, + &avail, &full_avail, size, constraint->align); } else { alloc.start = avail.start; @@ -777,7 +777,7 @@ next: if (!this || this->end == root->end) break; if (this != old) - tmp.start = this->end + 1; + full_avail.start = this->end + 1; this = this->sibling; } return -EBUSY; From 0734cb2412f5fdc06fac6c1e6f3046085a4fdf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:27 +0200 Subject: [PATCH 103/165] ARM/PCI: Remove unnecessary second application of align MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligning res->start by align inside pcibios_align_resource() is unnecessary because caller of pcibios_align_resource() is __find_resource_space() that aligns res->start with align before calling pcibios_align_resource(). Aligning by align in case of IORESOURCE_IO && start & 0x300 cannot ever result in changing start either because 0x300 bits would have not survived the earlier alignment if align was large enough to have an impact. Thus, remove the duplicated aligning from pcibios_align_resource(). Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260324165633.4583-5-ilpo.jarvinen@linux.intel.com --- arch/arm/kernel/bios32.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 5b9b4fcd0e54..cedb83a85dd9 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -571,8 +571,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, if (res->flags & IORESOURCE_IO && start & 0x300) start = (start + 0x3ff) & ~0x3ff; - start = (start + align - 1) & ~(align - 1); - host_bridge = pci_find_host_bridge(dev->bus); if (host_bridge->align_resource) From 4dd6e1aa35dcf616805eaf330bd731fd8f0da6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:28 +0200 Subject: [PATCH 104/165] m68k/PCI: Remove unnecessary second application of align MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligning res->start by align inside pcibios_align_resource() is unnecessary because caller of pcibios_align_resource() is __find_resource_space() that aligns res->start with align before calling pcibios_align_resource(). Aligning by align in case of IORESOURCE_IO && start & 0x300 cannot ever result in changing start either because 0x300 bits would have not survived the earlier alignment if align was large enough to have an impact. Thus, remove the duplicated aligning from pcibios_align_resource(). Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Acked-by: Greg Ungerer Link: https://patch.msgid.link/20260324165633.4583-6-ilpo.jarvinen@linux.intel.com --- arch/m68k/kernel/pcibios.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c index 1415f6e4e5ce..7e286ee1976b 100644 --- a/arch/m68k/kernel/pcibios.c +++ b/arch/m68k/kernel/pcibios.c @@ -36,8 +36,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, if ((res->flags & IORESOURCE_IO) && (start & 0x300)) start = (start + 0x3ff) & ~0x3ff; - start = (start + align - 1) & ~(align - 1); - return start; } From 3fa40d305ba185882479ee90ff71b9034622bf85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:29 +0200 Subject: [PATCH 105/165] MIPS: PCI: Remove unnecessary second application of align MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligning res->start by align inside pcibios_align_resource() is unnecessary because caller of pcibios_align_resource() is __find_resource_space() that aligns res->start with align before calling pcibios_align_resource(). Aligning by align in case of IORESOURCE_IO && start & 0x300 cannot ever result in changing start either because 0x300 bits would have not survived the earlier alignment if align was large enough to have an impact. Thus, remove the duplicated aligning from pcibios_align_resource(). Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260324165633.4583-7-ilpo.jarvinen@linux.intel.com --- arch/mips/pci/pci-generic.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c index f4957c26efc7..aaa1d6de8bef 100644 --- a/arch/mips/pci/pci-generic.c +++ b/arch/mips/pci/pci-generic.c @@ -32,8 +32,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, if (res->flags & IORESOURCE_IO && start & 0x300) start = (start + 0x3ff) & ~0x3ff; - start = (start + align - 1) & ~(align - 1); - host_bridge = pci_find_host_bridge(dev->bus); if (host_bridge->align_resource) From 38ec53e16fe51c427bd1c7ed50be2bcc3db4f01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:30 +0200 Subject: [PATCH 106/165] parisc/PCI: Clean up align handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caller of pcibios_align_resource() (__find_resource_space()) already aligns the start address by 'alignment' so aligning is only necessary if align > alignment. Change also to use ALIGN() instead of open-coding. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260324165633.4583-8-ilpo.jarvinen@linux.intel.com --- arch/parisc/kernel/pci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index f99b20795d5a..f50be1a63c4c 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -8,6 +8,7 @@ * Copyright (C) 1999-2001 Hewlett-Packard Company * Copyright (C) 1999-2001 Grant Grundler */ +#include #include #include #include @@ -200,7 +201,7 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t alignment) { - resource_size_t mask, align, start = res->start; + resource_size_t align, start = res->start; DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n", pci_name(((struct pci_dev *) data)), @@ -209,11 +210,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, /* If it's not IO, then it's gotta be MEM */ align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - - /* Align to largest of MIN or input size */ - mask = max(alignment, align) - 1; - start += mask; - start &= ~mask; + if (align > alignment) + start = ALIGN(start, align); return start; } From 8bbe8cec891f88dd3de8cae44812a884e10f1446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:31 +0200 Subject: [PATCH 107/165] PCI: Rename window_alignment() to pci_min_window_alignment() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit window_alignment() lacks prefix. Rename it to pci_min_window_alignment() in order to include the prefix and also add min to indicate the returned window alignment is the minimum PCI spec and arch allows. Also make it available in drivers/pci/pci.h as upcoming changes will need to call it from outside of setup-bus.c. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-9-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.h | 3 +++ drivers/pci/setup-bus.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..2edb03c1c6b9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1053,6 +1053,9 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, return resource_alignment(res); } +resource_size_t pci_min_window_alignment(struct pci_bus *bus, + unsigned long type); + void pci_acs_init(struct pci_dev *dev); void pci_enable_acs(struct pci_dev *dev); #ifdef CONFIG_PCI_QUIRKS diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9506845c112c..f853003d3fa6 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1035,7 +1035,7 @@ resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, #define PCI_P2P_DEFAULT_IO_ALIGN SZ_4K #define PCI_P2P_DEFAULT_IO_ALIGN_1K SZ_1K -static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) +resource_size_t pci_min_window_alignment(struct pci_bus *bus, unsigned long type) { resource_size_t align = 1, arch_align; @@ -1084,7 +1084,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size, if (resource_assigned(b_res)) return; - min_align = window_alignment(bus, IORESOURCE_IO); + min_align = pci_min_window_alignment(bus, IORESOURCE_IO); list_for_each_entry(dev, &bus->devices, bus_list) { struct resource *r; @@ -1339,7 +1339,7 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res, } } - win_align = window_alignment(bus, b_res->flags); + win_align = pci_min_window_alignment(bus, b_res->flags); min_align = calculate_head_align(aligns, max_order); min_align = max(min_align, win_align); size0 = calculate_memsize(size, realloc_head ? 0 : add_size, From 9036bd0efcb6162a77f3bf9bacbafba7686c7275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:32 +0200 Subject: [PATCH 108/165] PCI: Align head space better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a bridge window contains big and small resource(s), the small resource(s) may not amount to the half of the size of the big resource which would allow calculate_head_align() to shrink the head alignment. This results in always placing the small resource(s) after the big resource. In general, it would be good to be able to place the small resource(s) before the big resource to achieve better utilization of the address space. In the cases where the large resource can only fit at the end of the window, it is even required. However, carrying the information over from pbus_size_mem() and calculate_head_align() to __pci_assign_resource() and pcibios_align_resource() is not easy with the current data structures. A somewhat hacky way to move the non-aligning tail part to the head is possible within pcibios_align_resource(). The free space between the start of the free space span and the aligned start address can be compared with the non-aligning remainder of the size. If the free space is larger than the remainder, placing the remainder before the start address is possible. This relocation should generally work, because PCI resources consist only power-of-2 atoms. Various arch requirements may still need to override the relocation, so the relocation is only applied selectively in such cases. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221205 Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-10-ilpo.jarvinen@linux.intel.com --- arch/arm/kernel/bios32.c | 3 +++ arch/m68k/kernel/pcibios.c | 4 ++++ arch/mips/pci/pci-generic.c | 3 +++ arch/mips/pci/pci-legacy.c | 2 ++ arch/parisc/kernel/pci.c | 3 +++ arch/powerpc/kernel/pci-common.c | 2 ++ arch/sh/drivers/pci/pci.c | 2 ++ arch/x86/pci/i386.c | 2 ++ arch/xtensa/kernel/pci.c | 2 ++ drivers/pci/setup-res.c | 39 +++++++++++++++++++++++++++++++- include/linux/pci.h | 5 ++++ kernel/resource.c | 2 +- 12 files changed, 67 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index cedb83a85dd9..ac0e890510da 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -577,6 +577,9 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return host_bridge->align_resource(dev, res, start, size, align); + if (res->flags & IORESOURCE_MEM) + return pci_align_resource(dev, res, empty_res, size, align); + return start; } diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c index 7e286ee1976b..7a9e60df79c5 100644 --- a/arch/m68k/kernel/pcibios.c +++ b/arch/m68k/kernel/pcibios.c @@ -31,11 +31,15 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { + struct pci_dev *dev = data; resource_size_t start = res->start; if ((res->flags & IORESOURCE_IO) && (start & 0x300)) start = (start + 0x3ff) & ~0x3ff; + if (res->flags & IORESOURCE_MEM) + return pci_align_resource(dev, res, empty_res, size, align); + return start; } diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c index aaa1d6de8bef..c2e23d0c1d77 100644 --- a/arch/mips/pci/pci-generic.c +++ b/arch/mips/pci/pci-generic.c @@ -38,6 +38,9 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return host_bridge->align_resource(dev, res, start, size, align); + if (res->flags & IORESOURCE_MEM) + return pci_align_resource(dev, res, empty_res, size, align); + return start; } diff --git a/arch/mips/pci/pci-legacy.c b/arch/mips/pci/pci-legacy.c index 817e97402afe..dae6dafdd6e0 100644 --- a/arch/mips/pci/pci-legacy.c +++ b/arch/mips/pci/pci-legacy.c @@ -70,6 +70,8 @@ pcibios_align_resource(void *data, const struct resource *res, if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; } else if (res->flags & IORESOURCE_MEM) { + start = pci_align_resource(dev, res, empty_res, size, align); + /* Make sure we start at our min on all hoses */ if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start) start = PCIBIOS_MIN_MEM + hose->mem_resource->start; diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index f50be1a63c4c..b8007c7400d4 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -201,6 +201,7 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t alignment) { + struct pci_dev *dev = data; resource_size_t align, start = res->start; DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n", @@ -212,6 +213,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; if (align > alignment) start = ALIGN(start, align); + else + start = pci_align_resource(dev, res, empty_res, size, alignment); return start; } diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index e7bfa15da043..8efe95a0c4ff 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1144,6 +1144,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; + } else if (res->flags & IORESOURCE_MEM) { + start = pci_align_resource(dev, res, empty_res, size, align); } return start; diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 7a0522316ee3..878a27a1acfb 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -185,6 +185,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, */ if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; + } else if (res->flags & IORESOURCE_MEM) { + start = pci_align_resource(dev, res, empty_res, size, align); } return start; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 6fbd4b34c3f7..e2de26b82940 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -165,6 +165,8 @@ pcibios_align_resource(void *data, const struct resource *res, if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; } else if (res->flags & IORESOURCE_MEM) { + start = pci_align_resource(dev, res, empty_res, size, align); + /* The low 1MB range is reserved for ISA cards */ if (start < BIOS_END) start = BIOS_END; diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 64ccb7e0d92f..305031551136 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -54,6 +54,8 @@ pcibios_align_resource(void *data, const struct resource *res, if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; + } else if (res->flags & IORESOURCE_MEM) { + start = pci_align_resource(dev, res, empty_res, size, align); } return start; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index c375e255c509..fbc05cda96ee 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -244,6 +244,41 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, return 0; } +/* + * For mem bridge windows, try to relocate tail remainder space to space + * before res->start if there's enough free space there. This enables + * tighter packing for resources. + */ +resource_size_t pci_align_resource(struct pci_dev *dev, + const struct resource *res, + const struct resource *empty_res, + resource_size_t size, + resource_size_t align) +{ + resource_size_t remainder, start_addr; + + if (!(res->flags & IORESOURCE_MEM)) + return res->start; + + if (IS_ALIGNED(size, align)) + return res->start; + + remainder = size - ALIGN_DOWN(size, align); + /* Don't mess with size that doesn't align with window size granularity */ + if (!IS_ALIGNED(remainder, pci_min_window_alignment(dev->bus, res->flags))) + return res->start; + /* Try to place remainder that doesn't fill align before */ + if (res->start < remainder) + return res->start; + start_addr = res->start - remainder; + if (empty_res->start > start_addr) + return res->start; + + pci_dbg(dev, "%pR: moving candidate start address below align to %llx\n", + res, (unsigned long long)start_addr); + return start_addr; +} + /* * We don't have to worry about legacy ISA devices, so nothing to do here. * This is marked as __weak because multiple architectures define it; it should @@ -255,7 +290,9 @@ resource_size_t __weak pcibios_align_resource(void *data, resource_size_t size, resource_size_t align) { - return res->start; + struct pci_dev *dev = data; + + return pci_align_resource(dev, res, empty_res, size, align); } static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, diff --git a/include/linux/pci.h b/include/linux/pci.h index ac332ff9da9f..cedf948dc614 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1210,6 +1210,11 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, const struct resource *empty_res, resource_size_t size, resource_size_t align); +resource_size_t pci_align_resource(struct pci_dev *dev, + const struct resource *res, + const struct resource *empty_res, + resource_size_t size, + resource_size_t align); /* Generic PCI functions used internally */ diff --git a/kernel/resource.c b/kernel/resource.c index 8c5fcb30fc33..d02a53fb95d8 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -766,7 +766,7 @@ static int __find_resource_space(struct resource *root, struct resource *old, } alloc.end = alloc.start + size - 1; if (alloc.start <= alloc.end && - __resource_contains_unbound(&avail, &alloc)) { + __resource_contains_unbound(&full_avail, &alloc)) { new->start = alloc.start; new->end = alloc.end; return 0; From 8cb081667377709f4924ab6b3a88a0d7a761fe91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 24 Mar 2026 18:56:33 +0200 Subject: [PATCH 109/165] PCI: Fix alignment calculation for resource size larger than align MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit bc75c8e50711 ("PCI: Rewrite bridge window head alignment function") did not use if (r_size <= align) check from pbus_size_mem() for the new head alignment bookkeeping structure (aligns2[]). In some configurations, this can result in producing a gap into the bridge window which the resource larger than its alignment cannot fill. The old alignment calculation algorithm was removed by the subsequent commit 3958bf16e2fe ("PCI: Stop over-estimating bridge window size") which renamed the aligns2[] array leaving only aligns[] array. Add the if (r_size <= align) check back to avoid this problem. Fixes: bc75c8e50711 ("PCI: Rewrite bridge window head alignment function") Reported-by: Guenter Roeck Closes: https://lore.kernel.org/all/b05a6f14-979d-42c9-924c-d8408cb12ae7@roeck-us.net/ Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Xifer Link: https://patch.msgid.link/20260324165633.4583-11-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-bus.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f853003d3fa6..4cf120ebe5ad 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1333,7 +1333,14 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res, r_size = resource_size(r); size += max(r_size, align); - aligns[order] += align; + /* + * If resource's size is larger than its alignment, + * some configurations result in an unwanted gap in + * the head space that the larger resource cannot + * fill. + */ + if (r_size <= align) + aligns[order] += align; if (order > max_order) max_order = order; } From 362a4549f2acfb03390ddfcd91041e65a11a739f Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 6 Mar 2026 12:14:41 +0900 Subject: [PATCH 110/165] NTB: core: Add .get_dma_dev() callback to ntb_dev_ops Some NTB implementations are backed by a PCI function that is not the right struct device to use with DMA API helpers (e.g. due to IOMMU topology, or because the NTB device is virtual). Add an optional .get_dma_dev() callback to struct ntb_dev_ops and provide a helper, ntb_get_dma_dev(), so NTB clients can use the appropriate struct device for DMA allocations and mappings. If the callback is not implemented, ntb_get_dma_dev() returns the current default (ntb->dev.parent). Drivers that implement .get_dma_dev() must return a non-NULL device. Suggested-by: Frank Li Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam [bhelgaas: format doc] Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260306031443.1911860-2-den@valinux.co.jp --- include/linux/ntb.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/linux/ntb.h b/include/linux/ntb.h index 8ff9d663096b..879c3e89e026 100644 --- a/include/linux/ntb.h +++ b/include/linux/ntb.h @@ -256,6 +256,7 @@ static inline int ntb_ctx_ops_is_valid(const struct ntb_ctx_ops *ops) * @msg_clear_mask: See ntb_msg_clear_mask(). * @msg_read: See ntb_msg_read(). * @peer_msg_write: See ntb_peer_msg_write(). + * @get_dma_dev: See ntb_get_dma_dev(). */ struct ntb_dev_ops { int (*port_number)(struct ntb_dev *ntb); @@ -329,6 +330,7 @@ struct ntb_dev_ops { int (*msg_clear_mask)(struct ntb_dev *ntb, u64 mask_bits); u32 (*msg_read)(struct ntb_dev *ntb, int *pidx, int midx); int (*peer_msg_write)(struct ntb_dev *ntb, int pidx, int midx, u32 msg); + struct device *(*get_dma_dev)(struct ntb_dev *ntb); }; static inline int ntb_dev_ops_is_valid(const struct ntb_dev_ops *ops) @@ -391,6 +393,8 @@ static inline int ntb_dev_ops_is_valid(const struct ntb_dev_ops *ops) /* !ops->msg_clear_mask == !ops->msg_count && */ !ops->msg_read == !ops->msg_count && !ops->peer_msg_write == !ops->msg_count && + + /* ops->get_dma_dev is optional */ 1; } @@ -1563,6 +1567,26 @@ static inline int ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx, return ntb->ops->peer_msg_write(ntb, pidx, midx, msg); } +/** + * ntb_get_dma_dev() - get the device to use for DMA allocations/mappings + * @ntb: NTB device context. + * + * Return a struct device suitable for DMA API allocations and mappings. + * This is typically the parent of the NTB device, but may be overridden by a + * driver by implementing .get_dma_dev(). + * + * Drivers that implement .get_dma_dev() must return a non-NULL pointer. + * + * Return: device pointer to use for DMA operations. + */ +static inline struct device *ntb_get_dma_dev(struct ntb_dev *ntb) +{ + if (!ntb->ops->get_dma_dev) + return ntb->dev.parent; + + return ntb->ops->get_dma_dev(ntb); +} + /** * ntb_peer_resource_idx() - get a resource index for a given peer idx * @ntb: NTB device context. From 852b94ff92cbe26a0dbb15eed9474f5493767f3b Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 6 Mar 2026 12:14:42 +0900 Subject: [PATCH 111/165] NTB: ntb_transport: Use ntb_get_dma_dev() for DMA buffers ntb_transport currently uses ndev->pdev->dev for coherent allocations and frees. Switch the coherent buffer allocation/free paths to use ntb_get_dma_dev(), so ntb_transport can work with NTB implementations where the NTB PCI function is not the right device to use for DMA mappings. Suggested-by: Frank Li Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260306031443.1911860-3-den@valinux.co.jp --- drivers/ntb/ntb_transport.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 78e02fe6caba..a67cc26e47b9 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -759,13 +759,13 @@ static void ntb_transport_msi_desc_changed(void *data) static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) { struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; - struct pci_dev *pdev = nt->ndev->pdev; + struct device *dma_dev = ntb_get_dma_dev(nt->ndev); if (!mw->virt_addr) return; ntb_mw_clear_trans(nt->ndev, PIDX, num_mw); - dma_free_coherent(&pdev->dev, mw->alloc_size, + dma_free_coherent(dma_dev, mw->alloc_size, mw->alloc_addr, mw->dma_addr); mw->xlat_size = 0; mw->buff_size = 0; @@ -835,7 +835,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, resource_size_t size) { struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; - struct pci_dev *pdev = nt->ndev->pdev; + struct device *dma_dev = ntb_get_dma_dev(nt->ndev); size_t xlat_size, buff_size; resource_size_t xlat_align; resource_size_t xlat_align_size; @@ -864,12 +864,12 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, mw->buff_size = buff_size; mw->alloc_size = buff_size; - rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align); + rc = ntb_alloc_mw_buffer(mw, dma_dev, xlat_align); if (rc) { mw->alloc_size *= 2; - rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align); + rc = ntb_alloc_mw_buffer(mw, dma_dev, xlat_align); if (rc) { - dev_err(&pdev->dev, + dev_err(dma_dev, "Unable to alloc aligned MW buff\n"); mw->xlat_size = 0; mw->buff_size = 0; @@ -882,7 +882,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, rc = ntb_mw_set_trans(nt->ndev, PIDX, num_mw, mw->dma_addr, mw->xlat_size); if (rc) { - dev_err(&pdev->dev, "Unable to set mw%d translation", num_mw); + dev_err(dma_dev, "Unable to set mw%d translation", num_mw); ntb_free_mw(nt, num_mw); return -EIO; } From 70becc1a9b453ce04f97507585afc2cf47e67b11 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 6 Mar 2026 12:14:43 +0900 Subject: [PATCH 112/165] PCI: endpoint: pci-epf-vntb: Implement .get_dma_dev() When vNTB is used as a PCI endpoint function, the NTB device is backed by a virtual PCI function. For DMA API allocations and mappings, NTB clients must use the device that is associated with the IOMMU domain. Implement ntb_dev_ops->get_dma_dev() for pci-epf-vntb and return the EPC parent device. Suggested-by: Frank Li Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260306031443.1911860-4-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 805353528967..2256c3062b1a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1428,6 +1428,14 @@ static int vntb_epf_link_disable(struct ntb_dev *ntb) return 0; } +static struct device *vntb_epf_get_dma_dev(struct ntb_dev *ndev) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct pci_epc *epc = ntb->epf->epc; + + return epc->dev.parent; +} + static const struct ntb_dev_ops vntb_epf_ops = { .mw_count = vntb_epf_mw_count, .spad_count = vntb_epf_spad_count, @@ -1449,6 +1457,7 @@ static const struct ntb_dev_ops vntb_epf_ops = { .db_clear_mask = vntb_epf_db_clear_mask, .db_clear = vntb_epf_db_clear, .link_disable = vntb_epf_link_disable, + .get_dma_dev = vntb_epf_get_dma_dev, }; static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) From 5b6471fc72a42e6110adca54f46fd2c287dc49d4 Mon Sep 17 00:00:00 2001 From: Simon Richter Date: Sun, 8 Mar 2026 02:35:37 +0900 Subject: [PATCH 113/165] PCI/VGA: Fail pci_set_vga_state() if VGA decoding not supported PCI bridges are allowed to refuse activating VGA decoding, by simply ignoring attempts to set the bit that enables it, so after setting the bit, read it back to verify. One example of such a bridge is the root bridge in IBM PowerNV, but this is also useful for GPU passthrough into virtual machines, where it is difficult to set up routing for legacy IO through IOMMU. Signed-off-by: Simon Richter [bhelgaas: subject, add comment about VGA Enable writability] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260307173538.763188-5-Simon.Richter@hogyros.de --- drivers/pci/pci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..52eb3ef0f487 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -6197,6 +6197,18 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode, cmd &= ~PCI_BRIDGE_CTL_VGA; pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, cmd); + + + /* + * VGA Enable may not be writable if bridge doesn't + * support it. + */ + if (decode) { + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, + &cmd); + if (!(cmd & PCI_BRIDGE_CTL_VGA)) + return -EIO; + } } bus = bus->parent; } From 1ab4a3c805084d752ec571efc78272295a9f2f74 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 27 Mar 2026 10:56:43 +0100 Subject: [PATCH 114/165] PCI/AER: Stop ruling out unbound devices as error source When searching for the error source, the AER driver rules out devices whose enable_cnt is zero. This was introduced in 2009 by commit 28eb27cf0839 ("PCI AER: support invalid error source IDs") without providing a rationale. Drivers typically call pci_enable_device() on probe, hence the enable_cnt check essentially filters out unbound devices. At the time of the commit, drivers had to opt in to AER by calling pci_enable_pcie_error_reporting() and so any AER-enabled device could be assumed to be bound to a driver. The check thus made sense because it allowed skipping config space accesses to devices which were known not to be the error source. But since 2022, AER is universally enabled on all devices when they are enumerated, cf. commit f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"). Errors may very well be reported by unbound devices, e.g. due to link instability. By ruling them out as error source, errors reported by them are neither logged nor cleared. When they do get bound and another error occurs, the earlier error is reported together with the new error, which may confuse users. Stop doing so. Fixes: f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native") Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Stefan Roese Cc: stable@vger.kernel.org # v6.0+ Link: https://patch.msgid.link/734338c2e8b669db5a5a3b45d34131b55ffebfca.1774605029.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index d916378bc707..c4fd9c0b2a54 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1041,8 +1041,6 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) * 3) There are multiple errors and prior ID comparing fails; * We check AER status registers to find possible reporter. */ - if (atomic_read(&dev->enable_cnt) == 0) - return false; /* Check if AER is enabled */ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, ®16); From 47e2a338222c902dc44f4f6e29eb236a68600ef4 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 16 Mar 2026 20:28:03 -0400 Subject: [PATCH 115/165] PCI/sysfs: Suppress FW_BUG warning when NUMA node already matches The numa_node sysfs interface allows users to manually override a PCI device's NUMA node assignment. Currently, every write triggers a FW_BUG warning and taints the kernel, even when writing the same value that is already set. Check if the requested node is already assigned to the device. If it matches, return success immediately without tainting the kernel or printing a warning. Signed-off-by: Li RongQing Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260317002803.2353-1-lirongqing@baidu.com --- drivers/pci/pci-sysfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 16eaaf749ba9..dd7c9e5f993e 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -378,6 +378,9 @@ static ssize_t numa_node_store(struct device *dev, if (node != NUMA_NO_NODE && !node_online(node)) return -EINVAL; + if (node == dev->numa_node) + return count; + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); pci_alert(pdev, FW_BUG "Overriding NUMA node to %d. Contact your vendor for updates.", node); From 97970e7c694356e3386a10e3b936d61eafd06bce Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Wed, 18 Mar 2026 10:04:49 -0700 Subject: [PATCH 116/165] PCI/DPC: Log AER error info for DPC/EDR uncorrectable errors aer_print_error() skips printing if ratelimit_print[i] is not set. In the native AER path, ratelimit_print is initialized by add_error_device() during source device discovery, and is set to 1 for fatal errors to bypass rate limiting since fatal errors should always be logged. The DPC/EDR path uses the DPC-capable port as the error source and reads its AER uncorrectable error status registers directly in dpc_get_aer_uncorrect_severity(). Since it does not go through add_error_device(), ratelimit_print[0] is left uninitialized and zero. As a result, aer_print_error() silently drops all AER error messages for DPC/EDR triggered events. Set ratelimit_print[0] to 1 to bypass rate limiting and always print AER logs for uncorrectable errors detected by the DPC port. Fixes: a57f2bfb4a58 ("PCI/AER: Ratelimit correctable and non-fatal error logging") Co-developed-by: Goudar Manjunath Ramanagouda Signed-off-by: Goudar Manjunath Ramanagouda Signed-off-by: Kuppuswamy Sathyanarayanan [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260318170449.2733581-1-sathyanarayanan.kuppuswamy@linux.intel.com --- drivers/pci/pcie/dpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index f028bc795f19..2b779bd1d861 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -256,6 +256,7 @@ static int dpc_get_aer_uncorrect_severity(struct pci_dev *dev, info->dev[0] = dev; info->error_dev_num = 1; + info->ratelimit_print[0] = 1; return 1; } From 7010f13acd3828056e50badb5138bf9ca21ddd8f Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Mon, 30 Mar 2026 22:45:49 +0100 Subject: [PATCH 117/165] PCI: Clean up dead code in Kconfig There is already an 'if PCI' condition wrapping several config options, e.g., PCI_DOMAINS and VGA_ARB, making the 'depends on PCI' statement for each of these a duplicate dependency (dead code). Leave the outer 'if PCI...endif' and remove the individual 'depends on PCI' statement from each option. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260330214549.16157-1-julianbraha@gmail.com --- drivers/pci/Kconfig | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e3f848ffb52a..33c88432b728 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -31,7 +31,6 @@ if PCI config PCI_DOMAINS bool - depends on PCI config PCI_DOMAINS_GENERIC bool @@ -255,7 +254,7 @@ config PCI_DYNAMIC_OF_NODES choice prompt "PCI Express hierarchy optimization setting" default PCIE_BUS_DEFAULT - depends on PCI && EXPERT + depends on EXPERT help MPS (Max Payload Size) and MRRS (Max Read Request Size) are PCIe device parameters that affect performance and the ability to @@ -272,20 +271,17 @@ choice config PCIE_BUS_TUNE_OFF bool "Tune Off" - depends on PCI help Use the BIOS defaults; don't touch MPS at all. This is the same as booting with 'pci=pcie_bus_tune_off'. config PCIE_BUS_DEFAULT bool "Default" - depends on PCI help Default choice; ensure that the MPS matches upstream bridge. config PCIE_BUS_SAFE bool "Safe" - depends on PCI help Use largest MPS that boot-time devices support. If you have a closed system with no possibility of adding new devices, this @@ -294,7 +290,6 @@ config PCIE_BUS_SAFE config PCIE_BUS_PERFORMANCE bool "Performance" - depends on PCI help Use MPS and MRRS for best performance. Ensure that a given device's MPS is no larger than its parent MPS, which allows us to @@ -303,7 +298,6 @@ config PCIE_BUS_PERFORMANCE config PCIE_BUS_PEER2PEER bool "Peer2peer" - depends on PCI help Set MPS = 128 for all devices. MPS configuration effected by the other options could cause the MPS on one root port to be @@ -317,7 +311,7 @@ endchoice config VGA_ARB bool "VGA Arbitration" if EXPERT default y - depends on (PCI && !S390) + depends on !S390 select SCREEN_INFO if X86 help Some "legacy" VGA devices implemented on PCI typically have the same @@ -340,4 +334,4 @@ source "drivers/pci/endpoint/Kconfig" source "drivers/pci/switch/Kconfig" source "drivers/pci/pwrctrl/Kconfig" -endif +endif # PCI From 445588a3b18bb0702d746cb61f7a443639027651 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Wed, 1 Apr 2026 10:30:48 +0800 Subject: [PATCH 118/165] PCI: dwc: Fix type mismatch for kstrtou32_from_user() return value kstrtou32_from_user() returns int, but the return value was stored in a u32 variable 'val', risking sign loss. Use a dedicated int variable to correctly handle the return code. Fixes: 4fbfa17f9a07 ("PCI: dwc: Add debugfs based Silicon Debug support for DWC") Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260401023048.4182452-1-18255117159@163.com --- .../controller/dwc/pcie-designware-debugfs.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 2efddb21b2b2..d0884253be97 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -258,10 +258,11 @@ static ssize_t lane_detect_write(struct file *file, const char __user *buf, struct dw_pcie *pci = file->private_data; struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; u32 lane, val; + int ret; - val = kstrtou32_from_user(buf, count, 0, &lane); - if (val) - return val; + ret = kstrtou32_from_user(buf, count, 0, &lane); + if (ret) + return ret; val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); val &= ~(LANE_SELECT); @@ -397,10 +398,11 @@ static ssize_t counter_enable_write(struct file *file, const char __user *buf, struct dw_pcie *pci = pdata->pci; struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; u32 val, enable; + int ret; - val = kstrtou32_from_user(buf, count, 0, &enable); - if (val) - return val; + ret = kstrtou32_from_user(buf, count, 0, &enable); + if (ret) + return ret; mutex_lock(&rinfo->reg_event_lock); set_event_number(pdata, pci, rinfo); @@ -458,10 +460,11 @@ static ssize_t counter_lane_write(struct file *file, const char __user *buf, struct dw_pcie *pci = pdata->pci; struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; u32 val, lane; + int ret; - val = kstrtou32_from_user(buf, count, 0, &lane); - if (val) - return val; + ret = kstrtou32_from_user(buf, count, 0, &lane); + if (ret) + return ret; mutex_lock(&rinfo->reg_event_lock); set_event_number(pdata, pci, rinfo); From 9c55d0eb4e9d1bf229d691363fb0641a9b05d904 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Fri, 20 Mar 2026 23:01:39 +0900 Subject: [PATCH 119/165] misc: pci_endpoint_test: Use -EINVAL for small subrange size The sub_size check ensures that each subrange is large enough for 32-bit accesses. Subranges smaller than sizeof(u32) do not satisfy this assumption, so this is a local sanity check rather than a resource exhaustion case. Return -EINVAL instead of -ENOSPC for this case. Suggested-by: Niklas Cassel Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260320140139.2415480-1-den@valinux.co.jp --- drivers/misc/pci_endpoint_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 55e128ed82f0..496c8f509590 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -547,7 +547,7 @@ static int pci_endpoint_test_bar_subrange(struct pci_endpoint_test *test, sub_size = bar_size / nsub; if (sub_size < sizeof(u32)) { - ret = -ENOSPC; + ret = -EINVAL; goto out_clear; } From 03ec922f00250817a11b6b829601932d5f777998 Mon Sep 17 00:00:00 2001 From: Gerd Bayer Date: Mon, 30 Mar 2026 15:09:44 +0200 Subject: [PATCH 120/165] PCI: Do not enable AtomicOps by RCiEPs Since Root Complex Integrated Endpoints (RCiEPs) attach to a bus that has no bridge device describing the Root Port, the capability to complete AtomicOps requests cannot be determined with PCIe methods. Change default of pci_enable_atomic_ops_to_root() to not enable AtomicOps requests on RCiEPs. As far as we know, there are no RCiEPs that need AtomicOps (see Link below). Signed-off-by: Gerd Bayer Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260330-fix_pciatops-v7-1-f601818417e8@linux.ibm.com --- drivers/pci/pci.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8479c2e1f74f..135e5b591df4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3692,15 +3692,14 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) /* * Per PCIe r4.0, sec 6.15, endpoints and root ports may be - * AtomicOp requesters. For now, we only support endpoints as - * requesters and root ports as completers. No endpoints as + * AtomicOp requesters. For now, we only support (legacy) endpoints + * as requesters and root ports as completers. No endpoints as * completers, and no peer-to-peer. */ switch (pci_pcie_type(dev)) { case PCI_EXP_TYPE_ENDPOINT: case PCI_EXP_TYPE_LEG_END: - case PCI_EXP_TYPE_RC_END: break; default: return -EINVAL; From 1ae8c4ce157037e266184064a182af9ef9af278b Mon Sep 17 00:00:00 2001 From: Gerd Bayer Date: Mon, 30 Mar 2026 15:09:45 +0200 Subject: [PATCH 121/165] PCI: Enable AtomicOps only if Root Port supports them When inspecting the config space of a Connect-X physical function in an s390 system after it was initialized by the mlx5_core device driver, we found the function to be enabled to request AtomicOps despite the Root Port lacking support for completing them: 00:00.1 Ethernet controller: Mellanox Technologies MT2894 Family [ConnectX-6 Lx] Subsystem: Mellanox Technologies Device 0002 DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis- AtomicOpsCtl: ReqEn+ On s390 and many virtualized guests, the Endpoint is visible but the Root Port is not. In this case, pci_enable_atomic_ops_to_root() previously enabled AtomicOps in the Endpoint even though it can't tell whether the Root Port supports them as a completer. Change pci_enable_atomic_ops_to_root() to fail if there's no Root Port or the Root Port doesn't support AtomicOps. Fixes: 430a23689dea ("PCI: Add pci_enable_atomic_ops_to_root()") Reported-by: Alexander Schmidt Signed-off-by: Gerd Bayer [bhelgaas: commit log, check RP first to simplify flow] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260330-fix_pciatops-v7-2-f601818417e8@linux.ibm.com --- drivers/pci/pci.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 135e5b591df4..3a4ba587042b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3675,8 +3675,7 @@ void pci_acs_init(struct pci_dev *dev) */ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) { - struct pci_bus *bus = dev->bus; - struct pci_dev *bridge; + struct pci_dev *root, *bridge; u32 cap, ctl2; /* @@ -3705,35 +3704,35 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) return -EINVAL; } - while (bus->parent) { - bridge = bus->self; + root = pcie_find_root_port(dev); + if (!root) + return -EINVAL; - pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap); + pcie_capability_read_dword(root, PCI_EXP_DEVCAP2, &cap); + if ((cap & cap_mask) != cap_mask) + return -EINVAL; + bridge = pci_upstream_bridge(dev); + while (bridge != root) { switch (pci_pcie_type(bridge)) { - /* Ensure switch ports support AtomicOp routing */ case PCI_EXP_TYPE_UPSTREAM: - case PCI_EXP_TYPE_DOWNSTREAM: - if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE)) - return -EINVAL; - break; - - /* Ensure root port supports all the sizes we care about */ - case PCI_EXP_TYPE_ROOT_PORT: - if ((cap & cap_mask) != cap_mask) - return -EINVAL; - break; - } - - /* Ensure upstream ports don't block AtomicOps on egress */ - if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) { + /* Upstream ports must not block AtomicOps on egress */ pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2, &ctl2); if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK) return -EINVAL; + fallthrough; + + /* All switch ports need to route AtomicOps */ + case PCI_EXP_TYPE_DOWNSTREAM: + pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, + &cap); + if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE)) + return -EINVAL; + break; } - bus = bus->parent; + bridge = pci_upstream_bridge(bridge); } pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, From 8e69214402397fcbb17f25ac5acb178195aca4bc Mon Sep 17 00:00:00 2001 From: Gerd Bayer Date: Mon, 30 Mar 2026 15:09:46 +0200 Subject: [PATCH 122/165] PCI: Update PCIe spec references for AtomicOps Point to the relevant sections in the most recent release 7.0 of the PCIe spec. Text has mostly just moved around without any semantic change. Signed-off-by: Gerd Bayer Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260330-fix_pciatops-v7-3-f601818417e8@linux.ibm.com --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3a4ba587042b..fde970c78413 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3679,7 +3679,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) u32 cap, ctl2; /* - * Per PCIe r5.0, sec 9.3.5.10, the AtomicOp Requester Enable bit + * Per PCIe r7.0, sec 7.5.3.16, the AtomicOp Requester Enable bit * in Device Control 2 is reserved in VFs and the PF value applies * to all associated VFs. */ @@ -3690,7 +3690,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) return -EINVAL; /* - * Per PCIe r4.0, sec 6.15, endpoints and root ports may be + * Per PCIe r7.0, sec 6.15, endpoints and root ports may be * AtomicOp requesters. For now, we only support (legacy) endpoints * as requesters and root ports as completers. No endpoints as * completers, and no peer-to-peer. From 3a4e8302e72f83fd5cc8a916fc6f5c8fe5c8690e Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 31 Mar 2026 16:52:52 +0800 Subject: [PATCH 123/165] PCI: imx6: Keep Root Port MSI capability with iMSI-RX to work around hardware bug On NXP i.MX7D, i.MX8MM, and i.MX8MQ chipsets, MSIs from the endpoints won't be received by the iMSI-RX MSI controller if the Root Port MSI capability is disabled. Even though the Root Port MSIs won't be received by the iMSI-RX controller due to design, these chipsets have some weird hardware bug that prevents the endpoint MSIs from reaching when the Root Port MSI capability is disabled. Hence, introduce a new flag, 'dw_pcie_rp::keep_rp_msi_en', set it for the above mentioned SoCs, and always keep the Root Port MSI capability when this flag is set. Note that by keeping Root Port MSI capability, Root Port MSIs such as AER, PME and others won't be received by default. So users need to use workarounds such as passing 'pcie_pme=nomsi' cmdline param. Fixes: f5cd8a929c825 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller") Suggested-by: Manivannan Sadhasivam Signed-off-by: Richard Zhu [mani: commit log] Signed-off-by: Manivannan Sadhasivam [bhelgaas: fix typos] Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260331085252.1243108-1-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 7 +++++++ drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/dwc/pcie-designware.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 24c373020adc..a1da72dd85b0 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -117,6 +117,8 @@ enum imx_pcie_variants { #define IMX_PCIE_FLAG_HAS_LUT BIT(10) #define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11) #define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12) +/* Preserve MSI capability for platforms that require it */ +#define IMX_PCIE_FLAG_KEEP_MSI_CAP BIT(13) #define imx_check_flag(pci, val) (pci->drvdata->flags & val) @@ -1828,6 +1830,8 @@ static int imx_pcie_probe(struct platform_device *pdev) } else { if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY)) pci->pp.skip_l23_ready = true; + if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP)) + pci->pp.keep_rp_msi_en = true; pci->pp.use_atu_msg = true; ret = dw_pcie_host_init(&pci->pp); if (ret < 0) @@ -1907,6 +1911,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX7D] = { .variant = IMX7D, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_HAS_PHY_RESET, @@ -1919,6 +1924,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX8MQ] = { .variant = IMX8MQ, .flags = IMX_PCIE_FLAG_HAS_APP_RESET | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_PHY_RESET | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx8mq-iomuxc-gpr", @@ -1933,6 +1939,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX8MM] = { .variant = IMX8MM, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_PHYDRV | IMX_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mm-iomuxc-gpr", diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 6ae6189e9b8a..ded5462deae7 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1171,7 +1171,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) * the MSI and MSI-X capabilities of the Root Port to allow the drivers * to fall back to INTx instead. */ - if (pp->use_imsi_rx) { + if (pp->use_imsi_rx && !pp->keep_rp_msi_en) { dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI); dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index ae6389dd9caa..b12c5334552c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -421,6 +421,7 @@ struct dw_pcie_host_ops { struct dw_pcie_rp { bool use_imsi_rx:1; + bool keep_rp_msi_en:1; bool cfg0_io_shared:1; u64 cfg0_base; void __iomem *va_cfg0_base; From c54d5f5b33990f2649c20f35407f340bcadb8a53 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 24 Mar 2026 01:57:59 +0800 Subject: [PATCH 124/165] PCI: aspeed: Fix IRQ domain leak on platform_get_irq() failure The aspeed_pcie_probe() function calls aspeed_pcie_init_irq_domain() which allocates pcie->intx_domain and initializes MSI. However, if platform_get_irq() fails afterwards, the cleanup action was not yet registered via devm_add_action_or_reset(), causing the IRQ domain resources to leak. Fix this by registering the devm cleanup action immediately after aspeed_pcie_init_irq_domain() succeeds, before calling platform_get_irq(). This ensures proper cleanup on any subsequent failure. Fixes: 9aa0cb68fcc1 ("PCI: aspeed: Add ASPEED PCIe RC driver") Signed-off-by: Felix Gu Signed-off-by: Manivannan Sadhasivam Tested-by: Jacky Chou Link: https://patch.msgid.link/20260324-aspeed-v1-1-354181624c00@gmail.com --- drivers/pci/controller/pcie-aspeed.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c index 3e1a39d1e648..6acfae7d026e 100644 --- a/drivers/pci/controller/pcie-aspeed.c +++ b/drivers/pci/controller/pcie-aspeed.c @@ -1052,14 +1052,14 @@ static int aspeed_pcie_probe(struct platform_device *pdev) if (ret) return ret; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - ret = devm_add_action_or_reset(dev, aspeed_pcie_irq_domain_free, pcie); if (ret) return ret; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(dev, irq, aspeed_pcie_intr_handler, IRQF_SHARED, dev_name(dev), pcie); if (ret) From 401359ef44af43b6b775dc01bb7b31396db67aab Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 24 Mar 2026 10:30:32 +0800 Subject: [PATCH 125/165] dt-bindings: PCI: imx6q-pcie: Fix maxItems of clocks and clock-names Commit 1352f58d7c8d ("dt-bindings: PCI: pci-imx6: Add external reference clock input") that added reference clock to the binding was incomplete. The constraints for "clocks" and "clock-names" still enforce an incorrect number of items. Update maxItems for both properties to 6 to match the actual hardware configuration. Fixes: 1352f58d7c8d ("dt-bindings: PCI: pci-imx6: Add external reference clock input") Signed-off-by: Richard Zhu [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260324023036.784466-2-hongxing.zhu@nxp.com --- .../devicetree/bindings/pci/fsl,imx6q-pcie-common.yaml | 4 ++-- Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-common.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-common.yaml index cddbe21f99f2..0488c942092d 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-common.yaml @@ -17,11 +17,11 @@ description: properties: clocks: minItems: 3 - maxItems: 5 + maxItems: 6 clock-names: minItems: 3 - maxItems: 5 + maxItems: 6 num-lanes: const: 1 diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index 12a01f7a5744..21dda8066014 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -40,7 +40,8 @@ properties: - description: PCIe PHY clock. - description: Additional required clock entry for imx6sx-pcie, imx6sx-pcie-ep, imx8mq-pcie, imx8mq-pcie-ep. - - description: PCIe reference clock. + - description: PCIe internal reference clock. + - description: PCIe additional external reference clock. clock-names: minItems: 3 From 4d7937d8cc32b027a14cb8152d9df64d17e9392c Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 24 Mar 2026 10:30:33 +0800 Subject: [PATCH 126/165] dt-bindings: PCI: imx6q-pcie: Add i.MX94 and i.MX943 SoCs Add bindings support for PCIe endpoint controllers in i.MX94 and i.MX943 SoCs with fallback to the i.MX95 SoC. Signed-off-by: Richard Zhu [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260324023036.784466-3-hongxing.zhu@nxp.com --- .../bindings/pci/fsl,imx6q-pcie-ep.yaml | 18 ++++++++----- .../bindings/pci/fsl,imx6q-pcie.yaml | 26 ++++++++++++------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-ep.yaml index 0b3526de1d62..e4e30da0acb0 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie-ep.yaml @@ -18,12 +18,18 @@ description: |+ properties: compatible: - enum: - - fsl,imx8mm-pcie-ep - - fsl,imx8mq-pcie-ep - - fsl,imx8mp-pcie-ep - - fsl,imx8q-pcie-ep - - fsl,imx95-pcie-ep + oneOf: + - enum: + - fsl,imx8mm-pcie-ep + - fsl,imx8mp-pcie-ep + - fsl,imx8mq-pcie-ep + - fsl,imx8q-pcie-ep + - fsl,imx95-pcie-ep + - items: + - enum: + - fsl,imx94-pcie-ep + - fsl,imx943-pcie-ep + - const: fsl,imx95-pcie-ep clocks: minItems: 3 diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index 21dda8066014..9d1349855b42 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -21,16 +21,22 @@ description: |+ properties: compatible: - enum: - - fsl,imx6q-pcie - - fsl,imx6sx-pcie - - fsl,imx6qp-pcie - - fsl,imx7d-pcie - - fsl,imx8mq-pcie - - fsl,imx8mm-pcie - - fsl,imx8mp-pcie - - fsl,imx95-pcie - - fsl,imx8q-pcie + oneOf: + - enum: + - fsl,imx6q-pcie + - fsl,imx6qp-pcie + - fsl,imx6sx-pcie + - fsl,imx7d-pcie + - fsl,imx8mm-pcie + - fsl,imx8mp-pcie + - fsl,imx8mq-pcie + - fsl,imx8q-pcie + - fsl,imx95-pcie + - items: + - enum: + - fsl,imx94-pcie + - fsl,imx943-pcie + - const: fsl,imx95-pcie clocks: minItems: 3 From 5f352433ea39171e19fbb3a7e18d983510176854 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 24 Mar 2026 13:38:54 +0530 Subject: [PATCH 127/165] PCI: endpoint: Add reserved region type for MSI-X Table and PBA Add PCI_EPC_BAR_RSVD_MSIX_TBL_RAM and PCI_EPC_BAR_RSVD_MSIX_PBA_RAM to enum pci_epc_bar_rsvd_region_type so that Endpoint controllers can describe hardware-owned MSI-X Table and PBA (Pending Bit Array) regions behind a BAR_RESERVED BAR. Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260324080857.916263-2-mmaddireddy@nvidia.com --- include/linux/pci-epc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 334c2b7578d0..1eca1264815b 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -211,6 +211,8 @@ enum pci_epc_bar_type { /** * enum pci_epc_bar_rsvd_region_type - type of a fixed subregion behind a BAR * @PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO: Integrated DMA controller MMIO window + * @PCI_EPC_BAR_RSVD_MSIX_TBL_RAM: MSI-X table structure + * @PCI_EPC_BAR_RSVD_MSIX_PBA_RAM: MSI-X PBA structure * * BARs marked BAR_RESERVED are owned by the SoC/EPC hardware and must not be * reprogrammed by EPF drivers. Some of them still expose fixed subregions that @@ -218,6 +220,8 @@ enum pci_epc_bar_type { */ enum pci_epc_bar_rsvd_region_type { PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO = 0, + PCI_EPC_BAR_RSVD_MSIX_TBL_RAM, + PCI_EPC_BAR_RSVD_MSIX_PBA_RAM, }; /** From 5aec1f18b326ddc455ae9d9d0f5394efc20eee9b Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 24 Mar 2026 13:38:55 +0530 Subject: [PATCH 128/165] PCI: tegra194: Make BAR0 programmable and remove 1MB size limit The Tegra194/234 Endpoint does not support the Resizable BAR capability, but BAR0 can be programmed to different sizes via the DBI2 BAR registers in dw_pcie_ep_set_bar_programmable(). The BAR0 size is set once during initialization. Remove the fixed 1MB limit from pci_epc_features so Endpoint function drivers can configure the BAR0 size they need. Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260324080857.916263-3-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 6881f0b94c73..c5381ffdf1eb 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1978,12 +1978,12 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } +/* Tegra EP: BAR0 = 64-bit programmable BAR */ static const struct pci_epc_features tegra_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, - .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, - .only_64bit = true, }, + .bar[BAR_0] = { .only_64bit = true, }, .bar[BAR_2] = { .type = BAR_DISABLED, }, .bar[BAR_3] = { .type = BAR_DISABLED, }, .bar[BAR_4] = { .type = BAR_DISABLED, }, From 12a22fb38cbba1991a3f174912b343cdb6afa186 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 24 Mar 2026 13:38:56 +0530 Subject: [PATCH 129/165] PCI: tegra194: Expose BAR2 (MSI-X) and BAR4 (DMA) as 64-bit BAR_RESERVED Tegra Endpoint exposes three 64-bit BARs at indices 0, 2, and 4: - BAR0+BAR1: EPF test/data (programmable 64-bit BAR) - BAR2+BAR3: MSI-X table (hardware-backed) - BAR4+BAR5: DMA registers (hardware-backed) Update tegra_pcie_epc_features so that BAR2 is BAR_RESERVED with PCI_EPC_BAR_RSVD_MSIX_TBL_RAM (64 KB) & PCI_EPC_BAR_RSVD_MSIX_PBA_RAM (64 KB) and BAR4 is BAR_RESERVED with PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO (4KB). This keeps CONSECUTIVE_BAR_TEST working while allowing the host to use 64-bit BAR2 (MSI-X) and BAR4 (DMA). Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260324080857.916263-4-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index c5381ffdf1eb..ea7a6256450c 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1978,16 +1978,48 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, return 0; } -/* Tegra EP: BAR0 = 64-bit programmable BAR */ +static const struct pci_epc_bar_rsvd_region tegra194_bar2_rsvd[] = { + { + /* MSI-X table structure */ + .type = PCI_EPC_BAR_RSVD_MSIX_TBL_RAM, + .offset = 0x0, + .size = SZ_64K, + }, + { + /* MSI-X PBA structure */ + .type = PCI_EPC_BAR_RSVD_MSIX_PBA_RAM, + .offset = 0x10000, + .size = SZ_64K, + }, +}; + +static const struct pci_epc_bar_rsvd_region tegra194_bar4_rsvd[] = { + { + /* DMA_CAP (BAR4: DMA Port Logic Structure) */ + .type = PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO, + .offset = 0x0, + .size = SZ_4K, + }, +}; + +/* Tegra EP: BAR0 = 64-bit programmable BAR, BAR2 = 64-bit MSI-X table, BAR4 = 64-bit DMA regs. */ static const struct pci_epc_features tegra_pcie_epc_features = { DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .bar[BAR_0] = { .only_64bit = true, }, - .bar[BAR_2] = { .type = BAR_DISABLED, }, - .bar[BAR_3] = { .type = BAR_DISABLED, }, - .bar[BAR_4] = { .type = BAR_DISABLED, }, - .bar[BAR_5] = { .type = BAR_DISABLED, }, + .bar[BAR_2] = { + .type = BAR_RESERVED, + .only_64bit = true, + .nr_rsvd_regions = ARRAY_SIZE(tegra194_bar2_rsvd), + .rsvd_regions = tegra194_bar2_rsvd, + }, + .bar[BAR_4] = { + .type = BAR_RESERVED, + .only_64bit = true, + .nr_rsvd_regions = ARRAY_SIZE(tegra194_bar4_rsvd), + .rsvd_regions = tegra194_bar4_rsvd, + }, .align = SZ_64K, }; From 5ab7a225888baa5474def18ba3b0a298d27e6ba0 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 24 Mar 2026 13:38:57 +0530 Subject: [PATCH 130/165] misc: pci_endpoint_test: Add Tegra194 and Tegra234 device table entries Add PCI device IDs for Tegra194 (0x1ad4) and Tegra234(0x229b) Endpoint controllers, so that pci_endpoint_test can bind and run on these controllers. Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Link: https://patch.msgid.link/20260324080857.916263-5-mmaddireddy@nvidia.com --- drivers/misc/pci_endpoint_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 496c8f509590..38679dfb1f9b 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -114,6 +114,9 @@ #define PCI_DEVICE_ID_ROCKCHIP_RK3588 0x3588 +#define PCI_DEVICE_ID_NVIDIA_TEGRA194_EP 0x1ad4 +#define PCI_DEVICE_ID_NVIDIA_TEGRA234_EP 0x229b + #define PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB 2 static DEFINE_IDA(pci_endpoint_test_ida); @@ -1438,6 +1441,8 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_ROCKCHIP, PCI_DEVICE_ID_ROCKCHIP_RK3588), .driver_data = (kernel_ulong_t)&rk3588_data, }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA194_EP),}, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TEGRA234_EP),}, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); From 5573c44cb3fd01a9f62d569ae9ac870ef5f0e0ba Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 24 Mar 2026 17:35:41 +0800 Subject: [PATCH 131/165] PCI: mediatek-gen3: Prevent leaking IRQ domains when IRQ not found In mtk_pcie_setup_irq(), the IRQ domains are allocated before the controller's IRQ is fetched. If the latter fails, the function directly returns an error, without cleaning up the allocated domains. Hence, reverse the order so that the IRQ domains are allocated after the controller's IRQ is found. This was flagged by Sashiko during a review of "[PATCH v6 0/7] PCI: mediatek-gen3: add power control support". Fixes: 814cceebba9b ("PCI: mediatek-gen3: Add INTx support") Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Link: https://sashiko.dev/#/patchset/20260324052002.4072430-1-wenst%40chromium.org Link: https://patch.msgid.link/20260324093542.18523-1-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index a94fdbaf47fe..2f9365930d6a 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -890,14 +890,14 @@ static int mtk_pcie_setup_irq(struct mtk_gen3_pcie *pcie) struct platform_device *pdev = to_platform_device(dev); int err; - err = mtk_pcie_init_irq_domains(pcie); - if (err) - return err; - pcie->irq = platform_get_irq(pdev, 0); if (pcie->irq < 0) return pcie->irq; + err = mtk_pcie_init_irq_domains(pcie); + if (err) + return err; + irq_set_chained_handler_and_data(pcie->irq, mtk_pcie_irq_handler, pcie); return 0; From d9cf7154deed71a4f23e81101571c79cdc77be00 Mon Sep 17 00:00:00 2001 From: Aksh Garg Date: Thu, 2 Apr 2026 14:25:45 +0530 Subject: [PATCH 132/165] PCI: cadence: Use cdns_pcie_read_sz() for byte or word read access The commit 18ac51ae9df9 ("PCI: cadence: Implement capability search using PCI core APIs") assumed all the platforms using Cadence PCIe controller support byte and word register accesses. This is not true for all platforms (e.g., TI J721E SoC, which only supports dword register accesses). This causes capability searches via cdns_pcie_find_capability() to fail on such platforms. Fix this by using cdns_pcie_read_sz() for config read functions, which properly handles size-aligned accesses. Remove the now-unused byte and word read wrapper functions (cdns_pcie_readw and cdns_pcie_readb). Fixes: 18ac51ae9df9 ("PCI: cadence: Implement capability search using PCI core APIs") Signed-off-by: Aksh Garg Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260402085545.284457-1-a-garg7@ti.com --- drivers/pci/controller/cadence/pcie-cadence.h | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 443033c607d7..277f3706a4f4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -249,37 +249,6 @@ static inline u32 cdns_pcie_hpa_readl(struct cdns_pcie *pcie, return readl(pcie->reg_base + reg); } -static inline u16 cdns_pcie_readw(struct cdns_pcie *pcie, u32 reg) -{ - return readw(pcie->reg_base + reg); -} - -static inline u8 cdns_pcie_readb(struct cdns_pcie *pcie, u32 reg) -{ - return readb(pcie->reg_base + reg); -} - -static inline int cdns_pcie_read_cfg_byte(struct cdns_pcie *pcie, int where, - u8 *val) -{ - *val = cdns_pcie_readb(pcie, where); - return PCIBIOS_SUCCESSFUL; -} - -static inline int cdns_pcie_read_cfg_word(struct cdns_pcie *pcie, int where, - u16 *val) -{ - *val = cdns_pcie_readw(pcie, where); - return PCIBIOS_SUCCESSFUL; -} - -static inline int cdns_pcie_read_cfg_dword(struct cdns_pcie *pcie, int where, - u32 *val) -{ - *val = cdns_pcie_readl(pcie, where); - return PCIBIOS_SUCCESSFUL; -} - static inline u32 cdns_pcie_read_sz(void __iomem *addr, int size) { void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4); @@ -320,6 +289,31 @@ static inline void cdns_pcie_write_sz(void __iomem *addr, int size, u32 value) writel(val, aligned_addr); } +static inline int cdns_pcie_read_cfg_byte(struct cdns_pcie *pcie, int where, + u8 *val) +{ + void __iomem *addr = pcie->reg_base + where; + + *val = cdns_pcie_read_sz(addr, 0x1); + return PCIBIOS_SUCCESSFUL; +} + +static inline int cdns_pcie_read_cfg_word(struct cdns_pcie *pcie, int where, + u16 *val) +{ + void __iomem *addr = pcie->reg_base + where; + + *val = cdns_pcie_read_sz(addr, 0x2); + return PCIBIOS_SUCCESSFUL; +} + +static inline int cdns_pcie_read_cfg_dword(struct cdns_pcie *pcie, int where, + u32 *val) +{ + *val = cdns_pcie_readl(pcie, where); + return PCIBIOS_SUCCESSFUL; +} + /* Root Port register access */ static inline void cdns_pcie_rp_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) From 88cc4cbe08bba27bb58888d25d336774aa0ccab1 Mon Sep 17 00:00:00 2001 From: Franz Schnyder Date: Wed, 25 Mar 2026 10:31:16 +0100 Subject: [PATCH 133/165] PCI: imx6: Fix reference clock source selection for i.MX95 In the PCIe PHY init for the i.MX95, the reference clock source selection uses a conditional instead of always passing the mask. This currently breaks functionality if the internal refclk is used. To fix this issue, always pass IMX95_PCIE_REF_USE_PAD as the mask and clear bit if external refclk is not used. This essentially swaps the parameters. Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support") Signed-off-by: Franz Schnyder Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Richard Zhu Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325093118.684142-1-fra.schnyder@gmail.com --- drivers/pci/controller/dwc/pci-imx6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index a1da72dd85b0..3b298f8659ea 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -270,8 +270,8 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - ext ? IMX95_PCIE_REF_USE_PAD : 0, - IMX95_PCIE_REF_USE_PAD); + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, ext ? 0 : IMX95_PCIE_REF_CLKEN); From 16d021c878dca22532c984668c9e8cf4722d6a49 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Thu, 2 Apr 2026 17:38:50 +0800 Subject: [PATCH 134/165] PCI/NPEM: Set LED_HW_PLUGGABLE for hotplug-capable ports NPEM registers LED classdevs on PCI endpoint that may be behind hotplug-capable ports. During hot-removal, led_classdev_unregister() calls led_set_brightness(LED_OFF) which leads to a PCI config read to a disconnected device, which fails and returns -ENODEV (topology details in msgid.link below): leds 0003:01:00.0:enclosure:ok: Setting an LED's brightness failed (-19) The LED core already suppresses this for devices with LED_HW_PLUGGABLE set, but NPEM never sets it. Add the flag since NPEM LEDs are on hot-pluggable hardware by nature. Fixes: 4e893545ef87 ("PCI/NPEM: Add Native PCIe Enclosure Management support") Signed-off-by: Richard Cheng Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Acked-by: Kai-Heng Feng Link: https://patch.msgid.link/20260402093850.23075-1-icheng@nvidia.com --- drivers/pci/npem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/npem.c b/drivers/pci/npem.c index ffeeedf6e311..c51879fcd438 100644 --- a/drivers/pci/npem.c +++ b/drivers/pci/npem.c @@ -504,7 +504,7 @@ static int pci_npem_set_led_classdev(struct npem *npem, struct npem_led *nled) led->brightness_get = brightness_get; led->max_brightness = 1; led->default_trigger = "none"; - led->flags = 0; + led->flags = LED_HW_PLUGGABLE; ret = led_classdev_register(&npem->dev->dev, led); if (ret) From d1b7add89c004295cd48d7cd49946ed5cb5cbb55 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 25 Mar 2026 09:58:30 +0800 Subject: [PATCH 135/165] PCI: trace: Add PCI controller tracepoint feature Some PCI controllers may provide debug functionalities to track PCI bus activities like LTSSM state transitions and data rate changes. These will be very useful for debugging PCI link specific issues such as endpoint not getting detected or performance issues. Hence, implement the PCI controller tracepoint feature for recording LTSSM state transitions and data rate changes. Signed-off-by: Shawn Lin [mani: commit log and maintainers entry] Signed-off-by: Manivannan Sadhasivam Tested-by: Anand Moon Reviewed-by: Steven Rostedt (Google) Link: https://patch.msgid.link/1774403912-210670-2-git-send-email-shawn.lin@rock-chips.com --- MAINTAINERS | 1 + drivers/pci/trace.c | 1 + include/trace/events/pci_controller.h | 58 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 include/trace/events/pci_controller.h diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..b5a83f0b4211 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20399,6 +20399,7 @@ F: Documentation/devicetree/bindings/pci/ F: drivers/pci/controller/ F: drivers/pci/pci-bridge-emul.c F: drivers/pci/pci-bridge-emul.h +F: include/trace/events/pci_controller.h PCI PEER-TO-PEER DMA (P2PDMA) M: Bjorn Helgaas diff --git a/drivers/pci/trace.c b/drivers/pci/trace.c index cf11abca8602..c1da9d3d39d6 100644 --- a/drivers/pci/trace.c +++ b/drivers/pci/trace.c @@ -9,3 +9,4 @@ #define CREATE_TRACE_POINTS #include +#include diff --git a/include/trace/events/pci_controller.h b/include/trace/events/pci_controller.h new file mode 100644 index 000000000000..a4b387cf52a6 --- /dev/null +++ b/include/trace/events/pci_controller.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pci_controller + +#if !defined(_TRACE_HW_EVENT_PCI_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HW_EVENT_PCI_CONTROLLER_H + +#include +#include + +#define RATE \ + EM(PCIE_SPEED_2_5GT, "2.5 GT/s") \ + EM(PCIE_SPEED_5_0GT, "5.0 GT/s") \ + EM(PCIE_SPEED_8_0GT, "8.0 GT/s") \ + EM(PCIE_SPEED_16_0GT, "16.0 GT/s") \ + EM(PCIE_SPEED_32_0GT, "32.0 GT/s") \ + EM(PCIE_SPEED_64_0GT, "64.0 GT/s") \ + EMe(PCI_SPEED_UNKNOWN, "Unknown") + + +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +RATE + +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} + +TRACE_EVENT(pcie_ltssm_state_transition, + TP_PROTO(const char *dev_name, const char *state, u32 rate), + TP_ARGS(dev_name, state, rate), + + TP_STRUCT__entry( + __string(dev_name, dev_name) + __string(state, state) + __field(u32, rate) + ), + + TP_fast_assign( + __assign_str(dev_name); + __assign_str(state); + __entry->rate = rate; + ), + + TP_printk("dev: %s state: %s rate: %s", + __get_str(dev_name), __get_str(state), + __print_symbolic(__entry->rate, RATE) + ) +); + +#endif /* _TRACE_HW_EVENT_PCI_CONTROLLER_H */ + +/* This part must be outside protection */ +#include From a3966a6f915ea7d1af0941ea26848d921e574c45 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 25 Mar 2026 09:58:31 +0800 Subject: [PATCH 136/165] Documentation: tracing: Add PCI controller event documentation The PCI controller tracepoint, pcie_ltssm_state_transition, monitors the LTSSM state transition and data rate changes for debugging purposes. Add documentation for it. Signed-off-by: Shawn Lin [mani: added MAINTAINERS entry] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Steven Rostedt (Google) Link: https://patch.msgid.link/1774403912-210670-3-git-send-email-shawn.lin@rock-chips.com --- Documentation/trace/events-pci-controller.rst | 42 +++++++++++++++++++ Documentation/trace/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 44 insertions(+) create mode 100644 Documentation/trace/events-pci-controller.rst diff --git a/Documentation/trace/events-pci-controller.rst b/Documentation/trace/events-pci-controller.rst new file mode 100644 index 000000000000..cb9f71592973 --- /dev/null +++ b/Documentation/trace/events-pci-controller.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +Subsystem Trace Points: PCI Controller +====================================== + +Overview +======== +The PCI controller tracing system provides tracepoints to monitor controller +level information for debugging purpose. The events normally show up here: + + /sys/kernel/tracing/events/pci_controller + +Cf. include/trace/events/pci_controller.h for the events definitions. + +Available Tracepoints +===================== + +pcie_ltssm_state_transition +--------------------------- + +Monitors PCIe LTSSM state transition including state and rate information +:: + + pcie_ltssm_state_transition "dev: %s state: %s rate: %s\n" + +**Parameters**: + +* ``dev`` - PCIe controller instance +* ``state`` - PCIe LTSSM state +* ``rate`` - PCIe date rate + +**Example Usage**: + +.. code-block:: shell + + # Enable the tracepoint + echo 1 > /sys/kernel/debug/tracing/events/pci_controller/pcie_ltssm_state_transition/enable + + # Monitor events (the following output is generated when a device is linking) + cat /sys/kernel/debug/tracing/trace_pipe + kworker/0:0-9 [000] ..... 5.600221: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_EQ2 rate: 8.0 GT/s diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index 338bc4d7cfab..5715a32866ab 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -55,6 +55,7 @@ applications. events-nmi events-msr events-pci + events-pci-controller boottime-trace histogram histogram-design diff --git a/MAINTAINERS b/MAINTAINERS index b5a83f0b4211..f81b918c7507 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20396,6 +20396,7 @@ C: irc://irc.oftc.net/linux-pci T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git F: Documentation/ABI/testing/debugfs-pcie-ptm F: Documentation/devicetree/bindings/pci/ +F: Documentation/trace/events-pci-controller.rst F: drivers/pci/controller/ F: drivers/pci/pci-bridge-emul.c F: drivers/pci/pci-bridge-emul.h From f3ddb8a9a97fd7b933442d25309b90eafc5f2d74 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 25 Mar 2026 09:58:32 +0800 Subject: [PATCH 137/165] PCI: dw-rockchip: Add pcie_ltssm_state_transition tracepoint support Rockchip platforms provide a 64x4 bytes debug FIFO to trace the LTSSM transition and data rate change history. These will be useful for debugging issues such as link failure, etc. Hence, expose these information over pcie_ltssm_state_transition tracepoint. Signed-off-by: Shawn Lin [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Anand Moon Reviewed-by: Steven Rostedt (Google) Link: https://patch.msgid.link/1774403912-210670-4-git-send-email-shawn.lin@rock-chips.com --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 5b17da63151d..c7f67472ec45 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "../../pci.h" #include "pcie-designware.h" @@ -73,6 +75,20 @@ #define PCIE_CLIENT_CDM_RASDES_TBA_L1_1 BIT(4) #define PCIE_CLIENT_CDM_RASDES_TBA_L1_2 BIT(5) +/* Debug FIFO information */ +#define PCIE_CLIENT_DBG_FIFO_MODE_CON 0x310 +#define PCIE_CLIENT_DBG_EN 0xffff0007 +#define PCIE_CLIENT_DBG_DIS 0xffff0000 +#define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0 0x320 +#define PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1 0x324 +#define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0 0x328 +#define PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1 0x32c +#define PCIE_CLIENT_DBG_TRANSITION_DATA 0xffff0000 +#define PCIE_CLIENT_DBG_FIFO_STATUS 0x350 +#define PCIE_DBG_FIFO_RATE_MASK GENMASK(22, 20) +#define PCIE_DBG_FIFO_L1SUB_MASK GENMASK(10, 8) +#define PCIE_DBG_LTSSM_HISTORY_CNT 64 + /* Hot Reset Control Register */ #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 #define PCIE_LTSSM_APP_DLY2_EN BIT(1) @@ -98,6 +114,7 @@ struct rockchip_pcie { struct irq_domain *irq_domain; const struct rockchip_pcie_of_data *data; bool supports_clkreq; + struct delayed_work trace_work; }; struct rockchip_pcie_of_data { @@ -208,6 +225,96 @@ static enum dw_pcie_ltssm rockchip_pcie_get_ltssm(struct dw_pcie *pci) return rockchip_pcie_get_ltssm_reg(rockchip) & PCIE_LTSSM_STATUS_MASK; } +#ifdef CONFIG_TRACING +static void rockchip_pcie_ltssm_trace_work(struct work_struct *work) +{ + struct rockchip_pcie *rockchip = container_of(work, + struct rockchip_pcie, + trace_work.work); + struct dw_pcie *pci = &rockchip->pci; + enum dw_pcie_ltssm state; + u32 i, l1ss, prev_val = DW_PCIE_LTSSM_UNKNOWN, rate, val; + + if (!trace_pcie_ltssm_state_transition_enabled()) + goto skip_trace; + + for (i = 0; i < PCIE_DBG_LTSSM_HISTORY_CNT; i++) { + val = rockchip_pcie_readl_apb(rockchip, + PCIE_CLIENT_DBG_FIFO_STATUS); + rate = FIELD_GET(PCIE_DBG_FIFO_RATE_MASK, val); + l1ss = FIELD_GET(PCIE_DBG_FIFO_L1SUB_MASK, val); + val = FIELD_GET(PCIE_LTSSM_STATUS_MASK, val); + + /* + * Hardware Mechanism: The ring FIFO employs two tracking + * counters: + * - 'last-read-point': maintains the user's last read position + * - 'last-valid-point': tracks the HW's last state update + * + * Software Handling: When two consecutive LTSSM states are + * identical, it indicates invalid subsequent data in the FIFO. + * In this case, we skip the remaining entries. The dual counter + * design ensures that on the next state transition, reading can + * resume from the last user position. + */ + if ((i > 0 && val == prev_val) || val > DW_PCIE_LTSSM_RCVRY_EQ3) + break; + + state = prev_val = val; + if (val == DW_PCIE_LTSSM_L1_IDLE) { + if (l1ss == 2) + state = DW_PCIE_LTSSM_L1_2; + else if (l1ss == 1) + state = DW_PCIE_LTSSM_L1_1; + } + + trace_pcie_ltssm_state_transition(dev_name(pci->dev), + dw_pcie_ltssm_status_string(state), + ((rate + 1) > pci->max_link_speed) ? + PCI_SPEED_UNKNOWN : PCIE_SPEED_2_5GT + rate); + } + +skip_trace: + schedule_delayed_work(&rockchip->trace_work, msecs_to_jiffies(5000)); +} + +static void rockchip_pcie_ltssm_trace(struct rockchip_pcie *rockchip, + bool enable) +{ + if (enable) { + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_TRANSITION_DATA, + PCIE_CLIENT_DBG_FIFO_PTN_HIT_D0); + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_TRANSITION_DATA, + PCIE_CLIENT_DBG_FIFO_PTN_HIT_D1); + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_TRANSITION_DATA, + PCIE_CLIENT_DBG_FIFO_TRN_HIT_D0); + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_TRANSITION_DATA, + PCIE_CLIENT_DBG_FIFO_TRN_HIT_D1); + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_EN, + PCIE_CLIENT_DBG_FIFO_MODE_CON); + + INIT_DELAYED_WORK(&rockchip->trace_work, + rockchip_pcie_ltssm_trace_work); + schedule_delayed_work(&rockchip->trace_work, 0); + } else { + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_DBG_DIS, + PCIE_CLIENT_DBG_FIFO_MODE_CON); + cancel_delayed_work_sync(&rockchip->trace_work); + } +} +#else +static void rockchip_pcie_ltssm_trace(struct rockchip_pcie *rockchip, + bool enable) +{ +} +#endif + static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) { rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM, @@ -291,6 +398,9 @@ static int rockchip_pcie_start_link(struct dw_pcie *pci) * 100us as we don't know how long should the device need to reset. */ msleep(PCIE_T_PVPERL_MS); + + rockchip_pcie_ltssm_trace(rockchip, true); + gpiod_set_value_cansleep(rockchip->rst_gpio, 1); return 0; @@ -301,6 +411,7 @@ static void rockchip_pcie_stop_link(struct dw_pcie *pci) struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); rockchip_pcie_disable_ltssm(rockchip); + rockchip_pcie_ltssm_trace(rockchip, false); } static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) From 6f468ea360f0a6a1e45854afbc3019842ed891a8 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 8 Aug 2024 10:27:29 -0700 Subject: [PATCH 138/165] Input: pc110pad - remove driver Palm Top PC 110 is a handheld personal computer with 80486SX CPU that was released exclusively in Japan in September 1995. While the kernel still supports 486 CPU it is highly unlikely that anyone is using this device with the latest kernel. Remove the driver. [bhelgaas: since this was posted, "x86/cpu: Remove M486/M486SX/ELAN support" has been queued for v7.1, so pc110pad is no longer relevant: https://lore.kernel.org/all/20251214084710.3606385-2-mingo@kernel.org/] Signed-off-by: Dmitry Torokhov Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20240808172733.1194442-4-dmitry.torokhov@gmail.com --- drivers/input/mouse/Kconfig | 10 --- drivers/input/mouse/Makefile | 1 - drivers/input/mouse/pc110pad.c | 160 --------------------------------- 3 files changed, 171 deletions(-) delete mode 100644 drivers/input/mouse/pc110pad.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 833b643f0616..c8b87a87744c 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -326,16 +326,6 @@ config MOUSE_LOGIBM To compile this driver as a module, choose M here: the module will be called logibm. -config MOUSE_PC110PAD - tristate "IBM PC110 touchpad" - depends on ISA - help - Say Y if you have the IBM PC-110 micro-notebook and want its - touchpad supported. - - To compile this driver as a module, choose M here: the - module will be called pc110pad. - config MOUSE_AMIGA tristate "Amiga mouse" depends on AMIGA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index a1336d5bee6f..78ab5db28d7c 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o -obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c deleted file mode 100644 index efa58049f746..000000000000 --- a/drivers/input/mouse/pc110pad.c +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2000-2001 Vojtech Pavlik - * - * Based on the work of: - * Alan Cox Robin O'Leary - */ - -/* - * IBM PC110 touchpad driver for Linux - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("IBM PC110 touchpad driver"); -MODULE_LICENSE("GPL"); - -#define PC110PAD_OFF 0x30 -#define PC110PAD_ON 0x38 - -static int pc110pad_irq = 10; -static int pc110pad_io = 0x15e0; - -static struct input_dev *pc110pad_dev; -static int pc110pad_data[3]; -static int pc110pad_count; - -static irqreturn_t pc110pad_interrupt(int irq, void *ptr) -{ - int value = inb_p(pc110pad_io); - int handshake = inb_p(pc110pad_io + 2); - - outb(handshake | 1, pc110pad_io + 2); - udelay(2); - outb(handshake & ~1, pc110pad_io + 2); - udelay(2); - inb_p(0x64); - - pc110pad_data[pc110pad_count++] = value; - - if (pc110pad_count < 3) - return IRQ_HANDLED; - - input_report_key(pc110pad_dev, BTN_TOUCH, - pc110pad_data[0] & 0x01); - input_report_abs(pc110pad_dev, ABS_X, - pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); - input_report_abs(pc110pad_dev, ABS_Y, - pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); - input_sync(pc110pad_dev); - - pc110pad_count = 0; - return IRQ_HANDLED; -} - -static void pc110pad_close(struct input_dev *dev) -{ - outb(PC110PAD_OFF, pc110pad_io + 2); -} - -static int pc110pad_open(struct input_dev *dev) -{ - pc110pad_interrupt(0, NULL); - pc110pad_interrupt(0, NULL); - pc110pad_interrupt(0, NULL); - outb(PC110PAD_ON, pc110pad_io + 2); - pc110pad_count = 0; - - return 0; -} - -/* - * We try to avoid enabling the hardware if it's not - * there, but we don't know how to test. But we do know - * that the PC110 is not a PCI system. So if we find any - * PCI devices in the machine, we don't have a PC110. - */ -static int __init pc110pad_init(void) -{ - int err; - - if (!no_pci_devices()) - return -ENODEV; - - if (!request_region(pc110pad_io, 4, "pc110pad")) { - printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", - pc110pad_io, pc110pad_io + 4); - return -EBUSY; - } - - outb(PC110PAD_OFF, pc110pad_io + 2); - - if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) { - printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); - err = -EBUSY; - goto err_release_region; - } - - pc110pad_dev = input_allocate_device(); - if (!pc110pad_dev) { - printk(KERN_ERR "pc110pad: Not enough memory.\n"); - err = -ENOMEM; - goto err_free_irq; - } - - pc110pad_dev->name = "IBM PC110 TouchPad"; - pc110pad_dev->phys = "isa15e0/input0"; - pc110pad_dev->id.bustype = BUS_ISA; - pc110pad_dev->id.vendor = 0x0003; - pc110pad_dev->id.product = 0x0001; - pc110pad_dev->id.version = 0x0100; - - pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y); - pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff); - input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff); - - pc110pad_dev->open = pc110pad_open; - pc110pad_dev->close = pc110pad_close; - - err = input_register_device(pc110pad_dev); - if (err) - goto err_free_dev; - - return 0; - - err_free_dev: - input_free_device(pc110pad_dev); - err_free_irq: - free_irq(pc110pad_irq, NULL); - err_release_region: - release_region(pc110pad_io, 4); - - return err; -} - -static void __exit pc110pad_exit(void) -{ - outb(PC110PAD_OFF, pc110pad_io + 2); - free_irq(pc110pad_irq, NULL); - input_unregister_device(pc110pad_dev); - release_region(pc110pad_io, 4); -} - -module_init(pc110pad_init); -module_exit(pc110pad_exit); From d79dc408deb6c192adbad7893ee0c22d50826511 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 3 Apr 2026 00:18:15 +0200 Subject: [PATCH 139/165] PCI: Remove no_pci_devices() After having removed the last usage of no_pci_devices(), this function can be removed. Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/b0ce592d-c34c-4e0b-b389-4e346b3a0c44@gmail.com --- drivers/pci/probe.c | 17 ----------------- include/linux/pci.h | 3 --- 2 files changed, 20 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bccc7a4bdd79..19d73f6132fb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -67,23 +67,6 @@ static struct resource *get_pci_domain_busn_res(int domain_nr) return &r->res; } -/* - * Some device drivers need know if PCI is initiated. - * Basically, we think PCI is not initiated when there - * is no device to be found on the pci_bus_type. - */ -int no_pci_devices(void) -{ - struct device *dev; - int no_devices; - - dev = bus_find_next_device(&pci_bus_type, NULL); - no_devices = (dev == NULL); - put_device(dev); - return no_devices; -} -EXPORT_SYMBOL(no_pci_devices); - /* * PCI Bus Class */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 1c270f1d5123..482dd8460dd9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1193,8 +1193,6 @@ extern const struct bus_type pci_bus_type; /* Do NOT directly access these two variables, unless you are arch-specific PCI * code, or PCI core code. */ extern struct list_head pci_root_buses; /* List of all known PCI buses */ -/* Some device drivers need know if PCI is initiated */ -int no_pci_devices(void); void pcibios_resource_survey_bus(struct pci_bus *bus); void pcibios_bus_add_device(struct pci_dev *pdev); @@ -2132,7 +2130,6 @@ static inline struct pci_dev *pci_get_base_class(unsigned int class, static inline int pci_dev_present(const struct pci_device_id *ids) { return 0; } -#define no_pci_devices() (1) #define pci_dev_put(dev) do { } while (0) static inline void pci_set_master(struct pci_dev *dev) { } From 1d3225cb5d82680143ffd705088199917ceafd76 Mon Sep 17 00:00:00 2001 From: Christian Bruel Date: Tue, 7 Apr 2026 14:04:08 +0200 Subject: [PATCH 140/165] selftests: pci_endpoint: Skip BAR subrange test on -ENOSPC In pci-epf-test.c, set the STATUS_NO_RESOURCE status bit if pci_epc_set_bar() returns -ENOSPC. This status bit is used to indicate that there are not enough inbound window resources to allocate the subrange. In pci_endpoint_test.c, return -ENOSPC instead of -EIO when STATUS_NO_RESOURCE is set. In pci_endpoint_test.c, skip the BAR subrange test if -ENOSPC, i.e., there are not enough inbound window resources to run the test. Signed-off-by: Christian Bruel [mani: commit log] Signed-off-by: Manivannan Sadhasivam [bhelgaas: squash related commits] Signed-off-by: Bjorn Helgaas Reviewed-by: Niklas Cassel Reviewed-by: Frank Li Reviewed-by: Koichiro Den Link: https://patch.msgid.link/20260407-skip-bar_subrange-tests-if-enospc-v4-1-6f2e65f2298c@foss.st.com Link: https://patch.msgid.link/20260407-skip-bar_subrange-tests-if-enospc-v4-2-6f2e65f2298c@foss.st.com Link: https://patch.msgid.link/20260407-skip-bar_subrange-tests-if-enospc-v4-3-6f2e65f2298c@foss.st.com --- drivers/misc/pci_endpoint_test.c | 3 ++- drivers/pci/endpoint/functions/pci-epf-test.c | 3 +++ tools/testing/selftests/pci_endpoint/pci_endpoint_test.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 38679dfb1f9b..dbd017cabbb9 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -61,6 +61,7 @@ #define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15) #define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16) #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) +#define STATUS_NO_RESOURCE BIT(18) #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 @@ -480,7 +481,7 @@ static int pci_endpoint_test_bar_subrange_cmd(struct pci_endpoint_test *test, status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); if (status & fail_bit) - return -EIO; + return (status & STATUS_NO_RESOURCE) ? -ENOSPC : -EIO; if (!(status & ok_bit)) return -EIO; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 14e61ebe1f11..7eb94dffac2d 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -54,6 +54,7 @@ #define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15) #define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16) #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) +#define STATUS_NO_RESOURCE BIT(18) #define FLAG_USE_DMA BIT(0) @@ -901,6 +902,8 @@ static void pci_epf_test_bar_subrange_setup(struct pci_epf_test *epf_test, ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar); if (ret) { dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret); + if (ret == -ENOSPC) + status |= STATUS_NO_RESOURCE; bar->submap = old_submap; bar->num_submap = old_nsub; kfree(submap); diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index c417fb3a198b..588d75c97ad1 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -88,6 +88,8 @@ TEST_F(pci_ep_bar, BAR_SUBRANGE_TEST) SKIP(return, "Subrange map is not supported"); if (ret == -ENOBUFS) SKIP(return, "BAR is reserved"); + if (ret == -ENOSPC) + SKIP(return, "Not enough inbound windows"); EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno); } From adaffed907f14f954096555665ad6af2ae724d83 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:42 +0530 Subject: [PATCH 141/165] PCI: tegra194: Fix polling delay for L2 state As per PCIe r7.0, sec 5.3.3.2.1, after sending PME_Turn_Off message, Root Port should wait for 1-10 msec for PME_TO_Ack message. Currently, driver is polling for 10 msec with 1 usec delay which is aggressive. Use existing macro PCIE_PME_TO_L2_TIMEOUT_US to poll for 10 msec with 1 msec delay. Since this function is used in non-atomic context only, use non-atomic poll function. Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324190755.1094879-2-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 06571d806ab3..13949f6f7d5b 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -198,8 +198,6 @@ #define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK GENMASK(11, 8) #define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT 8 -#define PME_ACK_TIMEOUT 10000 - #define LTSSM_TIMEOUT 50000 /* 50ms */ #define GEN3_GEN4_EQ_PRESET_INIT 5 @@ -1553,9 +1551,10 @@ static int tegra_pcie_try_link_l2(struct tegra_pcie_dw *pcie) val |= APPL_PM_XMT_TURNOFF_STATE; appl_writel(pcie, val, APPL_RADM_STATUS); - return readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, val, - val & APPL_DEBUG_PM_LINKST_IN_L2_LAT, - 1, PME_ACK_TIMEOUT); + return readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val, + val & APPL_DEBUG_PM_LINKST_IN_L2_LAT, + PCIE_PME_TO_L2_TIMEOUT_US/10, + PCIE_PME_TO_L2_TIMEOUT_US); } static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) From 74dd8efe4d6cead433162147333af989a568aac7 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:43 +0530 Subject: [PATCH 142/165] PCI: tegra194: Increase LTSSM poll time on surprise link down On surprise link down, LTSSM state transits from L0 -> Recovery.RcvrLock -> Recovery.RcvrSpeed -> Gen1 Recovery.RcvrLock -> Detect. Recovery.RcvrLock and Recovery.RcvrSpeed transit times are 24 ms and 48 ms respectively, so the total time from L0 to Detect is ~96 ms. Increase the poll timeout to 120 ms to account for this. While at it, add LTSSM state defines for Detect-related states and use them in the poll condition. Use readl_poll_timeout() instead of readl_poll_timeout_atomic() in tegra_pcie_dw_pme_turnoff() since that path runs in non-atomic context. Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324190755.1094879-3-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 36 +++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 13949f6f7d5b..94113b2e3308 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -137,7 +137,11 @@ #define APPL_DEBUG_PM_LINKST_IN_L0 0x11 #define APPL_DEBUG_LTSSM_STATE_MASK GENMASK(8, 3) #define APPL_DEBUG_LTSSM_STATE_SHIFT 3 -#define LTSSM_STATE_PRE_DETECT 5 +#define LTSSM_STATE_DETECT_QUIET 0x00 +#define LTSSM_STATE_DETECT_ACT 0x08 +#define LTSSM_STATE_PRE_DETECT_QUIET 0x28 +#define LTSSM_STATE_DETECT_WAIT 0x30 +#define LTSSM_STATE_L2_IDLE 0xa8 #define APPL_RADM_STATUS 0xE4 #define APPL_PM_XMT_TURNOFF_STATE BIT(0) @@ -198,7 +202,8 @@ #define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK GENMASK(11, 8) #define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT 8 -#define LTSSM_TIMEOUT 50000 /* 50ms */ +#define LTSSM_DELAY_US 10000 /* 10 ms */ +#define LTSSM_TIMEOUT_US 120000 /* 120 ms */ #define GEN3_GEN4_EQ_PRESET_INIT 5 @@ -1597,15 +1602,14 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) data &= ~APPL_CTRL_LTSSM_EN; writel(data, pcie->appl_base + APPL_CTRL); - err = readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, - data, - ((data & - APPL_DEBUG_LTSSM_STATE_MASK) >> - APPL_DEBUG_LTSSM_STATE_SHIFT) == - LTSSM_STATE_PRE_DETECT, - 1, LTSSM_TIMEOUT); + err = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, data, + ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) || + ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) || + ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_PRE_DETECT_QUIET) || + ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_WAIT), + LTSSM_DELAY_US, LTSSM_TIMEOUT_US); if (err) - dev_info(pcie->dev, "Link didn't go to detect state\n"); + dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", data, err); } /* * DBI registers may not be accessible after this as PLL-E would be @@ -1685,12 +1689,14 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) appl_writel(pcie, val, APPL_CTRL); ret = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val, - ((val & APPL_DEBUG_LTSSM_STATE_MASK) >> - APPL_DEBUG_LTSSM_STATE_SHIFT) == - LTSSM_STATE_PRE_DETECT, - 1, LTSSM_TIMEOUT); + ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) || + ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) || + ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_PRE_DETECT_QUIET) || + ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_WAIT) || + ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_L2_IDLE), + LTSSM_DELAY_US, LTSSM_TIMEOUT_US); if (ret) - dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret); + dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", val, ret); reset_control_assert(pcie->core_rst); From 9fa0c242f8d7acf1b124d4462d18f4023573ac1c Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:44 +0530 Subject: [PATCH 143/165] PCI: tegra194: Disable LTSSM after transition to Detect on surprise link down After the link reaches a Detect-related LTSSM state, disable LTSSM so it does not keep toggling between Polling and Detect. Do this by polling for the Detect state first, then clearing APPL_CTRL_LTSSM_EN in both tegra_pcie_dw_pme_turnoff() and pex_ep_event_pex_rst_assert(). Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324190755.1094879-4-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 94113b2e3308..b38dbd02214b 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1594,14 +1594,6 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) data &= ~APPL_PINMUX_PEX_RST; appl_writel(pcie, data, APPL_PINMUX); - /* - * Some cards do not go to detect state even after de-asserting - * PERST#. So, de-assert LTSSM to bring link to detect state. - */ - data = readl(pcie->appl_base + APPL_CTRL); - data &= ~APPL_CTRL_LTSSM_EN; - writel(data, pcie->appl_base + APPL_CTRL); - err = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, data, ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) || ((data & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) || @@ -1610,6 +1602,14 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) LTSSM_DELAY_US, LTSSM_TIMEOUT_US); if (err) dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", data, err); + + /* + * Deassert LTSSM state to stop the state toggling between + * Polling and Detect. + */ + data = readl(pcie->appl_base + APPL_CTRL); + data &= ~APPL_CTRL_LTSSM_EN; + writel(data, pcie->appl_base + APPL_CTRL); } /* * DBI registers may not be accessible after this as PLL-E would be @@ -1683,11 +1683,6 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) if (pcie->ep_state == EP_STATE_DISABLED) return; - /* Disable LTSSM */ - val = appl_readl(pcie, APPL_CTRL); - val &= ~APPL_CTRL_LTSSM_EN; - appl_writel(pcie, val, APPL_CTRL); - ret = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val, ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_QUIET) || ((val & APPL_DEBUG_LTSSM_STATE_MASK) == LTSSM_STATE_DETECT_ACT) || @@ -1698,6 +1693,14 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) if (ret) dev_info(pcie->dev, "LTSSM state: 0x%x detect timeout: %d\n", val, ret); + /* + * Deassert LTSSM state to stop the state toggling between + * Polling and Detect. + */ + val = appl_readl(pcie, APPL_CTRL); + val &= ~APPL_CTRL_LTSSM_EN; + appl_writel(pcie, val, APPL_CTRL); + reset_control_assert(pcie->core_rst); tegra_pcie_disable_phy(pcie); From 71d9f67701e1affc82d18ca88ae798c5361beddf Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:45 +0530 Subject: [PATCH 144/165] PCI: tegra194: Don't force the device into the D0 state before L2 As per PCIe CEM r6.0, sec 2.3, the PCIe Endpoint device should be in D3cold to assert WAKE# pin. The previous workaround that forced downstream devices to D0 before taking the link to L2 cited PCIe r4.0, sec 5.2, "Link State Power Management"; however, that spec does not explicitly require putting the device into D0 and only indicates that power removal may be initiated without transitioning to D3hot. Remove the D0 workaround so that Endpoint devices can use wake functionality (WAKE# from D3). With some Endpoints the link may not enter L2 when they remain in D3, but the Root Port continues with the usual flow after PME timeout, so there is no functional issue. Fixes: 56e15a238d92 ("PCI: tegra: Add Tegra194 PCIe support") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Vidya Sagar Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324190755.1094879-5-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 41 ---------------------- 1 file changed, 41 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index b38dbd02214b..c84eb1ba3a11 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1258,44 +1258,6 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, return 0; } -static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) -{ - struct dw_pcie_rp *pp = &pcie->pci.pp; - struct pci_bus *child, *root_port_bus = NULL; - struct pci_dev *pdev; - - /* - * link doesn't go into L2 state with some of the endpoints with Tegra - * if they are not in D0 state. So, need to make sure that immediate - * downstream devices are in D0 state before sending PME_TurnOff to put - * link into L2 state. - * This is as per PCI Express Base r4.0 v1.0 September 27-2017, - * 5.2 Link State Power Management (Page #428). - */ - - list_for_each_entry(child, &pp->bridge->bus->children, node) { - if (child->parent == pp->bridge->bus) { - root_port_bus = child; - break; - } - } - - if (!root_port_bus) { - dev_err(pcie->dev, "Failed to find downstream bus of Root Port\n"); - return; - } - - /* Bring downstream devices to D0 if they are not already in */ - list_for_each_entry(pdev, &root_port_bus->devices, bus_list) { - if (PCI_SLOT(pdev->devfn) == 0) { - if (pci_set_power_state(pdev, PCI_D0)) - dev_err(pcie->dev, - "Failed to transition %s to D0 state\n", - dev_name(&pdev->dev)); - } - } -} - static int tegra_pcie_get_slot_regulators(struct tegra_pcie_dw *pcie) { pcie->slot_ctl_3v3 = devm_regulator_get_optional(pcie->dev, "vpcie3v3"); @@ -1625,7 +1587,6 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) static void tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie) { - tegra_pcie_downstream_dev_to_D0(pcie); dw_pcie_host_deinit(&pcie->pci.pp); tegra_pcie_dw_pme_turnoff(pcie); tegra_pcie_unconfig_controller(pcie); @@ -2336,7 +2297,6 @@ static int tegra_pcie_dw_suspend_noirq(struct device *dev) if (!pcie->link_state) return 0; - tegra_pcie_downstream_dev_to_D0(pcie); tegra_pcie_dw_pme_turnoff(pcie); tegra_pcie_unconfig_controller(pcie); @@ -2410,7 +2370,6 @@ static void tegra_pcie_dw_shutdown(struct platform_device *pdev) return; debugfs_remove_recursive(pcie->debugfs); - tegra_pcie_downstream_dev_to_D0(pcie); disable_irq(pcie->pci.pp.irq); if (IS_ENABLED(CONFIG_PCI_MSI)) From 40658a31b6e134169c648041efc84944c4c71dcd Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:46 +0530 Subject: [PATCH 145/165] PCI: tegra194: Disable PERST# IRQ only in Endpoint mode The PERST# GPIO interrupt is only registered when the controller is operating in Endpoint mode. In Root Port mode, the PERST# GPIO is configured as an output to control downstream devices, and no interrupt is registered for it. Currently, tegra_pcie_dw_stop_link() unconditionally calls disable_irq() on pex_rst_irq, which causes issues in Root Port mode where this IRQ is not registered. Fix this by only disabling the PERST# IRQ when operating in Endpoint mode, where the interrupt is actually registered and used to detect PERST# assertion/deassertion from the host. Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194") Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-6-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index c84eb1ba3a11..ceb34110a50b 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1025,7 +1025,8 @@ static void tegra_pcie_dw_stop_link(struct dw_pcie *pci) { struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); - disable_irq(pcie->pex_rst_irq); + if (pcie->of_data->mode == DW_PCIE_EP_TYPE) + disable_irq(pcie->pex_rst_irq); } static const struct dw_pcie_ops tegra_dw_pcie_ops = { From f62bc7917de1374dce86a852ffba8baf9cb7a56a Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:47 +0530 Subject: [PATCH 146/165] PCI: tegra194: Use devm_gpiod_get_optional() to parse "nvidia,refclk-select" The GPIO DT property "nvidia,refclk-select", to select the PCIe reference clock is optional. Use devm_gpiod_get_optional() to get it. Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-7-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index ceb34110a50b..71b80edd10c8 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1167,9 +1167,9 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) return err; } - pcie->pex_refclk_sel_gpiod = devm_gpiod_get(pcie->dev, - "nvidia,refclk-select", - GPIOD_OUT_HIGH); + pcie->pex_refclk_sel_gpiod = devm_gpiod_get_optional(pcie->dev, + "nvidia,refclk-select", + GPIOD_OUT_HIGH); if (IS_ERR(pcie->pex_refclk_sel_gpiod)) { int err = PTR_ERR(pcie->pex_refclk_sel_gpiod); const char *level = KERN_ERR; From 976f6763f57970388bcd7118931f33f447916927 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:48 +0530 Subject: [PATCH 147/165] PCI: tegra194: Disable direct speed change for Endpoint mode Pre-silicon simulation showed the controller operating in Endpoint mode initiating link speed change after completing Secondary Bus Reset. Ideally, the Root Port or the Switch Downstream Port should initiate the link speed change post SBR, not the Endpoint. So, as per the hardware team recommendation, disable direct speed change for the Endpoint mode to prevent it from initiating speed change after the physical layer link is up at Gen1, leaving speed change ownership with the host. Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-8-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 71b80edd10c8..4d8bfd3e34ec 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1805,6 +1805,10 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) reset_control_deassert(pcie->core_rst); + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + if (pcie->update_fc_fixup) { val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF); val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT; From b256493bf8cacf0e524bf4c10b5c4901d0c6cefe Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:49 +0530 Subject: [PATCH 148/165] PCI: tegra194: Set LTR message request before PCIe link up in Endpoint mode LTR message should be sent as soon as the Root Port enables LTR in the Endpoint mode. So set snoop and no-snoop LTR timing and LTR message request before the PCIe link comes up, so that the LTR message is sent upstream as soon as LTR is enabled. Without programming these values, the Endpoint would send latencies of 0 to the host, which will be inaccurate. Fixes: c57247f940e8 ("PCI: tegra: Add support for PCIe endpoint mode in Tegra194") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://patch.msgid.link/20260324190755.1094879-9-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 4d8bfd3e34ec..95dbf2102c89 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -485,15 +485,6 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg) if (val & PCI_COMMAND_MASTER) { ktime_t timeout; - /* 110us for both snoop and no-snoop */ - val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) | - FIELD_PREP(PCI_LTR_SCALE_MASK, 2) | - LTR_MSG_REQ | - FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) | - FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) | - LTR_NOSNOOP_MSG_REQ; - appl_writel(pcie, val, APPL_LTR_MSG_1); - /* Send LTR upstream */ val = appl_readl(pcie, APPL_LTR_MSG_2); val |= APPL_LTR_MSG_2_LTR_MSG_REQ_STATE; @@ -1803,6 +1794,15 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) val |= APPL_INTR_EN_L1_0_0_RDLH_LINK_UP_INT_EN; appl_writel(pcie, val, APPL_INTR_EN_L1_0_0); + /* 110us for both snoop and no-snoop */ + val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) | + FIELD_PREP(PCI_LTR_SCALE_MASK, 2) | + LTR_MSG_REQ | + FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) | + FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) | + LTR_NOSNOOP_MSG_REQ; + appl_writel(pcie, val, APPL_LTR_MSG_1); + reset_control_deassert(pcie->core_rst); val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); From c76f8eae7d4695b1176c4ea5eb93c17e16a20272 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:50 +0530 Subject: [PATCH 149/165] PCI: tegra194: Allow system suspend when the Endpoint link is not up Host software initiates the L2 sequence. PCIe link is kept in L2 state during suspend. If Endpoint mode is enabled and the link is up, the software cannot proceed with suspend. However, when the PCIe Endpoint driver is probed, but the PCIe link is not up, Tegra can go into suspend state. So, allow system to suspend in this case. Fixes: de2bbf2b71bb ("PCI: tegra194: Don't allow suspend when Tegra PCIe is in EP mode") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-10-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 33 +++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 95dbf2102c89..06742796c332 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2270,16 +2270,28 @@ static void tegra_pcie_dw_remove(struct platform_device *pdev) gpiod_set_value(pcie->pex_refclk_sel_gpiod, 0); } +static int tegra_pcie_dw_suspend(struct device *dev) +{ + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + + if (pcie->of_data->mode == DW_PCIE_EP_TYPE) { + if (pcie->ep_state == EP_STATE_ENABLED) { + dev_err(dev, "Tegra PCIe is in EP mode, suspend not allowed\n"); + return -EPERM; + } + + disable_irq(pcie->pex_rst_irq); + return 0; + } + + return 0; +} + static int tegra_pcie_dw_suspend_late(struct device *dev) { struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); u32 val; - if (pcie->of_data->mode == DW_PCIE_EP_TYPE) { - dev_err(dev, "Failed to Suspend as Tegra PCIe is in EP mode\n"); - return -EPERM; - } - if (!pcie->link_state) return 0; @@ -2299,6 +2311,9 @@ static int tegra_pcie_dw_suspend_noirq(struct device *dev) { struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + if (pcie->of_data->mode == DW_PCIE_EP_TYPE) + return 0; + if (!pcie->link_state) return 0; @@ -2313,6 +2328,9 @@ static int tegra_pcie_dw_resume_noirq(struct device *dev) struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); int ret; + if (pcie->of_data->mode == DW_PCIE_EP_TYPE) + return 0; + if (!pcie->link_state) return 0; @@ -2345,8 +2363,8 @@ static int tegra_pcie_dw_resume_early(struct device *dev) u32 val; if (pcie->of_data->mode == DW_PCIE_EP_TYPE) { - dev_err(dev, "Suspend is not supported in EP mode"); - return -ENOTSUPP; + enable_irq(pcie->pex_rst_irq); + return 0; } if (!pcie->link_state) @@ -2451,6 +2469,7 @@ static const struct of_device_id tegra_pcie_dw_of_match[] = { }; static const struct dev_pm_ops tegra_pcie_dw_pm_ops = { + .suspend = tegra_pcie_dw_suspend, .suspend_late = tegra_pcie_dw_suspend_late, .suspend_noirq = tegra_pcie_dw_suspend_noirq, .resume_noirq = tegra_pcie_dw_resume_noirq, From 8870f02f7868209eb9bdc5dc53540a6262cf9227 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:51 +0530 Subject: [PATCH 150/165] PCI: tegra194: Free up Endpoint resources during remove() Free up the resources during remove() that were acquired by the DesignWare driver for the Endpoint mode during probe(). Fixes: bb617cbd8151 ("PCI: tegra194: Clean up the exit path for Endpoint mode") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-11-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 06742796c332..3527a4e82bac 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2251,6 +2251,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) static void tegra_pcie_dw_remove(struct platform_device *pdev) { struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); + struct dw_pcie_ep *ep = &pcie->pci.ep; if (pcie->of_data->mode == DW_PCIE_RC_TYPE) { if (!pcie->link_state) @@ -2262,6 +2263,7 @@ static void tegra_pcie_dw_remove(struct platform_device *pdev) } else { disable_irq(pcie->pex_rst_irq); pex_ep_event_pex_rst_assert(pcie); + dw_pcie_ep_deinit(ep); } pm_runtime_disable(pcie->dev); From ea60ca067f0f098043610c96a915d162113c1aac Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:52 +0530 Subject: [PATCH 151/165] PCI: tegra194: Use DWC IP core version Tegra194 PCIe driver used custom version numbers to detect Tegra194 and Tegra234 IPs. With version detect logic added, version check results in mismatch warnings: tegra194-pcie 14100000.pcie: Versions don't match (0000562a != 3536322a) Use HW version numbers which match to PORT_LOGIC.PCIE_VERSION_OFF in Tegra194 driver to avoid these kernel warnings. Fixes: a54e19073718 ("PCI: tegra194: Add Tegra234 PCIe support") Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-12-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-designware.h | 2 ++ drivers/pci/controller/dwc/pcie-tegra194.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index ae6389dd9caa..82946bf78b21 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -34,8 +34,10 @@ #define DW_PCIE_VER_470A 0x3437302a #define DW_PCIE_VER_480A 0x3438302a #define DW_PCIE_VER_490A 0x3439302a +#define DW_PCIE_VER_500A 0x3530302a #define DW_PCIE_VER_520A 0x3532302a #define DW_PCIE_VER_540A 0x3534302a +#define DW_PCIE_VER_562A 0x3536322a #define __dw_pcie_ver_cmp(_pci, _ver, _op) \ ((_pci)->version _op DW_PCIE_VER_ ## _ver) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 3527a4e82bac..688da5a73d02 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -35,8 +35,8 @@ #include #include "../../pci.h" -#define TEGRA194_DWC_IP_VER 0x490A -#define TEGRA234_DWC_IP_VER 0x562A +#define TEGRA194_DWC_IP_VER DW_PCIE_VER_500A +#define TEGRA234_DWC_IP_VER DW_PCIE_VER_562A #define APPL_PINMUX 0x0 #define APPL_PINMUX_PEX_RST BIT(0) From 40805f32dceadebb7381d911003100bec7b8cd51 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:53 +0530 Subject: [PATCH 152/165] PCI: dwc: Apply ECRC workaround to DesignWare 5.00a as well The ECRC (TLP digest) workaround was originally added for DesignWare version 4.90a. Tegra234 SoC has 5.00a DWC HW version, which has the same ATU TD override behaviour, so apply the workaround for 5.00a too. Fixes: a54e19073718 ("PCI: tegra194: Add Tegra234 PCIe support") Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-13-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-designware.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 5741c09dde7f..bb4e82fbfd5c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -487,13 +487,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg static inline u32 dw_pcie_enable_ecrc(u32 val) { /* - * DesignWare core version 4.90A has a design issue where the 'TD' - * bit in the Control register-1 of the ATU outbound region acts - * like an override for the ECRC setting, i.e., the presence of TLP - * Digest (ECRC) in the outgoing TLPs is solely determined by this - * bit. This is contrary to the PCIe spec which says that the - * enablement of the ECRC is solely determined by the AER - * registers. + * DWC versions 0x3530302a and 0x3536322a have a design issue where + * the 'TD' bit in the Control register-1 of the ATU outbound + * region acts like an override for the ECRC setting, i.e., the + * presence of TLP Digest (ECRC) in the outgoing TLPs is solely + * determined by this bit. This is contrary to the PCIe spec which + * says that the enablement of the ECRC is solely determined by the + * AER registers. * * Because of this, even when the ECRC is enabled through AER * registers, the transactions going through ATU won't have TLP @@ -563,7 +563,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (dw_pcie_ver_is(pci, 490A)) + if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A)) val = dw_pcie_enable_ecrc(val); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); From f59df1d9e6bdb6bd7ef65fb5d200900ac40c20ba Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:37:54 +0530 Subject: [PATCH 153/165] PCI: tegra194: Disable L1.2 capability of Tegra234 EP When Tegra234 is operating in the Endpoint mode with L1.2 enabled, PCIe link goes down during L1.2 exit. This is because Tegra234 powers up UPHY PLL immediately without making sure that the REFCLK is stable. This causes UPHY PLL to fail to lock to the correct frequency and leads to link going down. There is no hardware fix for this, hence do not advertise the L1.2 capability in the Endpoint mode. Fixes: a54e19073718 ("PCI: tegra194: Add Tegra234 PCIe support") Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-14-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 688da5a73d02..eb24f88e0175 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -234,6 +234,7 @@ struct tegra_pcie_dw_of_data { bool has_sbr_reset_fix; bool has_l1ss_exit_fix; bool has_ltr_req_fix; + bool disable_l1_2; u32 cdm_chk_int_en_bit; u32 gen4_preset_vec; u8 n_fts[2]; @@ -679,6 +680,23 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie) if (pcie->supports_clkreq) pci->l1ss_support = true; + /* + * Disable L1.2 capability advertisement for Tegra234 Endpoint mode. + * Tegra234 has a hardware bug where during L1.2 exit, the UPHY PLL is + * powered up immediately without waiting for REFCLK to stabilize. This + * causes the PLL to fail to lock to the correct frequency, resulting in + * PCIe link loss. Since there is no hardware fix available, we prevent + * the Endpoint from advertising L1.2 support by clearing the L1.2 bits + * in the L1 PM Substates Capabilities register. This ensures the host + * will not attempt to enter L1.2 state with this Endpoint. + */ + if (pcie->of_data->disable_l1_2 && + pcie->of_data->mode == DW_PCIE_EP_TYPE) { + val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP); + val &= ~(PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2); + dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val); + } + /* Program L0s and L1 entrance latencies */ val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR); val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK; @@ -2444,6 +2462,7 @@ static const struct tegra_pcie_dw_of_data tegra234_pcie_dw_ep_of_data = { .mode = DW_PCIE_EP_TYPE, .has_l1ss_exit_fix = true, .has_ltr_req_fix = true, + .disable_l1_2 = true, .cdm_chk_int_en_bit = BIT(18), /* Gen4 - 6, 8 and 9 presets enabled */ .gen4_preset_vec = 0x340, From 34b3eef48d980cd37b876e128bbf314f69fb5d70 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 25 Mar 2026 00:37:55 +0530 Subject: [PATCH 154/165] PCI: tegra194: Fix CBB timeout caused by DBI access before core power-on When PERST# is deasserted twice (assert -> deassert -> assert -> deassert), a CBB (Control Backbone) timeout occurs at DBI register offset 0x8bc (PCIE_MISC_CONTROL_1_OFF). This happens because pci_epc_deinit_notify() and dw_pcie_ep_cleanup() are called before reset_control_deassert() powers on the controller core. The call chain that causes the timeout: pex_ep_event_pex_rst_deassert() pci_epc_deinit_notify() pci_epf_test_epc_deinit() pci_epf_test_clear_bar() pci_epc_clear_bar() dw_pcie_ep_clear_bar() __dw_pcie_ep_reset_bar() dw_pcie_dbi_ro_wr_en() <- Accesses 0x8bc DBI register reset_control_deassert(pcie->core_rst) <- Core powered on HERE The DBI registers, including PCIE_MISC_CONTROL_1_OFF (0x8bc), are only accessible after the controller core is powered on via reset_control_deassert(pcie->core_rst). Accessing them before this point results in a CBB timeout because the hardware is not yet operational. Fix this by moving pci_epc_deinit_notify() and dw_pcie_ep_cleanup() to after reset_control_deassert(pcie->core_rst), ensuring the controller is fully powered on before any DBI register accesses occur. Fixes: 40e2125381dc ("PCI: tegra194: Move controller cleanups to pex_ep_event_pex_rst_deassert()") Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324190755.1094879-15-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index eb24f88e0175..336d3c759547 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1750,10 +1750,6 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) goto fail_phy; } - /* Perform cleanup that requires refclk */ - pci_epc_deinit_notify(pcie->pci.ep.epc); - dw_pcie_ep_cleanup(&pcie->pci.ep); - /* Clear any stale interrupt statuses */ appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0); appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0); @@ -1823,6 +1819,10 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) reset_control_deassert(pcie->core_rst); + /* Perform cleanup that requires refclk and core reset deasserted */ + pci_epc_deinit_notify(pcie->pci.ep.epc); + dw_pcie_ep_cleanup(&pcie->pci.ep); + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); val &= ~PORT_LOGIC_SPEED_CHANGE; dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); From 01d36261ae331583e6bc2034e6aa75c101b83e1d Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:52 +0530 Subject: [PATCH 155/165] PCI: tegra194: Assert CLKREQ# explicitly by default The Root Port's CLKREQ# signal is shared with a downstream PCIe switch and the endpoints behind it. By default, APPL_PINMUX_CLKREQ_OVERRIDE only overrides the CLKREQ# input to the controller (so REFCLK is enabled internally); it does not drive the CLKREQ# output pin low. Some PCIe switches (e.g. Broadcom PCIe Gen4) forward the Root Port's CLKREQ# to their downstream side and expect it to be driven low for REFCLK, even when the switch does not support CLK-PM or ASPM-L1SS. Without driving the output pin low, link-up can fail between the switch and endpoints. Clear APPL_PINMUX_CLKREQ_DEFAULT_VALUE so the CLKREQ# output pad is explicitly driven low. That makes the shared CLKREQ# line low on the wire and avoids link-up issues with such switches. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Vidya Sagar Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324191000.1095768-2-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 336d3c759547..002945de5e11 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -44,6 +44,7 @@ #define APPL_PINMUX_CLKREQ_OVERRIDE BIT(3) #define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN BIT(4) #define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE BIT(5) +#define APPL_PINMUX_CLKREQ_DEFAULT_VALUE BIT(13) #define APPL_CTRL 0x4 #define APPL_CTRL_SYS_PRE_DET_STATE BIT(6) @@ -1429,6 +1430,7 @@ static int tegra_pcie_config_controller(struct tegra_pcie_dw *pcie, val = appl_readl(pcie, APPL_PINMUX); val |= APPL_PINMUX_CLKREQ_OVERRIDE_EN; val &= ~APPL_PINMUX_CLKREQ_OVERRIDE; + val &= ~APPL_PINMUX_CLKREQ_DEFAULT_VALUE; appl_writel(pcie, val, APPL_PINMUX); } From f50e0c7d57b08dfbd6a2aab1eed8f99dd8e81377 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:53 +0530 Subject: [PATCH 156/165] PCI: tegra194: Calibrate pipe to UPHY for Endpoint mode Calibrate 'Pipe to Universal PHY(UPHY)' (P2U) for the Endpoint controller to request UPHY PLL rate change to 2.5GT/s (Gen 1) during initialization. This helps to reset stale PLL state from the previous bad link state. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324191000.1095768-3-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 002945de5e11..7f74d72a21dd 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1072,6 +1072,9 @@ static int tegra_pcie_enable_phy(struct tegra_pcie_dw *pcie) ret = phy_power_on(pcie->phys[i]); if (ret < 0) goto phy_exit; + + if (pcie->of_data->mode == DW_PCIE_EP_TYPE) + phy_calibrate(pcie->phys[i]); } return 0; From 323a6e370af56c550255aeeb9264d1f493d878a9 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:54 +0530 Subject: [PATCH 157/165] PCI: tegra194: Remove IRQF_ONESHOT flag during Endpoint interrupt registration The Tegra PCIe Endpoint controller has a single interrupt line that is shared between multiple interrupt sources: 1. PCIe link state events (link up, hot reset done) 2. Configuration space events (Bus Master Enable changes) 3. DMA completion events The interrupt is currently registered with IRQF_ONESHOT, which keeps the interrupt line masked until the threaded handler completes. That blocks processing of DMA completion events (and other sources) while the threaded handler runs. Removing IRQF_ONESHOT is safe for the following reasons: 1. The hard IRQ handler (tegra_pcie_ep_hard_irq) properly acknowledges and clears all interrupt status bits in hardware before returning. This prevents interrupt storms and ensures the interrupt controller can re-enable the interrupt line immediately. 2. The follow-up commit adds handling in the hard IRQ for DMA completion events. Dropping IRQF_ONESHOT is required so the line is unmasked after the hard IRQ returns and those events can be serviced without being blocked by the threaded handler. 3. The threaded handler (tegra_pcie_ep_irq_thread) only processes link-up notifications and LTR message sending. These operations don't conflict with DMA interrupt processing and don't require the interrupt line to remain masked. This change enables both DMA driver and Endpoint controller driver to share the interrupt line without blocking each other. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324191000.1095768-4-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 7f74d72a21dd..91b3953f780a 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2245,7 +2245,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, pp->irq, tegra_pcie_ep_hard_irq, tegra_pcie_ep_irq_thread, - IRQF_SHARED | IRQF_ONESHOT, + IRQF_SHARED, "tegra-pcie-ep-intr", pcie); if (ret) { dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq, From 66861c592af8c2176a36f52e6b3949e8c98e5a8e Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:55 +0530 Subject: [PATCH 158/165] PCI: tegra194: Enable DMA interrupt Enable DMA interrupt to support Tegra PCIe DMA in both Root Port and Endpoint modes. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324191000.1095768-5-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 91b3953f780a..409f8eaceb39 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -91,6 +91,7 @@ #define APPL_INTR_EN_L1_8_0 0x44 #define APPL_INTR_EN_L1_8_BW_MGT_INT_EN BIT(2) #define APPL_INTR_EN_L1_8_AUTO_BW_INT_EN BIT(3) +#define APPL_INTR_EN_L1_8_EDMA_INT_EN BIT(6) #define APPL_INTR_EN_L1_8_INTX_EN BIT(11) #define APPL_INTR_EN_L1_8_AER_INT_EN BIT(15) @@ -544,6 +545,17 @@ static irqreturn_t tegra_pcie_ep_hard_irq(int irq, void *arg) spurious = 0; } + if (status_l0 & APPL_INTR_STATUS_L0_INT_INT) { + status_l1 = appl_readl(pcie, APPL_INTR_STATUS_L1_8_0); + + /* + * Interrupt is handled by DMA driver; don't treat it as + * spurious + */ + if (status_l1 & APPL_INTR_STATUS_L1_8_0_EDMA_INT_MASK) + spurious = 0; + } + if (spurious) { dev_warn(pcie->dev, "Random interrupt (STATUS = 0x%08X)\n", status_l0); @@ -780,6 +792,7 @@ static void tegra_pcie_enable_intx_interrupts(struct dw_pcie_rp *pp) val |= APPL_INTR_EN_L1_8_INTX_EN; val |= APPL_INTR_EN_L1_8_AUTO_BW_INT_EN; val |= APPL_INTR_EN_L1_8_BW_MGT_INT_EN; + val |= APPL_INTR_EN_L1_8_EDMA_INT_EN; if (IS_ENABLED(CONFIG_PCIEAER)) val |= APPL_INTR_EN_L1_8_AER_INT_EN; appl_writel(pcie, val, APPL_INTR_EN_L1_8_0); @@ -1806,6 +1819,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) val |= APPL_INTR_EN_L0_0_SYS_INTR_EN; val |= APPL_INTR_EN_L0_0_LINK_STATE_INT_EN; val |= APPL_INTR_EN_L0_0_PCI_CMD_EN_INT_EN; + val |= APPL_INTR_EN_L0_0_INT_INT_EN; appl_writel(pcie, val, APPL_INTR_EN_L0_0); val = appl_readl(pcie, APPL_INTR_EN_L1_0_0); @@ -1813,6 +1827,10 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) val |= APPL_INTR_EN_L1_0_0_RDLH_LINK_UP_INT_EN; appl_writel(pcie, val, APPL_INTR_EN_L1_0_0); + val = appl_readl(pcie, APPL_INTR_EN_L1_8_0); + val |= APPL_INTR_EN_L1_8_EDMA_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L1_8_0); + /* 110us for both snoop and no-snoop */ val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) | FIELD_PREP(PCI_LTR_SCALE_MASK, 2) | From acd46d51f22f0d4b83d4e34088e68498110085b5 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:56 +0530 Subject: [PATCH 159/165] PCI: tegra194: Enable hardware hot reset mode in Endpoint mode When PCIe link goes down, hardware can retrain the link and try to link up. To enable this feature, program the APPL_CTRL register with hardware hot reset with immediate LTSSM enable mode when the controller is operating in endpoint mode. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324191000.1095768-6-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 409f8eaceb39..eeb93cc1200a 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1796,6 +1796,8 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) val = appl_readl(pcie, APPL_CTRL); val |= APPL_CTRL_SYS_PRE_DET_STATE; val |= APPL_CTRL_HW_HOT_RST_EN; + val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK << APPL_CTRL_HW_HOT_RST_MODE_SHIFT); + val |= (APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST_LTSSM_EN << APPL_CTRL_HW_HOT_RST_MODE_SHIFT); appl_writel(pcie, val, APPL_CTRL); val = appl_readl(pcie, APPL_CFG_MISC); From 5aed9ab3dff22b8cae6f6ff901dee0a14941f7bc Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:57 +0530 Subject: [PATCH 160/165] dt-bindings: PCI: tegra194: Add monitor clock support Tegra supports PCIe core clock monitoring for any rate changes that may be happening because of the link speed changes. This is useful in tracking any changes in the core clock that are not initiated by the software. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Rob Herring (Arm) Reviewed-by: Jon Hunter Reviewed-by: Vidya Sagar Link: https://patch.msgid.link/20260324191000.1095768-7-mmaddireddy@nvidia.com --- .../devicetree/bindings/pci/nvidia,tegra194-pcie-ep.yaml | 6 +++++- .../devicetree/bindings/pci/nvidia,tegra194-pcie.yaml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie-ep.yaml index 6d6052a2748f..7805757f2e2d 100644 --- a/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie-ep.yaml @@ -55,12 +55,16 @@ properties: - const: intr clocks: + minItems: 1 items: - - description: module clock + - description: core clock + - description: monitor clock clock-names: + minItems: 1 items: - const: core + - const: core_m resets: items: diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie.yaml b/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie.yaml index fe81d52c7277..41041ae7e0a4 100644 --- a/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra194-pcie.yaml @@ -58,12 +58,16 @@ properties: - const: msi clocks: + minItems: 1 items: - - description: module clock + - description: core clock + - description: monitor clock clock-names: + minItems: 1 items: - const: core + - const: core_m resets: items: From a86ca8698c88461dd5770b638a2e2459f58d370c Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Wed, 25 Mar 2026 00:39:58 +0530 Subject: [PATCH 161/165] PCI: tegra194: Add core monitor clock support Add support for Tegra PCIe core clock monitoring. Monitoring tracks rate changes that may occur due to link speed changes and is useful for detecting core clock changes not initiated by software. Parse the monitor clock from device tree and enable it when present. Signed-off-by: Vidya Sagar Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260324191000.1095768-8-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index eeb93cc1200a..b00b58914b22 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -249,6 +249,7 @@ struct tegra_pcie_dw { struct resource *atu_dma_res; void __iomem *appl_base; struct clk *core_clk; + struct clk *core_clk_m; struct reset_control *core_apb_rst; struct reset_control *core_rst; struct dw_pcie pci; @@ -950,6 +951,8 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp) } clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ); + if (clk_prepare_enable(pcie->core_clk_m)) + dev_err(pci->dev, "Failed to enable core monitor clock\n"); return 0; } @@ -1022,6 +1025,12 @@ static int tegra_pcie_dw_start_link(struct dw_pcie *pci) val &= ~PCI_DLF_EXCHANGE_ENABLE; dw_pcie_writel_dbi(pci, offset + PCI_DLF_CAP, val); + /* + * core_clk_m is enabled as part of host_init callback in + * dw_pcie_host_init(). Disable the clock since below + * tegra_pcie_dw_host_init() will enable it again. + */ + clk_disable_unprepare(pcie->core_clk_m); tegra_pcie_dw_host_init(pp); dw_pcie_setup_rc(pp); @@ -1615,6 +1624,7 @@ static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) static void tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie) { + clk_disable_unprepare(pcie->core_clk_m); dw_pcie_host_deinit(&pcie->pci.pp); tegra_pcie_dw_pme_turnoff(pcie); tegra_pcie_unconfig_controller(pcie); @@ -2166,6 +2176,11 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) return PTR_ERR(pcie->core_clk); } + pcie->core_clk_m = devm_clk_get_optional(dev, "core_m"); + if (IS_ERR(pcie->core_clk_m)) + return dev_err_probe(dev, PTR_ERR(pcie->core_clk_m), + "Failed to get monitor clock\n"); + pcie->appl_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "appl"); if (!pcie->appl_res) { @@ -2362,6 +2377,7 @@ static int tegra_pcie_dw_suspend_noirq(struct device *dev) if (!pcie->link_state) return 0; + clk_disable_unprepare(pcie->core_clk_m); tegra_pcie_dw_pme_turnoff(pcie); tegra_pcie_unconfig_controller(pcie); From 5ccc76a87f1ec2422811e61be44165bfc9e7cf54 Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Sun, 5 Apr 2026 15:41:53 +0000 Subject: [PATCH 162/165] PCI: cadence: Add flags for disabling ASPM capability for broken Root Ports Add flags for disabling the ASPM L0s/L1 capability for broken Root Ports by clearing the corresponding bits in Link Capabilities Register through the local management bus. This allows ASPM to be disabled on platforms which don't support it. Signed-off-by: Yao Zi Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Han Gao Tested-by: Chen Wang # Pioneerbox Reviewed-by: Chen Wang Link: https://patch.msgid.link/20260405154154.46829-2-me@ziyao.cc --- .../controller/cadence/pcie-cadence-host.c | 7 +++++++ drivers/pci/controller/cadence/pcie-cadence.h | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index db3154c1eccb..0bc9e6e90e0e 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -147,6 +147,13 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); + value = cdns_pcie_rp_readl(pcie, CDNS_PCIE_RP_CAP_OFFSET + PCI_EXP_LNKCAP); + if (rc->quirk_broken_aspm_l0s) + value &= ~PCI_EXP_LNKCAP_ASPM_L0S; + if (rc->quirk_broken_aspm_l1) + value &= ~PCI_EXP_LNKCAP_ASPM_L1; + cdns_pcie_rp_writel(pcie, CDNS_PCIE_RP_CAP_OFFSET + PCI_EXP_LNKCAP, value); + return 0; } diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 443033c607d7..efbda0b3ec2c 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -115,6 +115,8 @@ struct cdns_pcie { * @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk * @ecam_supported: Whether the ECAM is supported * @no_inbound_map: Whether inbound mapping is supported + * @quirk_broken_aspm_l0s: Disable ASPM L0s support as quirk + * @quirk_broken_aspm_l1: Disable ASPM L1 support as quirk */ struct cdns_pcie_rc { struct cdns_pcie pcie; @@ -127,6 +129,8 @@ struct cdns_pcie_rc { unsigned int quirk_detect_quiet_flag:1; unsigned int ecam_supported:1; unsigned int no_inbound_map:1; + unsigned int quirk_broken_aspm_l0s:1; + unsigned int quirk_broken_aspm_l1:1; }; /** @@ -344,6 +348,21 @@ static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg) return cdns_pcie_read_sz(addr, 0x2); } +static inline void cdns_pcie_rp_writel(struct cdns_pcie *pcie, + u32 reg, u32 value) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + cdns_pcie_write_sz(addr, 0x4, value); +} + +static inline u32 cdns_pcie_rp_readl(struct cdns_pcie *pcie, u32 reg) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + return cdns_pcie_read_sz(addr, 0x4); +} + static inline void cdns_pcie_hpa_rp_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) { From 988ef706cdd8a72e61dd90c0d0554eec4df7594a Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Sun, 5 Apr 2026 15:41:54 +0000 Subject: [PATCH 163/165] PCI: sg2042: Avoid L0s and L1 on Sophgo 2042 PCIe Root Ports Since commit f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") force enables ASPM on all device tree platforms, the SG2042 Root Ports are breaking as they advertise L0s and L1 capabilities without supporting them. Set ASPM quirks to disable the L0s and L1 capabilities for the Root Ports so that these broken link states won't be enabled. Fixes: 4e27aca4881a ("riscv: sophgo: dts: add PCIe controllers for SG2042") Co-developed-by: Inochi Amaoto Signed-off-by: Inochi Amaoto Signed-off-by: Yao Zi [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Han Gao Tested-by: Chen Wang # Pioneerbox Reviewed-by: Chen Wang Link: https://patch.msgid.link/20260405154154.46829-3-me@ziyao.cc --- drivers/pci/controller/cadence/pcie-sg2042.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-sg2042.c b/drivers/pci/controller/cadence/pcie-sg2042.c index 0c50c74d03ee..4a2af4d0713e 100644 --- a/drivers/pci/controller/cadence/pcie-sg2042.c +++ b/drivers/pci/controller/cadence/pcie-sg2042.c @@ -48,6 +48,8 @@ static int sg2042_pcie_probe(struct platform_device *pdev) bridge->child_ops = &sg2042_pcie_child_ops; rc = pci_host_bridge_priv(bridge); + rc->quirk_broken_aspm_l0s = 1; + rc->quirk_broken_aspm_l1 = 1; pcie = &rc->pcie; pcie->dev = dev; From 1a1f96e3e2a8dc1774c626bc6e15944c3e160393 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 9 Apr 2026 15:01:22 +0000 Subject: [PATCH 164/165] PCI/P2PDMA: Allow wildcard Device IDs in host bridge list Currently, the pci_p2pdma_whitelist array requires an exact match for both Vendor and Device ID. Some hardware vendors support cross bridge peer-to-peer DMA across their entire silicon lineup, so add support for wildcard device IDs to avoid the need to continuously update this array. Signed-off-by: Jacob Moroni Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe Link: https://patch.msgid.link/20260409150123.3538444-1-jmoroni@google.com --- drivers/pci/p2pdma.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index e0f546166eb8..25e9358d066e 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -530,7 +530,7 @@ static bool cpu_supports_p2pdma(void) static const struct pci_p2pdma_whitelist_entry { unsigned short vendor; - unsigned short device; + int device; enum { REQ_SAME_HOST_BRIDGE = 1 << 0, } flags; @@ -601,8 +601,12 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host, device = root->device; for (entry = pci_p2pdma_whitelist; entry->vendor; entry++) { - if (vendor != entry->vendor || device != entry->device) + if (vendor != entry->vendor) continue; + + if (entry->device != PCI_ANY_ID && device != entry->device) + continue; + if (entry->flags & REQ_SAME_HOST_BRIDGE && !same_host_bridge) return false; From 70cf146a674c447753ceeb34246ad0afdd0064bb Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 9 Apr 2026 15:01:23 +0000 Subject: [PATCH 165/165] PCI/P2PDMA: Add Google SoCs to the P2P DMA host bridge list All Google SoCs support peer-to-peer DMA between Root Ports, so add a wildcard rule to the host bridge list. Signed-off-by: Jacob Moroni Signed-off-by: Bjorn Helgaas Tested-by: David Hu Reviewed-by: Logan Gunthorpe Link: https://patch.msgid.link/20260409150123.3538444-2-jmoroni@google.com --- drivers/pci/p2pdma.c | 2 ++ include/linux/pci_ids.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 25e9358d066e..7c898542af8d 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -548,6 +548,8 @@ static const struct pci_p2pdma_whitelist_entry { {PCI_VENDOR_ID_INTEL, 0x2033, 0}, {PCI_VENDOR_ID_INTEL, 0x2020, 0}, {PCI_VENDOR_ID_INTEL, 0x09a2, 0}, + /* Google SoCs. */ + {PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0}, {} }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 406abf629be2..24cb42f66e4b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2586,6 +2586,8 @@ #define PCI_VENDOR_ID_AZWAVE 0x1a3b +#define PCI_VENDOR_ID_GOOGLE 0x1ae0 + #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 #define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 #define PCI_SUBDEVICE_ID_QEMU 0x1100