* [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support.
@ 2026-05-21 17:19 Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 1/3] PCI: mediatek: Use actual physical address instead of virt_to_phys() Caleb James DeLisle
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Caleb James DeLisle @ 2026-05-21 17:19 UTC (permalink / raw)
To: linux-pci
Cc: linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi, kwilczynski,
mani, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel, Caleb James DeLisle
Tested on TpLink Archer VR1200V-V2 (EN751221 with Gen2 device)
Changes since v8:
* guard(rwsem_read)(&pci_bus_sem); in mtk_pcie_retrain
* v8: https://lore.kernel.org/linux-mips/20260520183827.908243-1-cjd@cjdns.fr
Changes from v7:
* mtk_pcie_retrain retrain all root ports not just first
* Include fix from Manivannan Sadhasivam, wrong usage of virt_to_phys()
* v7: https://lore.kernel.org/linux-mips/20260514151318.3444959-1-cjd@cjdns.fr
Changes from v6:
* s/reset/resets/ in .yaml
* s/re-train/retrain/g
* s/Root bridge/Root port/
* If module not builtin, log at mtk_pcie_startup_port_en7528()
* Do not fail if error in mtk_pcie_retrain()
* v6: https://lore.kernel.org/linux-mips/20260513191652.3200607-1-cjd@cjdns.fr
Changes from v5:
* s/errno-base.h/errno.h/
* Breakout mtk_pcie_retrain() into a function
* Use for_each_pci_bridge() to find root bridge
* v5: https://lore.kernel.org/linux-mips/20260413140339.16238-1-cjd@cjdns.fr/
Changes from v4:
* Fixed missing Acked-by
* Rebased to commit 66672af7a095 ("Add linux-next specific files for 20260410")
* v4: https://lore.kernel.org/linux-mips/20260404182854.2183651-1-cjd@cjdns.fr/
Changes from v3:
* s/initiallized/initialized/
* Use PCIE_T_PVPERL_MS for sleep time
* Use PCI_PM_D3COLD_WAIT for startup wait time
* Clarify comment "Activate INTx interrupts"
* Add MTK_PCIE_RETRAIN quirk for devices which require link re-train
* Do not retrain *all* bridges, only root bridge
* Better comments and logging in retraining logic
* v3: https://lore.kernel.org/linux-mips/20260320094212.696671-1-cjd@cjdns.fr/
Changes from v2:
* mediatek-pcie.yaml -> s/power-domain/power-domains/ and drop example
* Patch 3 dropped as it has been applied (Thanks!)
* v2: https://lore.kernel.org/linux-mips/20260316155157.679533-1-cjd@cjdns.fr/
Changes from v1:
* mediatek-pcie.yaml slot0 needs device-type = "pci", fix dt_binding_check
Link: https://lore.kernel.org/linux-mips/177334026016.3889069.9474337544951486443.robh@kernel.org
* v1: https://lore.kernel.org/linux-mips/20260312165332.569772-1-cjd@cjdns.fr/
This was split from a larger PCIe patchset which crossed multiple
subsystems. I'm not labeling this a v3 because it's a new patchset, but
I'm keeping the historical record anyway.
Changes from econet-pcie v2:
* mediatek-pcie.yaml add missing constraints to PCI node properties
* econet-pcie v2: https://lore.kernel.org/linux-mips/20260309131818.74467-1-cjd@cjdns.fr
Changes from econet-pcie v1:
* pcie-mediatek.c Exclude pcie_retrain_link() when building as a module
* econet-pcie v1: https://lore.kernel.org/linux-mips/20260303190948.694783-1-cjd@cjdns.fr/
Caleb James DeLisle (2):
dt-bindings: PCI: mediatek: Add support for EcoNet EN7528
PCI: mediatek: Add support for EcoNet EN7528 SoC
Manivannan Sadhasivam (1):
PCI: mediatek: Use actual physical address instead of virt_to_phys()
.../bindings/pci/mediatek-pcie.yaml | 26 +++
drivers/pci/controller/Kconfig | 2 +-
drivers/pci/controller/pcie-mediatek.c | 170 +++++++++++++++++-
3 files changed, 194 insertions(+), 4 deletions(-)
base-commit: 687da68900cd1a46549f7d9430c7d40346cb86a0
--
2.39.5
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v9 1/3] PCI: mediatek: Use actual physical address instead of virt_to_phys()
2026-05-21 17:19 [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Caleb James DeLisle
@ 2026-05-21 17:19 ` Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 2/3] dt-bindings: PCI: mediatek: Add support for EcoNet EN7528 Caleb James DeLisle
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Caleb James DeLisle @ 2026-05-21 17:19 UTC (permalink / raw)
To: linux-pci
Cc: linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi, kwilczynski,
mani, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel, Manivannan Sadhasivam, Caleb James DeLisle
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
The driver previously used virt_to_phys() on the ioremapped register base
(port->base) to compute the MSI message address. Using virt_to_phys() on an
IO mapped address is incorrect because it expects a kernel virtual address.
To fix it, store the physical start of the I/O register region in
mtk_pcie_port->phys_base and use it to build the MSI address. This replaces
the incorrect virt_to_phys() usage and ensures MSI addresses are generated
correctly.
Fixes: 43e6409db64d ("PCI: mediatek: Add MSI support for MT2712 and MT7622")
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Tested-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/pci/controller/pcie-mediatek.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 75722524fe74..c503fbd774d0 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -175,6 +175,7 @@ struct mtk_pcie_soc {
/**
* struct mtk_pcie_port - PCIe port information
* @base: IO mapped register base
+ * @phys_base: Physical address of the I/O register base region
* @list: port list
* @pcie: pointer to PCIe host info
* @reset: pointer to port reset control
@@ -196,6 +197,7 @@ struct mtk_pcie_soc {
*/
struct mtk_pcie_port {
void __iomem *base;
+ phys_addr_t phys_base;
struct list_head list;
struct mtk_pcie *pcie;
struct reset_control *reset;
@@ -405,7 +407,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
phys_addr_t addr;
/* MT2712/MT7622 only support 32-bit MSI addresses */
- addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+ addr = port->phys_base + PCIE_MSI_VECTOR;
msg->address_hi = 0;
msg->address_lo = lower_32_bits(addr);
@@ -520,7 +522,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
u32 val;
phys_addr_t msg_addr;
- msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+ msg_addr = port->phys_base + PCIE_MSI_VECTOR;
val = lower_32_bits(msg_addr);
writel(val, port->base + PCIE_IMSI_ADDR);
@@ -953,6 +955,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);
+ struct resource *res;
char name[20];
int err;
@@ -961,7 +964,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
return -ENOMEM;
snprintf(name, sizeof(name), "port%d", slot);
- port->base = devm_platform_ioremap_resource_byname(pdev, name);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res) {
+ dev_err(dev, "failed to get port%d base\n", slot);
+ return -EINVAL;
+ }
+
+ port->phys_base = res->start;
+ port->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(port->base)) {
dev_err(dev, "failed to map port%d base\n", slot);
return PTR_ERR(port->base);
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v9 2/3] dt-bindings: PCI: mediatek: Add support for EcoNet EN7528
2026-05-21 17:19 [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 1/3] PCI: mediatek: Use actual physical address instead of virt_to_phys() Caleb James DeLisle
@ 2026-05-21 17:19 ` Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
2026-06-11 7:12 ` [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Manivannan Sadhasivam
3 siblings, 0 replies; 8+ messages in thread
From: Caleb James DeLisle @ 2026-05-21 17:19 UTC (permalink / raw)
To: linux-pci
Cc: linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi, kwilczynski,
mani, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel, Caleb James DeLisle, Conor Dooley
Introduce EcoNet EN7528 SoC compatible in MediaTek PCIe controller
binding.
EcoNet PCIe controller has the same configuration model as
Mediatek v2 but is initialized more similarly to an MT7621
PCIe.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../bindings/pci/mediatek-pcie.yaml | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml
index 0b8c78ec4f91..c009a7a52bc6 100644
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml
@@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- airoha,an7583-pcie
+ - econet,en7528-pcie
- mediatek,mt2712-pcie
- mediatek,mt7622-pcie
- mediatek,mt7629-pcie
@@ -226,6 +227,31 @@ allOf:
mediatek,pbus-csr: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: econet,en7528-pcie
+ then:
+ properties:
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ maxItems: 1
+
+ resets: false
+
+ reset-names: false
+
+ power-domains: false
+
+ mediatek,pbus-csr: false
+
+ required:
+ - phys
+ - phy-names
+
unevaluatedProperties: false
examples:
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC
2026-05-21 17:19 [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 1/3] PCI: mediatek: Use actual physical address instead of virt_to_phys() Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 2/3] dt-bindings: PCI: mediatek: Add support for EcoNet EN7528 Caleb James DeLisle
@ 2026-05-21 17:19 ` Caleb James DeLisle
2026-05-21 18:27 ` sashiko-bot
2026-06-11 7:12 ` [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Manivannan Sadhasivam
3 siblings, 1 reply; 8+ messages in thread
From: Caleb James DeLisle @ 2026-05-21 17:19 UTC (permalink / raw)
To: linux-pci
Cc: linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi, kwilczynski,
mani, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel, Caleb James DeLisle
Add support for the PCIe present on the EcoNet EN7528 (and EN751221) SoCs.
These SoCs have a mix of Gen1 and Gen2 capable ports, but the Gen2 ports
require re-training after startup.
Co-developed-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
Before sending v9 I did some investigation to see if
pcie_failed_link_retrain() in quirks.c could be a suitable replacement
for mtk_pcie_retrain().
I discovered that it exits early on this hardware because the hardware
does not set PCI_EXP_LNKCAP_DLLLARC, and link_active_reporting is
tested.
The hardware has a register bit which claims to enable Data Link Layer
active reporting, but setting it during init does not cause
link_active_reporting to become true. Furthermore, skipping the check
in pcie_failed_link_retrain() makes the function run, but the port does
not upgrade to Gen2. Therefore I determined that it is most prudent to
keep with the mtk_pcie_retrain() logic which follows behavior of
reference code and is shown to work.
---
drivers/pci/controller/Kconfig | 2 +-
drivers/pci/controller/pcie-mediatek.c | 154 +++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 2247709ef6d6..8a3a31b2bc12 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -209,7 +209,7 @@ config PCI_MVEBU
config PCIE_MEDIATEK
tristate "MediaTek PCIe controller"
- depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
+ depends on ARCH_AIROHA || ARCH_MEDIATEK || ECONET || COMPILE_TEST
depends on OF
depends on PCI_MSI
select IRQ_MSI_LIB
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index c503fbd774d0..30f8070967b8 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -9,11 +9,13 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
#include <linux/irqdomain.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/msi.h>
@@ -77,6 +79,7 @@
#define PCIE_CONF_VEND_ID 0x100
#define PCIE_CONF_DEVICE_ID 0x102
+#define PCIE_CONF_REV_CLASS 0x104
#define PCIE_CONF_CLASS_ID 0x106
#define PCIE_INT_MASK 0x420
@@ -89,6 +92,11 @@
#define MSI_MASK BIT(23)
#define MTK_MSI_IRQS_NUM 32
+#define EN7528_HOST_MODE 0x00804201
+#define EN7528_LINKUP_REG 0x50
+#define EN7528_RC0_LINKUP BIT(1)
+#define EN7528_RC1_LINKUP BIT(2)
+
#define PCIE_AHB_TRANS_BASE0_L 0x438
#define PCIE_AHB_TRANS_BASE0_H 0x43c
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
@@ -148,12 +156,15 @@ struct mtk_pcie_port;
* @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
* @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
* @MTK_PCIE_SKIP_RSTB: Skip calling RSTB bits on PCIe probe
+ * @MTK_PCIE_RETRAIN: Retrain link to bridge after startup because some
+ * Gen2-capable devices start as Gen1.
*/
enum mtk_pcie_quirks {
MTK_PCIE_FIX_CLASS_ID = BIT(0),
MTK_PCIE_FIX_DEVICE_ID = BIT(1),
MTK_PCIE_NO_MSI = BIT(2),
MTK_PCIE_SKIP_RSTB = BIT(3),
+ MTK_PCIE_RETRAIN = BIT(4),
};
/**
@@ -755,6 +766,134 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
return 0;
}
+static int mtk_pcie_startup_port_en7528(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct resource *mem = NULL;
+ struct resource_entry *entry;
+ u32 val, link_mask;
+ int err;
+
+ entry = resource_list_first_type(&host->windows, IORESOURCE_MEM);
+ if (entry)
+ mem = entry->res;
+ if (!mem)
+ return -EINVAL;
+
+ if (!pcie->cfg) {
+ dev_err(pcie->dev, "EN7528: pciecfg syscon not available\n");
+ return -EINVAL;
+ }
+
+ /* Assert all reset signals */
+ writel(0, port->base + PCIE_RST_CTRL);
+
+ /*
+ * Enable PCIe link down reset, if link status changed from link up to
+ * link down, this will reset MAC control registers and configuration
+ * space.
+ */
+ writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
+
+ msleep(PCIE_T_PVPERL_MS);
+
+ /* De-assert PHY, PE, PIPE, MAC and configuration reset */
+ val = readl(port->base + PCIE_RST_CTRL);
+ val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
+ PCIE_MAC_SRSTB | PCIE_CRSTB;
+ writel(val, port->base + PCIE_RST_CTRL);
+
+ writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
+ port->base + PCIE_CONF_REV_CLASS);
+ writel(EN7528_HOST_MODE, port->base);
+
+ link_mask = (port->slot == 0) ? EN7528_RC0_LINKUP : EN7528_RC1_LINKUP;
+
+ /* 100ms timeout value should be enough for Gen1/2 training */
+ err = regmap_read_poll_timeout(pcie->cfg, EN7528_LINKUP_REG, val,
+ !!(val & link_mask), 20,
+ PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
+ if (err) {
+ dev_err(pcie->dev, "EN7528: port%d link timeout\n", port->slot);
+ return -ETIMEDOUT;
+ }
+
+ /* Activate INTx interrupts */
+ val = readl(port->base + PCIE_INT_MASK);
+ val &= ~INTX_MASK;
+ writel(val, port->base + PCIE_INT_MASK);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ mtk_pcie_enable_msi(port);
+
+ /* Set AHB to PCIe translation windows */
+ val = lower_32_bits(mem->start) |
+ AHB2PCIE_SIZE(fls(resource_size(mem)));
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
+
+ val = upper_32_bits(mem->start);
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
+
+ writel(WIN_ENABLE, port->base + PCIE_AXI_WINDOW0);
+
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ dev_info(pcie->dev,
+ "module not built-in, Gen2 unavailable even if supported\n");
+
+ return 0;
+}
+
+/**
+ * mtk_pcie_retrain - retrain the root bridge link if needed
+ * @dev: The device, for use in logging
+ * @host: The host bridge which contains the link
+ *
+ * Due to what is likely a hardware bug, some devices (notably EcoNet) start up
+ * as Gen1, and must be retrained once after initial configuration in order to
+ * reach Gen2.
+ *
+ * These devices always self-identify as Gen2 capable, but sometimes the PHY is
+ * only capable of Gen1 operation, and sometimes the PCIe card (e.g. wifi) is
+ * only Gen1 capable. Therefore it is most convenient to retrain every port
+ * after startup.
+ */
+static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
+{
+ struct pci_dev *rp;
+ int ret = -ENOENT;
+ u16 lnksta = 0;
+ u32 speed;
+
+ /* Should already have been warned about during startup_port */
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ return 0;
+
+ guard(rwsem_read)(&pci_bus_sem);
+
+ for_each_pci_bridge(rp, host->bus) {
+ if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
+ continue;
+
+#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
+ ret = pcie_retrain_link(rp, true);
+#endif
+
+ if (ret)
+ return dev_err_probe(&rp->dev, ret,
+ "failed to retrain port\n");
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
+ speed = lnksta & PCI_EXP_LNKSTA_CLS;
+
+ pci_info(rp, "link retrained, speed %s\n",
+ pci_speed_string(pcie_link_speed[speed]));
+
+ }
+
+ return 0;
+}
+
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
@@ -1159,6 +1298,13 @@ static int mtk_pcie_probe(struct platform_device *pdev)
if (err)
goto put_resources;
+ /*
+ * Ignore error because pci_host_probe() was already called, and in any
+ * case it is possible that the port will still work as Gen1.
+ */
+ if (pcie->soc->quirks & MTK_PCIE_RETRAIN)
+ mtk_pcie_retrain(dev, host);
+
return 0;
put_resources:
@@ -1274,8 +1420,16 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
.quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
};
+static const struct mtk_pcie_soc mtk_pcie_soc_en7528 = {
+ .ops = &mtk_pcie_ops_v2,
+ .startup = mtk_pcie_startup_port_en7528,
+ .setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_RETRAIN,
+};
+
static const struct of_device_id mtk_pcie_ids[] = {
{ .compatible = "airoha,an7583-pcie", .data = &mtk_pcie_soc_an7583 },
+ { .compatible = "econet,en7528-pcie", .data = &mtk_pcie_soc_en7528 },
{ .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 },
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC
2026-05-21 17:19 ` [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
@ 2026-05-21 18:27 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-05-21 18:27 UTC (permalink / raw)
To: Caleb James DeLisle; +Cc: conor+dt, robh, linux-pci, devicetree
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The driver bypasses module export boundaries and locking encapsulation by using IS_BUILTIN() to access unexported PCI core functions and locks.
- [High] Retraining the PCIe link after `pci_host_probe()` causes the link to drop while endpoint drivers are actively probing and operating.
- [High] A failure to retrain one root port aborts the entire loop, leaving subsequent ports stuck at Gen1.
--
commit c15a1642adf464e152e3330bafda659a6425825d
Author: Caleb James DeLisle <cjd@cjdns.fr>
Subject: PCI: mediatek: Add support for EcoNet EN7528 SoC
This commit adds support for the PCIe controller present on the EcoNet EN7528
and EN751221 SoCs. It includes a quirk to retrain the link to Gen2 after
startup, as some Gen2-capable ports initially start in Gen1 mode.
> diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
> index c503fbd774d09..30f8070967b82 100644
> --- a/drivers/pci/controller/pcie-mediatek.c
> +++ b/drivers/pci/controller/pcie-mediatek.c
[ ... ]
> +static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
> +{
> + struct pci_dev *rp;
> + int ret = -ENOENT;
> + u16 lnksta = 0;
> + u32 speed;
> +
> + /* Should already have been warned about during startup_port */
> + if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
> + return 0;
> +
> + guard(rwsem_read)(&pci_bus_sem);
[Severity: High]
Does using IS_BUILTIN() to conditionally access internal locks like pci_bus_sem
and unexported functions bypass module export boundaries?
When compiled as a module, does this silently break functionality by
completely skipping the Gen2 link retraining and leaving the ports permanently
degraded to Gen1?
> +
> + for_each_pci_bridge(rp, host->bus) {
> + if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
> + continue;
> +
> +#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
> + ret = pcie_retrain_link(rp, true);
> +#endif
> +
> + if (ret)
> + return dev_err_probe(&rp->dev, ret,
> + "failed to retrain port\n");
[Severity: High]
If pcie_retrain_link() fails for one root port, does returning immediately skip
retraining for all remaining root ports on the bus?
Since the caller explicitly ignores the return value of mtk_pcie_retrain(),
should this perhaps just log an error and continue to the next port instead?
> +
> + pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
> + speed = lnksta & PCI_EXP_LNKSTA_CLS;
> +
> + pci_info(rp, "link retrained, speed %s\n",
> + pci_speed_string(pcie_link_speed[speed]));
> +
> + }
> +
> + return 0;
> +}
[ ... ]
> @@ -1159,6 +1298,13 @@ static int mtk_pcie_probe(struct platform_device *pdev)
> if (err)
> goto put_resources;
>
> + /*
> + * Ignore error because pci_host_probe() was already called, and in any
> + * case it is possible that the port will still work as Gen1.
> + */
> + if (pcie->soc->quirks & MTK_PCIE_RETRAIN)
> + mtk_pcie_retrain(dev, host);
[Severity: High]
Does retraining the link here cause it to drop while endpoint drivers are
already actively probing?
Because pci_host_probe() enumerates the bus and binds endpoint drivers,
taking the link down immediately after could cause concurrent MMIO or DMA
transactions to fail. Could this also result in endpoint drivers observing
incorrect Gen1 link speeds during their initialization?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260521171951.1495781-1-cjd@cjdns.fr?part=3
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support.
2026-05-21 17:19 [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Caleb James DeLisle
` (2 preceding siblings ...)
2026-05-21 17:19 ` [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
@ 2026-06-11 7:12 ` Manivannan Sadhasivam
2026-06-11 8:23 ` [PATCH v10] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
3 siblings, 1 reply; 8+ messages in thread
From: Manivannan Sadhasivam @ 2026-06-11 7:12 UTC (permalink / raw)
To: Caleb James DeLisle
Cc: linux-pci, linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi,
kwilczynski, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel
On Thu, May 21, 2026 at 05:19:48PM +0000, Caleb James DeLisle wrote:
> Tested on TpLink Archer VR1200V-V2 (EN751221 with Gen2 device)
>
> Changes since v8:
> * guard(rwsem_read)(&pci_bus_sem); in mtk_pcie_retrain
> * v8: https://lore.kernel.org/linux-mips/20260520183827.908243-1-cjd@cjdns.fr
>
> Changes from v7:
> * mtk_pcie_retrain retrain all root ports not just first
> * Include fix from Manivannan Sadhasivam, wrong usage of virt_to_phys()
> * v7: https://lore.kernel.org/linux-mips/20260514151318.3444959-1-cjd@cjdns.fr
>
> Changes from v6:
> * s/reset/resets/ in .yaml
> * s/re-train/retrain/g
> * s/Root bridge/Root port/
> * If module not builtin, log at mtk_pcie_startup_port_en7528()
> * Do not fail if error in mtk_pcie_retrain()
> * v6: https://lore.kernel.org/linux-mips/20260513191652.3200607-1-cjd@cjdns.fr
>
> Changes from v5:
> * s/errno-base.h/errno.h/
> * Breakout mtk_pcie_retrain() into a function
> * Use for_each_pci_bridge() to find root bridge
> * v5: https://lore.kernel.org/linux-mips/20260413140339.16238-1-cjd@cjdns.fr/
>
> Changes from v4:
> * Fixed missing Acked-by
> * Rebased to commit 66672af7a095 ("Add linux-next specific files for 20260410")
> * v4: https://lore.kernel.org/linux-mips/20260404182854.2183651-1-cjd@cjdns.fr/
>
> Changes from v3:
> * s/initiallized/initialized/
> * Use PCIE_T_PVPERL_MS for sleep time
> * Use PCI_PM_D3COLD_WAIT for startup wait time
> * Clarify comment "Activate INTx interrupts"
> * Add MTK_PCIE_RETRAIN quirk for devices which require link re-train
> * Do not retrain *all* bridges, only root bridge
> * Better comments and logging in retraining logic
> * v3: https://lore.kernel.org/linux-mips/20260320094212.696671-1-cjd@cjdns.fr/
>
> Changes from v2:
> * mediatek-pcie.yaml -> s/power-domain/power-domains/ and drop example
> * Patch 3 dropped as it has been applied (Thanks!)
> * v2: https://lore.kernel.org/linux-mips/20260316155157.679533-1-cjd@cjdns.fr/
>
> Changes from v1:
> * mediatek-pcie.yaml slot0 needs device-type = "pci", fix dt_binding_check
> Link: https://lore.kernel.org/linux-mips/177334026016.3889069.9474337544951486443.robh@kernel.org
> * v1: https://lore.kernel.org/linux-mips/20260312165332.569772-1-cjd@cjdns.fr/
>
> This was split from a larger PCIe patchset which crossed multiple
> subsystems. I'm not labeling this a v3 because it's a new patchset, but
> I'm keeping the historical record anyway.
>
> Changes from econet-pcie v2:
> * mediatek-pcie.yaml add missing constraints to PCI node properties
> * econet-pcie v2: https://lore.kernel.org/linux-mips/20260309131818.74467-1-cjd@cjdns.fr
>
> Changes from econet-pcie v1:
> * pcie-mediatek.c Exclude pcie_retrain_link() when building as a module
> * econet-pcie v1: https://lore.kernel.org/linux-mips/20260303190948.694783-1-cjd@cjdns.fr/
>
>
> Caleb James DeLisle (2):
> dt-bindings: PCI: mediatek: Add support for EcoNet EN7528
> PCI: mediatek: Add support for EcoNet EN7528 SoC
Patch 3 doesn't apply cleanly. So I applied patches 1 and 2. Please rebase patch
3 on top of pci/controller/mediatek branch and resend it.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v10] PCI: mediatek: Add support for EcoNet EN7528 SoC
2026-06-11 7:12 ` [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Manivannan Sadhasivam
@ 2026-06-11 8:23 ` Caleb James DeLisle
2026-06-11 8:47 ` sashiko-bot
0 siblings, 1 reply; 8+ messages in thread
From: Caleb James DeLisle @ 2026-06-11 8:23 UTC (permalink / raw)
To: mani
Cc: linux-pci, linux-mips, naseefkm, ryder.lee, helgaas, lpieralisi,
kwilczynski, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, ansuelsmth, linux-mediatek, devicetree,
linux-kernel, Caleb James DeLisle
Add support for the PCIe present on the EcoNet EN7528 (and EN751221) SoCs.
These SoCs have a mix of Gen1 and Gen2 capable ports, but the Gen2 ports
require re-training after startup.
Co-developed-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
This is dubbed v10 because it comes from a patchset which was
already mostly applied.
Link: https://lore.kernel.org/linux-mips/20260521171951.1495781-1-cjd@cjdns.fr/
---
drivers/pci/controller/Kconfig | 2 +-
drivers/pci/controller/pcie-mediatek.c | 154 +++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 2247709ef6d6..8a3a31b2bc12 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -209,7 +209,7 @@ config PCI_MVEBU
config PCIE_MEDIATEK
tristate "MediaTek PCIe controller"
- depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
+ depends on ARCH_AIROHA || ARCH_MEDIATEK || ECONET || COMPILE_TEST
depends on OF
depends on PCI_MSI
select IRQ_MSI_LIB
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 1bb8839c3cb0..95b651ddc355 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -10,11 +10,13 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
#include <linux/irqdomain.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/msi.h>
@@ -78,6 +80,7 @@
#define PCIE_CONF_VEND_ID 0x100
#define PCIE_CONF_DEVICE_ID 0x102
+#define PCIE_CONF_REV_CLASS 0x104
#define PCIE_CONF_CLASS_ID 0x106
#define PCIE_INT_MASK 0x420
@@ -90,6 +93,11 @@
#define MSI_MASK BIT(23)
#define MTK_MSI_IRQS_NUM 32
+#define EN7528_HOST_MODE 0x00804201
+#define EN7528_LINKUP_REG 0x50
+#define EN7528_RC0_LINKUP BIT(1)
+#define EN7528_RC1_LINKUP BIT(2)
+
#define PCIE_AHB_TRANS_BASE0_L 0x438
#define PCIE_AHB_TRANS_BASE0_H 0x43c
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
@@ -149,12 +157,15 @@ struct mtk_pcie_port;
* @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
* @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
* @MTK_PCIE_SKIP_RSTB: Skip calling RSTB bits on PCIe probe
+ * @MTK_PCIE_RETRAIN: Retrain link to bridge after startup because some
+ * Gen2-capable devices start as Gen1.
*/
enum mtk_pcie_quirks {
MTK_PCIE_FIX_CLASS_ID = BIT(0),
MTK_PCIE_FIX_DEVICE_ID = BIT(1),
MTK_PCIE_NO_MSI = BIT(2),
MTK_PCIE_SKIP_RSTB = BIT(3),
+ MTK_PCIE_RETRAIN = BIT(4),
};
/**
@@ -760,6 +771,134 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
return 0;
}
+static int mtk_pcie_startup_port_en7528(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct resource *mem = NULL;
+ struct resource_entry *entry;
+ u32 val, link_mask;
+ int err;
+
+ entry = resource_list_first_type(&host->windows, IORESOURCE_MEM);
+ if (entry)
+ mem = entry->res;
+ if (!mem)
+ return -EINVAL;
+
+ if (!pcie->cfg) {
+ dev_err(pcie->dev, "EN7528: pciecfg syscon not available\n");
+ return -EINVAL;
+ }
+
+ /* Assert all reset signals */
+ writel(0, port->base + PCIE_RST_CTRL);
+
+ /*
+ * Enable PCIe link down reset, if link status changed from link up to
+ * link down, this will reset MAC control registers and configuration
+ * space.
+ */
+ writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
+
+ msleep(PCIE_T_PVPERL_MS);
+
+ /* De-assert PHY, PE, PIPE, MAC and configuration reset */
+ val = readl(port->base + PCIE_RST_CTRL);
+ val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
+ PCIE_MAC_SRSTB | PCIE_CRSTB;
+ writel(val, port->base + PCIE_RST_CTRL);
+
+ writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
+ port->base + PCIE_CONF_REV_CLASS);
+ writel(EN7528_HOST_MODE, port->base);
+
+ link_mask = (port->slot == 0) ? EN7528_RC0_LINKUP : EN7528_RC1_LINKUP;
+
+ /* 100ms timeout value should be enough for Gen1/2 training */
+ err = regmap_read_poll_timeout(pcie->cfg, EN7528_LINKUP_REG, val,
+ !!(val & link_mask), 20,
+ PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
+ if (err) {
+ dev_err(pcie->dev, "EN7528: port%d link timeout\n", port->slot);
+ return -ETIMEDOUT;
+ }
+
+ /* Activate INTx interrupts */
+ val = readl(port->base + PCIE_INT_MASK);
+ val &= ~INTX_MASK;
+ writel(val, port->base + PCIE_INT_MASK);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ mtk_pcie_enable_msi(port);
+
+ /* Set AHB to PCIe translation windows */
+ val = lower_32_bits(mem->start) |
+ AHB2PCIE_SIZE(fls(resource_size(mem)));
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
+
+ val = upper_32_bits(mem->start);
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
+
+ writel(WIN_ENABLE, port->base + PCIE_AXI_WINDOW0);
+
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ dev_info(pcie->dev,
+ "module not built-in, Gen2 unavailable even if supported\n");
+
+ return 0;
+}
+
+/**
+ * mtk_pcie_retrain - retrain the root bridge link if needed
+ * @dev: The device, for use in logging
+ * @host: The host bridge which contains the link
+ *
+ * Due to what is likely a hardware bug, some devices (notably EcoNet) start up
+ * as Gen1, and must be retrained once after initial configuration in order to
+ * reach Gen2.
+ *
+ * These devices always self-identify as Gen2 capable, but sometimes the PHY is
+ * only capable of Gen1 operation, and sometimes the PCIe card (e.g. wifi) is
+ * only Gen1 capable. Therefore it is most convenient to retrain every port
+ * after startup.
+ */
+static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
+{
+ struct pci_dev *rp;
+ int ret = -ENOENT;
+ u16 lnksta = 0;
+ u32 speed;
+
+ /* Should already have been warned about during startup_port */
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ return 0;
+
+ guard(rwsem_read)(&pci_bus_sem);
+
+ for_each_pci_bridge(rp, host->bus) {
+ if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
+ continue;
+
+#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
+ ret = pcie_retrain_link(rp, true);
+#endif
+
+ if (ret)
+ return dev_err_probe(&rp->dev, ret,
+ "failed to retrain port\n");
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
+ speed = lnksta & PCI_EXP_LNKSTA_CLS;
+
+ pci_info(rp, "link retrained, speed %s\n",
+ pci_speed_string(pcie_link_speed[speed]));
+
+ }
+
+ return 0;
+}
+
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
@@ -1173,6 +1312,13 @@ static int mtk_pcie_probe(struct platform_device *pdev)
if (err)
goto put_resources;
+ /*
+ * Ignore error because pci_host_probe() was already called, and in any
+ * case it is possible that the port will still work as Gen1.
+ */
+ if (pcie->soc->quirks & MTK_PCIE_RETRAIN)
+ mtk_pcie_retrain(dev, host);
+
return 0;
put_resources:
@@ -1292,8 +1438,16 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
.quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
};
+static const struct mtk_pcie_soc mtk_pcie_soc_en7528 = {
+ .ops = &mtk_pcie_ops_v2,
+ .startup = mtk_pcie_startup_port_en7528,
+ .setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_RETRAIN,
+};
+
static const struct of_device_id mtk_pcie_ids[] = {
{ .compatible = "airoha,an7583-pcie", .data = &mtk_pcie_soc_an7583 },
+ { .compatible = "econet,en7528-pcie", .data = &mtk_pcie_soc_en7528 },
{ .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 },
base-commit: 843044971a5167087a9484f8f6eec81da30f2a71
--
2.39.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v10] PCI: mediatek: Add support for EcoNet EN7528 SoC
2026-06-11 8:23 ` [PATCH v10] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
@ 2026-06-11 8:47 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-06-11 8:47 UTC (permalink / raw)
To: Caleb James DeLisle; +Cc: conor+dt, devicetree, linux-pci, robh
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] Premature loop termination in `mtk_pcie_retrain` prevents remaining root ports from being retrained.
- [Low] Calling `dev_err_probe()` on a child device from the parent's probe context.
Pre-existing issues:
- [High] IRQ domains are leaked and leave dangling pointers when a PCIe port fails to start up.
- [High] The PCIe port startup callbacks sleep while executing in the atomic noirq system resume phase.
- [High] The `resource_size(mem)` parameter is silently truncated on 64-bit platforms because `fls()` only accepts a 32-bit unsigned int.
--
commit f0ee2a994c044496f2233db60f4bbc71d16d530c
Author: Caleb James DeLisle <cjd@cjdns.fr>
Date: Thu Jun 11 08:23:28 2026 +0000
PCI: mediatek: Add support for EcoNet EN7528 SoC
Add support for the PCIe present on the EcoNet EN7528 (and EN751221) SoCs.
These SoCs have a mix of Gen1 and Gen2 capable ports, but the Gen2 ports
require re-training after startup.
Co-developed-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 6d0ee089ca74b..f4963af0d6085 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
[ ... ]
> @@ -750,6 +761,134 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
> return 0;
> }
>
> +static int mtk_pcie_startup_port_en7528(struct mtk_pcie_port *port)
> +{
> + struct mtk_pcie *pcie = port->pcie;
> + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
[ ... ]
> + /* Assert all reset signals */
> + writel(0, port->base + PCIE_RST_CTRL);
> +
> + /*
> + * Enable PCIe link down reset, if link status changed from link up to
> + * link down, this will reset MAC control registers and configuration
> + * space.
> + */
> + writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
> +
> + msleep(PCIE_T_PVPERL_MS);
[Severity: High]
This is a pre-existing issue in the driver's power management design, but
will adding msleep() here cause a "scheduling while atomic" BUG?
The driver's PM ops configure mtk_pcie_resume_noirq() to run during the
system resume_noirq phase, which executes with local hardware interrupts
disabled.
Since mtk_pcie_resume_noirq() unconditionally calls mtk_pcie_enable_port(),
which then invokes this startup callback, will calling sleepable functions
like msleep() and regmap_read_poll_timeout() here trigger a panic during
system resume?
> +
> + /* De-assert PHY, PE, PIPE, MAC and configuration reset */
> + val = readl(port->base + PCIE_RST_CTRL);
> + val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
> + PCIE_MAC_SRSTB | PCIE_CRSTB;
> + writel(val, port->base + PCIE_RST_CTRL);
> +
> + writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
> + port->base + PCIE_CONF_REV_CLASS);
> + writel(EN7528_HOST_MODE, port->base);
> +
> + link_mask = (port->slot == 0) ? EN7528_RC0_LINKUP : EN7528_RC1_LINKUP;
> +
> + /* 100ms timeout value should be enough for Gen1/2 training */
> + err = regmap_read_poll_timeout(pcie->cfg, EN7528_LINKUP_REG, val,
> + !!(val & link_mask), 20,
> + PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
> + if (err) {
> + dev_err(pcie->dev, "EN7528: port%d link timeout\n", port->slot);
> + return -ETIMEDOUT;
[Severity: High]
This is a pre-existing issue, but does returning an error here leak IRQ
domains?
When this function returns -ETIMEDOUT for an empty slot, the caller
mtk_pcie_enable_port() executes its error path and calls
mtk_pcie_port_free().
However, mtk_pcie_port_free() frees the port struct using devm_kfree(), but
it never tears down the IRQ domains using irq_domain_remove(). Does this
permanently leak the IRQ domains on every empty slot timeout and leave their
domain->host_data pointers dangling to the freed port struct?
> + }
> +
> + /* Activate INTx interrupts */
> + val = readl(port->base + PCIE_INT_MASK);
> + val &= ~INTX_MASK;
> + writel(val, port->base + PCIE_INT_MASK);
> +
> + if (IS_ENABLED(CONFIG_PCI_MSI))
> + mtk_pcie_enable_msi(port);
> +
> + /* Set AHB to PCIe translation windows */
> + val = lower_32_bits(mem->start) |
> + AHB2PCIE_SIZE(fls(resource_size(mem)));
[Severity: High]
This is a pre-existing issue, but is resource_size(mem) silently truncated
here on 64-bit platforms?
The resource_size(mem) function returns a resource_size_t, which is a 64-bit
integer on 64-bit architectures. Since fls() only accepts a 32-bit unsigned
int, if the resource size is 4GB (0x100000000) or larger, will the lower 32
bits evaluate to zero or be truncated, causing fls() to return an
incorrectly small value and misconfiguring the inbound translation window?
> + writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
[ ... ]
> +static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
> +{
> + struct pci_dev *rp;
> + int ret = -ENOENT;
> + u16 lnksta = 0;
> + u32 speed;
> +
> + /* Should already have been warned about during startup_port */
> + if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
> + return 0;
> +
> + guard(rwsem_read)(&pci_bus_sem);
> +
> + for_each_pci_bridge(rp, host->bus) {
> + if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
> + continue;
> +
> +#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
> + ret = pcie_retrain_link(rp, true);
> +#endif
> +
> + if (ret)
> + return dev_err_probe(&rp->dev, ret,
> + "failed to retrain port\n");
[Severity: High]
Does returning early here prevent the remaining root ports on the bus from
being retrained?
If pcie_retrain_link() fails for any port (for example, due to a timeout on
an empty slot), this aborts the loop. Since mtk_pcie_probe() intentionally
ignores the return value of mtk_pcie_retrain(), the early return doesn't
propagate the error but causes subsequent ports to be permanently stuck at
Gen1 speeds.
Should this instead log the error and continue to the next port?
[Severity: Low]
Additionally, is it correct to call dev_err_probe() on the root port
(rp->dev) here?
Since this function executes as part of the parent host controller's probe
sequence (mtk_pcie_probe()), the root port is not the device actively
probing. Will calling dev_err_probe() on a device that is not currently
probing print a misleading message (like 'probe of <device> failed')?
Should standard pci_err(rp, ...) be used instead?
> +
> + pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
> + speed = lnksta & PCI_EXP_LNKSTA_CLS;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611082329.243515-1-cjd@cjdns.fr?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-11 8:47 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-21 17:19 [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 1/3] PCI: mediatek: Use actual physical address instead of virt_to_phys() Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 2/3] dt-bindings: PCI: mediatek: Add support for EcoNet EN7528 Caleb James DeLisle
2026-05-21 17:19 ` [PATCH v9 3/3] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
2026-05-21 18:27 ` sashiko-bot
2026-06-11 7:12 ` [PATCH v9 0/3] Add EcoNet EN7528 (and EN751221) PCIe support Manivannan Sadhasivam
2026-06-11 8:23 ` [PATCH v10] PCI: mediatek: Add support for EcoNet EN7528 SoC Caleb James DeLisle
2026-06-11 8:47 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox