airoha: replace PCIe x2-mode patch with new pending version
The current x2-mode patch is mostly an hack ported from Airoha SDK. Replace with in favor of a new version posted upstream that better implement handling of reset and configuring x2-mode. This now use the standard num-lanes property and dedicated PERSTOUT resets exported by the clk driver. All the DTS that used x2-mode are updated. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
@@ -359,10 +359,23 @@
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
airoha,x2-mode;
|
||||
status = "okay";
|
||||
|
||||
reg = <0x0 0x1fc00000 0x0 0x1670>,
|
||||
<0x0 0x1fc20000 0x0 0x1670>;
|
||||
reg-names = "pcie-mac", "sec-pcie-mac";
|
||||
|
||||
resets = <&scuclk EN7581_PCIE0_RST>,
|
||||
<&scuclk EN7581_PCIE1_RST>,
|
||||
<&scuclk EN7581_PCIC_PERSTOUT0_RST>,
|
||||
<&scuclk EN7581_PCIC_PERSTOUT1_RST>;
|
||||
reset-names = "phy-lane0", "phy-lane1",
|
||||
"perstout", "sec-perstout";
|
||||
|
||||
num-lanes = <2>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_rst_pins>;
|
||||
status = "okay";
|
||||
|
||||
pcie@0,0 {
|
||||
reg = <0x0000 0 0 0 0>;
|
||||
@@ -401,6 +414,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&pcie2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie2_rst_pins>;
|
||||
|
||||
@@ -235,7 +235,18 @@
|
||||
&pcie0 {
|
||||
status = "okay";
|
||||
|
||||
airoha,x2-mode;
|
||||
reg = <0x0 0x1fc00000 0x0 0x1670>,
|
||||
<0x0 0x1fc20000 0x0 0x1670>;
|
||||
reg-names = "pcie-mac", "sec-pcie-mac";
|
||||
|
||||
resets = <&scuclk EN7581_PCIE0_RST>,
|
||||
<&scuclk EN7581_PCIE1_RST>,
|
||||
<&scuclk EN7581_PCIC_PERSTOUT0_RST>,
|
||||
<&scuclk EN7581_PCIC_PERSTOUT1_RST>;
|
||||
reset-names = "phy-lane0", "phy-lane1",
|
||||
"perstout", "sec-perstout";
|
||||
|
||||
num-lanes = <2>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_rst_pins>;
|
||||
@@ -278,6 +289,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&pcie2 {
|
||||
status = "okay";
|
||||
|
||||
|
||||
@@ -779,15 +779,11 @@
|
||||
|
||||
ranges = <0x02000000 0 0x20000000 0x0 0x20000000 0 0x4000000>;
|
||||
|
||||
resets = <&scuclk EN7581_PCIE0_RST>,
|
||||
<&scuclk EN7581_PCIE1_RST>,
|
||||
<&scuclk EN7581_PCIE2_RST>;
|
||||
reset-names = "phy-lane0", "phy-lane1", "phy-lane2";
|
||||
resets = <&scuclk EN7581_PCIE0_RST>, <&scuclk EN7581_PCIC_PERSTOUT0_RST>;
|
||||
reset-names = "phy-lane0", "perstout";
|
||||
|
||||
mediatek,pbus-csr = <&pbus_csr 0x0 0x4>;
|
||||
|
||||
airoha,np-scu = <&scuclk>;
|
||||
|
||||
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
|
||||
bus-range = <0x00 0xff>;
|
||||
#interrupt-cells = <1>;
|
||||
@@ -824,10 +820,8 @@
|
||||
|
||||
ranges = <0x02000000 0 0x24000000 0x0 0x24000000 0 0x4000000>;
|
||||
|
||||
resets = <&scuclk EN7581_PCIE0_RST>,
|
||||
<&scuclk EN7581_PCIE1_RST>,
|
||||
<&scuclk EN7581_PCIE2_RST>;
|
||||
reset-names = "phy-lane0", "phy-lane1", "phy-lane2";
|
||||
resets = <&scuclk EN7581_PCIE1_RST>, <&scuclk EN7581_PCIC_PERSTOUT1_RST>;
|
||||
reset-names = "phy-lane1", "perstout";
|
||||
|
||||
mediatek,pbus-csr = <&pbus_csr 0x8 0xc>;
|
||||
|
||||
@@ -867,15 +861,11 @@
|
||||
|
||||
ranges = <0x02000000 0 0x28000000 0x0 0x28000000 0 0x4000000>;
|
||||
|
||||
resets = <&scuclk EN7581_PCIE0_RST>,
|
||||
<&scuclk EN7581_PCIE1_RST>,
|
||||
<&scuclk EN7581_PCIE2_RST>;
|
||||
reset-names = "phy-lane0", "phy-lane1", "phy-lane2";
|
||||
resets = <&scuclk EN7581_PCIE2_RST>, <&scuclk EN7581_PCIC_PERSTOUT2_RST>;
|
||||
reset-names = "phy-lane2", "perstout";
|
||||
|
||||
mediatek,pbus-csr = <&pbus_csr 0x10 0x14>;
|
||||
|
||||
airoha,np-scu = <&scuclk>;
|
||||
|
||||
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
bus-range = <0x00 0xff>;
|
||||
#interrupt-cells = <1>;
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
From 198c66755440564bec1bf16f37cfa9702394fd02 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 25 Jun 2026 13:48:34 +0200
|
||||
Subject: [PATCH 1/3] dt-bindings: clock: airoha: Add additional reset for PCIe
|
||||
PERSTOUT
|
||||
|
||||
Add additional reset to control PCIe PERSTOUT reset line for each of the 3
|
||||
PCIe lines.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
include/dt-bindings/reset/airoha,en7581-reset.h | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/include/dt-bindings/reset/airoha,en7581-reset.h
|
||||
+++ b/include/dt-bindings/reset/airoha,en7581-reset.h
|
||||
@@ -62,5 +62,9 @@
|
||||
#define EN7581_CPU_TIMER_RST 50
|
||||
#define EN7581_PCIE_HB_RST 51
|
||||
#define EN7581_XPON_MAC_RST 52
|
||||
+/* RST_PCIC */
|
||||
+#define EN7581_PCIC_PERSTOUT0_RST 53
|
||||
+#define EN7581_PCIC_PERSTOUT1_RST 54
|
||||
+#define EN7581_PCIC_PERSTOUT2_RST 55
|
||||
|
||||
#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ */
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
From 335db8dda524add9d8ec3bdd88eddc339d5d3439 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 25 Jun 2026 13:49:39 +0200
|
||||
Subject: [PATCH 2/4] clk: en7523: add support for dedicated PCIe PERSTOUT
|
||||
reset
|
||||
|
||||
Add support for resetting the PCIe lines with the PERSTOUT reset. These
|
||||
special reset are controlled by the PCIC register and are specific to each
|
||||
of the 3 PCIe lines.
|
||||
|
||||
Notice that reset logic is inverted for these bit where 0 is assert and 1
|
||||
deassert. This is intenrally handled in the reset function.
|
||||
|
||||
PCI enable/disable are updated to drop PERSTOUT bits in favor dedicated
|
||||
reset handling.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/clk/clk-en7523.c | 39 ++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 28 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk-en7523.c
|
||||
+++ b/drivers/clk/clk-en7523.c
|
||||
@@ -451,6 +451,7 @@ static const struct en_clk_desc an7583_b
|
||||
static const u16 en7581_rst_ofs[] = {
|
||||
REG_RST_CTRL2,
|
||||
REG_RST_CTRL1,
|
||||
+ REG_NP_SCU_PCIC,
|
||||
};
|
||||
|
||||
static const u16 en7523_rst_map[] = {
|
||||
@@ -556,6 +557,11 @@ static const u16 en7581_rst_map[] = {
|
||||
[EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
|
||||
[EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
|
||||
[EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
|
||||
+
|
||||
+ /* RST_PCIC */
|
||||
+ [EN7581_PCIC_PERSTOUT0_RST] = 2 * RST_NR_PER_BANK + 29,
|
||||
+ [EN7581_PCIC_PERSTOUT1_RST] = 2 * RST_NR_PER_BANK + 26,
|
||||
+ [EN7581_PCIC_PERSTOUT2_RST] = 2 * RST_NR_PER_BANK + 16,
|
||||
};
|
||||
|
||||
static const u16 an7583_rst_map[] = {
|
||||
@@ -960,9 +966,7 @@ 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;
|
||||
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
|
||||
regmap_set_bits(map, REG_PCI_CONTROL, mask);
|
||||
|
||||
return 0;
|
||||
@@ -974,9 +978,7 @@ 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;
|
||||
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
|
||||
regmap_clear_bits(map, REG_PCI_CONTROL, mask);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
@@ -1027,9 +1029,16 @@ static int en7523_reset_update(struct re
|
||||
{
|
||||
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
|
||||
u32 addr = rst_data->bank_ofs[id / RST_NR_PER_BANK];
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* For PCIC reset logic is inverted, 0:assert 1:deassert*/
|
||||
+ if (addr == REG_NP_SCU_PCIC)
|
||||
+ val |= assert ? 0 : BIT(id % RST_NR_PER_BANK);
|
||||
+ else
|
||||
+ val |= assert ? BIT(id % RST_NR_PER_BANK) : 0;
|
||||
|
||||
regmap_update_bits(rst_data->map, addr, BIT(id % RST_NR_PER_BANK),
|
||||
- assert ? BIT(id % RST_NR_PER_BANK) : 0);
|
||||
+ val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1051,10 +1060,16 @@ static int en7523_reset_status(struct re
|
||||
{
|
||||
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
|
||||
u32 addr = rst_data->bank_ofs[id / RST_NR_PER_BANK];
|
||||
+ bool inverted = false;
|
||||
u32 val;
|
||||
|
||||
+ /* For PCIC reset logic is inverted, 0:assert 1:deassert*/
|
||||
+ if (addr == REG_NP_SCU_PCIC)
|
||||
+ inverted = true;
|
||||
+
|
||||
regmap_read(rst_data->map, addr, &val);
|
||||
- return !!(val & BIT(id % RST_NR_PER_BANK));
|
||||
+ val &= BIT(id % RST_NR_PER_BANK);
|
||||
+ return inverted ? !val : !!val;
|
||||
}
|
||||
|
||||
static int en7523_reset_xlate(struct reset_controller_dev *rcdev,
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
From 25984a8f0891959a47eff4aa00709de3c23c21e6 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 25 Jun 2026 16:20:16 +0200
|
||||
Subject: [PATCH v4 4/4] PCI: mediatek-gen3: Add 2-lanes mode support for
|
||||
Airoha AN7581
|
||||
|
||||
The Airoha AN7581 SoC supports configuring the first PCIe0 lane to 2-lanes
|
||||
mode (2x link) by bonding it with the second PCIe lane (PCIe1). This is
|
||||
done by configuring the PCIe MUX in the SCU register.
|
||||
|
||||
To correctly configure PCIe0 in 2x link, define in DT the following
|
||||
additional property:
|
||||
- additional reg, 'sec-pcie-mac' for the secondary PCIe.
|
||||
- PERSTOUT reset for both main and secondary PCIE0, called 'perstout' and
|
||||
'sec-perstout'
|
||||
- airoha,scu property to correctly configure the SCU register for the PCIe
|
||||
MUX
|
||||
- 'num-lanes' set to '2' to enable PCIe0 in 2x link
|
||||
|
||||
In such configuration the EQ preset are configured to the same values.
|
||||
|
||||
To permit correct configuration of the PCIe line, additional logic is added
|
||||
to assert and deassert the PERSTOUT resets.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/pci/controller/pcie-mediatek-gen3.c | 106 ++++++++++++++++----
|
||||
1 file changed, 89 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
@@ -31,6 +31,11 @@
|
||||
|
||||
#include "../pci.h"
|
||||
|
||||
+/* AN7581 SCU register */
|
||||
+#define SCU_PCIC 0x88
|
||||
+#define SCU_PCIC_PCIE_CTRL GENMASK(7, 0)
|
||||
+
|
||||
+/* PCIe register */
|
||||
#define PCIE_BASE_CFG_REG 0x14
|
||||
#define PCIE_BASE_CFG_SPEED GENMASK(15, 8)
|
||||
|
||||
@@ -130,6 +135,7 @@
|
||||
#define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2)
|
||||
|
||||
#define MAX_NUM_PHY_RESETS 3
|
||||
+#define MAX_NUM_PERSTOUT_RESETS 2
|
||||
|
||||
#define PCIE_MTK_RESET_TIME_US 10
|
||||
|
||||
@@ -202,9 +208,11 @@ struct mtk_msi_set {
|
||||
struct mtk_gen3_pcie {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
+ void __iomem *sec_base;
|
||||
phys_addr_t reg_base;
|
||||
struct reset_control *mac_reset;
|
||||
struct reset_control_bulk_data phy_resets[MAX_NUM_PHY_RESETS];
|
||||
+ struct reset_control_bulk_data perstout_resets[MAX_NUM_PERSTOUT_RESETS];
|
||||
struct phy *phy;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
@@ -893,6 +901,14 @@ static int mtk_pcie_parse_port(struct mt
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ pcie->perstout_resets[0].id = "perstout";
|
||||
+ pcie->perstout_resets[1].id = "sec-perstout";
|
||||
+
|
||||
+ ret = devm_reset_control_bulk_get_optional_exclusive(dev, MAX_NUM_PERSTOUT_RESETS,
|
||||
+ pcie->perstout_resets);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to get PERSTOUT 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);
|
||||
@@ -926,18 +942,38 @@ static int mtk_pcie_parse_port(struct mt
|
||||
pcie->num_lanes = num_lanes;
|
||||
}
|
||||
|
||||
+ /* Map secondary PCIe for 2-lanes mode for EN7581 */
|
||||
+ if (pcie->num_lanes == 2 && device_is_compatible(dev, "airoha,en7581-pcie")) {
|
||||
+ regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sec-pcie-mac");
|
||||
+ if (!regs)
|
||||
+ return -EINVAL;
|
||||
+ pcie->sec_base = devm_ioremap_resource(dev, regs);
|
||||
+ if (IS_ERR(pcie->sec_base))
|
||||
+ return dev_err_probe(dev, PTR_ERR(pcie->sec_base), "failed to map secondary register base\n");
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie)
|
||||
{
|
||||
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
|
||||
+ unsigned int num_lanes = max(1, pcie->num_lanes);
|
||||
+ struct regmap *pbus_regmap, *scu;
|
||||
struct device *dev = pcie->dev;
|
||||
struct resource_entry *entry;
|
||||
- struct regmap *pbus_regmap;
|
||||
u32 val, args[2], size;
|
||||
resource_size_t addr;
|
||||
- int err;
|
||||
+ int i, err;
|
||||
+
|
||||
+ if (num_lanes > 2)
|
||||
+ return dev_err_probe(dev, -EINVAL, "unsupported num-lanes, maximum 2 lanes supported\n");
|
||||
+
|
||||
+ if (num_lanes == 2) {
|
||||
+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,scu");
|
||||
+ if (IS_ERR(scu))
|
||||
+ return dev_err_probe(dev, PTR_ERR(scu), "failed to map SCU regmap\n");
|
||||
+ }
|
||||
|
||||
/*
|
||||
* The controller may have been left out of reset by the bootloader
|
||||
@@ -975,6 +1011,19 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
return err;
|
||||
}
|
||||
|
||||
+ /* Assert PERSTOUT for all relevant lines */
|
||||
+ err = reset_control_bulk_assert(MAX_NUM_PERSTOUT_RESETS,
|
||||
+ pcie->perstout_resets);
|
||||
+ if (err) {
|
||||
+ dev_err(dev, "failed to assert PERSTOUTs\n");
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ /* Configure SCU MUX to disable PCIE1 for 2 lines mode */
|
||||
+ if (num_lanes == 2)
|
||||
+ regmap_update_bits(scu, SCU_PCIC, SCU_PCIC_PCIE_CTRL,
|
||||
+ FIELD_PREP(SCU_PCIC_PCIE_CTRL, BIT(1)));
|
||||
+
|
||||
/*
|
||||
* Unlike the other MediaTek Gen3 controllers, the Airoha EN7581
|
||||
* requires PHY initialization and power-on before PHY reset deassert.
|
||||
@@ -1007,18 +1056,6 @@ 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);
|
||||
-
|
||||
err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to prepare clock\n");
|
||||
@@ -1026,14 +1063,47 @@ 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.
|
||||
+ * Old Airoha EN7581 clock driver performed PCIe reset via
|
||||
+ * clk callbacks since it has a hw issue with PCIE_PE_RSTB signal.
|
||||
+ * This is now handled by dedicated PERSTOUT resets with clk
|
||||
+ * driver only enabling the refclk.
|
||||
+ *
|
||||
+ * Wait is still needed for refclk to stabilize
|
||||
*/
|
||||
msleep(PCIE_T_PVPERL_MS);
|
||||
|
||||
+ /* Configure all the lines to the same EQ config */
|
||||
+ for (i = 0; i < num_lanes; i++) {
|
||||
+ void __iomem *base = pcie->base;
|
||||
+
|
||||
+ if (i == 1)
|
||||
+ base = pcie->sec_base;
|
||||
+
|
||||
+ 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, 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, base + PCIE_PIPE4_PIE8_REG);
|
||||
+ }
|
||||
+
|
||||
+ /* Deassert PERSTOUT for all relevant lines */
|
||||
+ err = reset_control_bulk_deassert(MAX_NUM_PERSTOUT_RESETS,
|
||||
+ pcie->perstout_resets);
|
||||
+ if (err) {
|
||||
+ dev_err(dev, "failed to deassert PERSTOUTs\n");
|
||||
+ goto err_perstout_deassert;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
|
||||
+err_perstout_deassert:
|
||||
+ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks);
|
||||
err_clk_prepare_enable:
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
@@ -1119,6 +1189,8 @@ static void mtk_pcie_power_down(struct m
|
||||
|
||||
phy_power_off(pcie->phy);
|
||||
phy_exit(pcie->phy);
|
||||
+ reset_control_bulk_assert(MAX_NUM_PERSTOUT_RESETS,
|
||||
+ pcie->perstout_resets);
|
||||
reset_control_bulk_assert(pcie->soc->phy_resets.num_resets,
|
||||
pcie->phy_resets);
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
From: Ryan Chen <rchen14b@gmail.com>
|
||||
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 <rchen14b@gmail.com>
|
||||
--- a/drivers/clk/clk-en7523.c
|
||||
+++ b/drivers/clk/clk-en7523.c
|
||||
@@ -960,9 +960,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;
|
||||
@@ -974,9 +976,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);
|
||||
}
|
||||
-244
@@ -1,244 +0,0 @@
|
||||
From: Ryan Chen <rchen14b@gmail.com>
|
||||
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 <rchen14b@gmail.com>
|
||||
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
@@ -67,6 +67,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)
|
||||
|
||||
@@ -221,6 +229,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] */
|
||||
@@ -969,6 +982,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");
|
||||
@@ -1007,17 +1042,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) {
|
||||
@@ -1026,12 +1072,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:
|
||||
@@ -0,0 +1,25 @@
|
||||
From 12047c34237be89c85a0e144c6c99281656a1153 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jun 2026 14:26:20 +0200
|
||||
Subject: [PATCH] clk: en7523: reset PCIE HB on init
|
||||
|
||||
Reset PCIE on clk HW init to handle warm boot problem with PCIe.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/clk/clk-en7523.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/drivers/clk/clk-en7523.c
|
||||
+++ b/drivers/clk/clk-en7523.c
|
||||
@@ -1147,6 +1147,10 @@ static int en7581_clk_hw_init(struct pla
|
||||
regmap_update_bits(clk_map, REG_NP_SCU_PCIC, REG_PCIE_CTRL,
|
||||
FIELD_PREP(REG_PCIE_CTRL, 3));
|
||||
|
||||
+ /* Reset PCIE_HB */
|
||||
+ regmap_set_bits(clk_map, REG_RST_CTRL1, BIT(29));
|
||||
+ regmap_clear_bits(clk_map, REG_RST_CTRL1, BIT(29));
|
||||
+
|
||||
return en7581_reset_register(&pdev->dev, clk_map, en7581_rst_map,
|
||||
ARRAY_SIZE(en7581_rst_map));
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
From fde8e40b0bf72436ce75d21e8778049368ea200f Mon Sep 17 00:00:00 2001
|
||||
From: Kenneth Kasilag <kenneth@kasilag.me>
|
||||
Date: Mon, 6 Apr 2026 23:04:38 +0000
|
||||
Subject: [PATCH] airoha: fix PERST deassert in PCIe driver
|
||||
|
||||
Due to hardware bugs, the PCIE Gen3 IP in Airoha AN7581 requires a
|
||||
special reset procedure:
|
||||
> MAC reset asserted thru the SCU block
|
||||
> PERST asserted thru the SCU for the desired PCIE lane
|
||||
> PHY initialization runs, clocks enabled
|
||||
> MAC reset deasserted
|
||||
> EQ config (if needed) written
|
||||
> PERST deasserted
|
||||
|
||||
The existing code currently toggles PERST for all three PCIE
|
||||
ports every time mtk_pcie_en7581_power_up is called, resulting
|
||||
in PCIE link down issues.
|
||||
|
||||
This issue was discovered during porting of the 6.18 kernel to
|
||||
AN7581. It is not entirely clear how the hardware seemed to
|
||||
function under kernel 6.12 with similar driver code, it is
|
||||
presumed that differences in assert/deassert times for PERST in
|
||||
6.18 changed which exposed the bug.
|
||||
|
||||
Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
|
||||
---
|
||||
drivers/pci/controller/pcie-mediatek-gen3.c | 124 ++++++++-------
|
||||
1 file changed, 86 insertions(+), 38 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
|
||||
@@ -72,8 +72,21 @@
|
||||
|
||||
/* EN7581 NP_SCU register offsets for x2 link init */
|
||||
#define NP_SCU_LANE_CFG0 0x830
|
||||
+#define NP_SCU_XSI_MAC_RST BIT(7)
|
||||
+#define NP_SCU_XSI_PHY_RST BIT(8)
|
||||
+#define NP_SCU_PCIE2_RST BIT(27)
|
||||
#define NP_SCU_LANE_CFG1 0x834
|
||||
+#define NP_SCU_PCIE0_RST BIT(26)
|
||||
+#define NP_SCU_PCIE1_RST BIT(27)
|
||||
#define NP_SCU_CTRL_REG 0x88
|
||||
+#define NP_SCU_PERSTOUT BIT(29)
|
||||
+#define NP_SCU_PERSTOUT1 BIT(26)
|
||||
+#define NP_SCU_PERSTOUT2 BIT(16)
|
||||
+#define NP_SCU_PERST_ALL (NP_SCU_PERSTOUT | \
|
||||
+ NP_SCU_PERSTOUT1 | \
|
||||
+ NP_SCU_PERSTOUT2)
|
||||
+#define NP_SCU_SERDES_MUX_MASK GENMASK(1, 0)
|
||||
+#define NP_SCU_SERDES_MUX_X2 2
|
||||
|
||||
#define PCIE_LINK_STATUS_REG 0x154
|
||||
#define PCIE_PORT_LINKUP BIT(8)
|
||||
@@ -1004,6 +1017,23 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
dev_info(dev, "x2 mode: sister MAC mapped\n");
|
||||
}
|
||||
|
||||
+ /* Select the correct reset for each PCIe port */
|
||||
+ u32 perst_mask = NP_SCU_PERST_ALL;
|
||||
+ if (pcie->x2_mode)
|
||||
+ perst_mask = NP_SCU_PERSTOUT | NP_SCU_PERSTOUT1;
|
||||
+ else if (pcie->reg_base == 0x1fc00000)
|
||||
+ perst_mask = NP_SCU_PERSTOUT;
|
||||
+ else if (pcie->reg_base == 0x1fc20000)
|
||||
+ perst_mask = NP_SCU_PERSTOUT1;
|
||||
+ else if (pcie->reg_base == 0x1fc40000)
|
||||
+ perst_mask = NP_SCU_PERSTOUT2;
|
||||
+
|
||||
+ /* Assert selected PERST prior to PHY init */
|
||||
+ if (pcie->np_scu) {
|
||||
+ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, perst_mask, 0);
|
||||
+ msleep(1);
|
||||
+ }
|
||||
+
|
||||
err = phy_set_mode(pcie->phy, PHY_MODE_PCIE);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to set PHY mode\n");
|
||||
@@ -1043,26 +1073,22 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/*
|
||||
- * 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.
|
||||
+ * EN7581 x2: Do 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 */
|
||||
+ /* Assert serdes reset on lanes 0 and 1 */
|
||||
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));
|
||||
+ NP_SCU_PCIE0_RST | NP_SCU_PCIE1_RST,
|
||||
+ NP_SCU_PCIE0_RST | NP_SCU_PCIE1_RST);
|
||||
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);
|
||||
+ NP_SCU_SERDES_MUX_MASK,
|
||||
+ NP_SCU_SERDES_MUX_X2);
|
||||
+ msleep(1);
|
||||
}
|
||||
|
||||
err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
|
||||
@@ -1085,11 +1111,9 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
*/
|
||||
msleep(30);
|
||||
|
||||
- /* Deassert serdes reset on all lanes */
|
||||
+ /* Deassert serdes reset on lanes 0 and 1 */
|
||||
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);
|
||||
+ NP_SCU_PCIE0_RST | NP_SCU_PCIE1_RST, 0);
|
||||
|
||||
/* Clear SETTING_REG bit 13 for x2 mode on both MACs */
|
||||
val = readl_relaxed(pcie->base + PCIE_SETTING_REG);
|
||||
@@ -1109,59 +1133,16 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
if (pcie->sister_base)
|
||||
writel_relaxed(0x1018020f, pcie->sister_base + 0x338);
|
||||
|
||||
- /* Deassert PERST for all ports - link training starts */
|
||||
+ /* Deassert PERST on selected port - 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));
|
||||
+ perst_mask,
|
||||
+ perst_mask);
|
||||
|
||||
/* 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");
|
||||
- }
|
||||
+ msleep(1000);
|
||||
|
||||
- dev_info(dev, "x2: init complete, PERST deasserted\n");
|
||||
+ dev_info(dev, "x2: init complete\n");
|
||||
} else {
|
||||
/*
|
||||
* Non-x2 mode: standard EQ config then deassert PERST.
|
||||
@@ -1178,11 +1159,11 @@ static int mtk_pcie_en7581_power_up(stru
|
||||
FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf);
|
||||
writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG);
|
||||
|
||||
- /* Deassert PERST for all ports */
|
||||
+ /* Deassert PERST on selected port */
|
||||
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));
|
||||
+ perst_mask,
|
||||
+ perst_mask);
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user