diff --git a/target/linux/airoha/dts/an7581.dtsi b/target/linux/airoha/dts/an7581.dtsi index 08955dd354..c038c94314 100644 --- a/target/linux/airoha/dts/an7581.dtsi +++ b/target/linux/airoha/dts/an7581.dtsi @@ -723,6 +723,11 @@ mediatek,pbus-csr = <&pbus_csr 0x0 0x4>; + airoha,chip-scu = <&chip_scu>; + airoha,np-scu = <&scuclk>; + airoha,x2-mode; + airoha,serdes-lanes-mask = <0x3>; + interrupts = ; bus-range = <0x00 0xff>; #interrupt-cells = <1>; @@ -809,6 +814,10 @@ mediatek,pbus-csr = <&pbus_csr 0x10 0x14>; + airoha,chip-scu = <&chip_scu>; + airoha,serdes-lanes-mask = <0x4>; + airoha,np-scu = <&scuclk>; + interrupts = ; bus-range = <0x00 0xff>; #interrupt-cells = <1>; diff --git a/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch b/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch new file mode 100644 index 0000000000..555bc50101 --- /dev/null +++ b/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch @@ -0,0 +1,42 @@ +From: Ryan Chen +Subject: clk: en7581: Separate PERST from refclk in PCIe clock callbacks + +The EN7581 PCIe clock enable/disable callbacks currently toggle both +PERST (reset) and reference clock signals together. This prevents the +PCIe controller driver from properly sequencing PERST relative to MAC +register configuration, which is required for x2 link mode. + +Separate the two concerns: clock callbacks only manage reference clocks +(REFCLK_EN0/EN1), while PERST (PERSTOUT/PERSTOUT1/PERSTOUT2) is left +to the PCIe controller driver to manage directly via the NP_SCU regmap. + +Signed-off-by: Ryan Chen +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -961,9 +961,11 @@ static int en7581_pci_enable(struct clk_ + struct regmap *map = cg->map; + u32 mask; + +- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | +- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | +- REG_PCI_CONTROL_PERSTOUT; ++ /* Only enable reference clocks - PERST is managed separately by the ++ * PCIe controller driver to allow proper sequencing of MAC register ++ * configuration between PERST assert and deassert. ++ */ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; + regmap_set_bits(map, REG_PCI_CONTROL, mask); + + return 0; +@@ -975,9 +977,8 @@ static void en7581_pci_disable(struct cl + struct regmap *map = cg->map; + u32 mask; + +- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | +- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | +- REG_PCI_CONTROL_PERSTOUT; ++ /* Only disable reference clocks - PCIe driver manages PERST */ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; + regmap_clear_bits(map, REG_PCI_CONTROL, mask); + usleep_range(1000, 2000); + } diff --git a/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch b/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch new file mode 100644 index 0000000000..9426756203 --- /dev/null +++ b/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch @@ -0,0 +1,244 @@ +From: Ryan Chen +Subject: PCI: mediatek-gen3: Add PCIe x2 link support for Airoha EN7581 + +The Airoha EN7581 SoC supports PCIe x2 mode where two PCIe lanes are +bonded together for a single x2 link. This requires coordination with +the NP_SCU system controller for serdes mux routing, PERST management, +and lane configuration. + +The x2 initialization sequence: +1. Assert serdes reset and PERST before enabling clocks +2. Configure serdes mux for 2-lane mode +3. Enable reference clocks, deassert serdes reset +4. Clear x1 mode bit and write EQ presets on both MACs +5. Deassert PERST to start link training + +After initial link training, if the link negotiates Gen2 instead of +Gen3, a serdes reset toggle forces the MAC to re-discover the PHY +Gen3 capability from the Link Capabilities 2 register, allowing the +link to retrain at Gen3 x2 (8 GT/s). + +Signed-off-by: Ryan Chen +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -61,6 +61,14 @@ + #define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24) + #define PCIE_LTSSM_STATE_L2_IDLE 0x14 + ++/* EN7581 x2 mode support */ ++#define PCIE_SETTING_REG_X1_MODE BIT(13) ++ ++/* EN7581 NP_SCU register offsets for x2 link init */ ++#define NP_SCU_LANE_CFG0 0x830 ++#define NP_SCU_LANE_CFG1 0x834 ++#define NP_SCU_CTRL_REG 0x88 ++ + #define PCIE_LINK_STATUS_REG 0x154 + #define PCIE_PORT_LINKUP BIT(8) + +@@ -205,6 +213,11 @@ struct mtk_gen3_pcie { + DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM); + + const struct mtk_gen3_pcie_pdata *soc; ++ ++ /* EN7581 x2 mode support */ ++ struct regmap *np_scu; ++ bool x2_mode; ++ void __iomem *sister_base; + }; + + /* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */ +@@ -925,6 +938,28 @@ static int mtk_pcie_en7581_power_up(stru + size = lower_32_bits(resource_size(entry->res)); + regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); + ++ /* Lookup NP_SCU regmap for x2 mode support */ ++ pcie->np_scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,np-scu"); ++ if (IS_ERR(pcie->np_scu)) { ++ dev_dbg(dev, "np_scu not available, x2 mode disabled\n"); ++ pcie->np_scu = NULL; ++ } ++ ++ /* Check for x2 mode property */ ++ pcie->x2_mode = of_property_read_bool(dev->of_node, "airoha,x2-mode"); ++ if (pcie->x2_mode) ++ dev_info(dev, "x2 mode enabled\n"); ++ ++ /* Map sister MAC for x2 mode (MAC1 at +0x20000) */ ++ if (pcie->x2_mode) { ++ pcie->sister_base = devm_ioremap(dev, ++ pcie->reg_base + 0x20000, 0x20000); ++ if (!pcie->sister_base) ++ dev_warn(dev, "failed to map sister MAC for x2\n"); ++ else ++ dev_info(dev, "x2 mode: sister MAC mapped\n"); ++ } ++ + err = phy_set_mode(pcie->phy, PHY_MODE_PCIE); + if (err) { + dev_err(dev, "failed to set PHY mode\n"); +@@ -962,17 +997,28 @@ static int mtk_pcie_en7581_power_up(stru + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + +- val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | +- FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | +- FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | +- FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); +- writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); +- +- val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | +- FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | +- FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | +- FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); +- writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); ++ /* ++ * EN7581 x2: Assert PERST and serdes reset before enabling clocks ++ * so that MAC registers can be configured while devices are held ++ * in reset, ensuring link trains with the correct x2 settings. ++ */ ++ if (pcie->x2_mode && pcie->np_scu) { ++ /* Assert serdes reset on all lanes */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), BIT(26) | BIT(27)); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(27), BIT(27)); ++ msleep(100); ++ ++ /* Assert PERST on all ports */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), 0); ++ ++ /* Set serdes mux for 2-lane mode (bits[1:0] = 2) */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(0) | BIT(1), BIT(1)); ++ mdelay(1); ++ } + + err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (err) { +@@ -981,12 +1027,121 @@ static int mtk_pcie_en7581_power_up(stru + } + + /* +- * Airoha EN7581 performs PCIe reset via clk callbacks since it has a +- * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to +- * complete the PCIe reset. ++ * Airoha EN7581: clock enable only provides refclk (patch 911). ++ * For x2, PERST + serdes are already asserted above. ++ * Wait for refclk to stabilize. + */ + msleep(PCIE_T_PVPERL_MS); + ++ if (pcie->x2_mode && pcie->np_scu) { ++ /* ++ * EN7581 x2: PERST asserted, serdes reset asserted, ++ * refclk now stable. Complete the init sequence. ++ */ ++ msleep(30); ++ ++ /* Deassert serdes reset on all lanes */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), 0); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(27), 0); ++ ++ /* Clear SETTING_REG bit 13 for x2 mode on both MACs */ ++ val = readl_relaxed(pcie->base + PCIE_SETTING_REG); ++ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE, ++ pcie->base + PCIE_SETTING_REG); ++ if (pcie->sister_base) { ++ val = readl_relaxed(pcie->sister_base + 0x80); ++ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE, ++ pcie->sister_base + 0x80); ++ } ++ ++ /* EQ presets on both MACs */ ++ writel_relaxed(0x41474147, pcie->base + PCIE_EQ_PRESET_01_REG); ++ if (pcie->sister_base) ++ writel_relaxed(0x41474147, pcie->sister_base + 0x100); ++ writel_relaxed(0x1018020f, pcie->base + PCIE_PIPE4_PIE8_REG); ++ if (pcie->sister_base) ++ writel_relaxed(0x1018020f, pcie->sister_base + 0x338); ++ ++ /* Deassert PERST for all ports - link training starts */ ++ msleep(10); ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), ++ BIT(16) | BIT(26) | BIT(29)); ++ ++ /* Wait for link training to complete */ ++ msleep(800); ++ ++ /* ++ * Check if link trained at Gen3. If not, toggle serdes ++ * reset to force MAC to re-discover PHY Gen3 capability. ++ * Without this, MAC only advertises Gen1-Gen2 in LnkCap2. ++ */ ++ val = readl_relaxed(pcie->base + PCIE_LINK_STATUS_REG); ++ if (val & PCIE_PORT_LINKUP) { ++ void __iomem *cfg = pcie->base + PCIE_CFG_OFFSET_ADDR; ++ u8 cap_ptr; ++ int speed = 0; ++ ++ /* Walk PCI cap list to find PCIe cap (ID=0x10) */ ++ cap_ptr = readl_relaxed(cfg + PCI_CAPABILITY_LIST) & 0xFF; ++ while (cap_ptr >= 0x40) { ++ u32 hdr = readl_relaxed(cfg + cap_ptr); ++ if ((hdr & 0xFF) == PCI_CAP_ID_EXP) { ++ u32 lnk = readl_relaxed(cfg + cap_ptr + PCI_EXP_LNKCTL); ++ speed = (lnk >> 16) & PCI_EXP_LNKSTA_CLS; ++ break; ++ } ++ cap_ptr = (hdr >> 8) & 0xFF; ++ } ++ ++ if (speed > 0 && speed < 3) { ++ dev_info(dev, "x2: link at Gen%d, toggling serdes for Gen3\n", speed); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(7) | BIT(8), BIT(7) | BIT(8)); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), BIT(26) | BIT(27)); ++ msleep(1000); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(7) | BIT(8), 0); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), 0); ++ msleep(2000); ++ dev_info(dev, "x2: serdes toggle done, link retraining\n"); ++ } else { ++ dev_info(dev, "x2: link at Gen%d, no toggle needed\n", speed); ++ } ++ } else { ++ dev_info(dev, "x2: link not up after init, skipping speed check\n"); ++ } ++ ++ dev_info(dev, "x2: init complete, PERST deasserted\n"); ++ } else { ++ /* ++ * Non-x2 mode: standard EQ config then deassert PERST. ++ */ ++ val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | ++ FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); ++ writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); ++ ++ val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | ++ FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); ++ writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); ++ ++ /* Deassert PERST for all ports */ ++ if (pcie->np_scu) { ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), ++ BIT(16) | BIT(26) | BIT(29)); ++ msleep(100); ++ } ++ } ++ + return 0; + + err_clk_prepare_enable: