CONFIG_GPIO_WATCHDOG=y
# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set
CONFIG_GRO_CELLS=y
-CONFIG_IPQ5018_PHY=y
CONFIG_IPQ_CMN_PLL=y
CONFIG_IPQ_GCC_5018=y
CONFIG_LEDS_PWM=y
--- /dev/null
+From f6a4a55ae5d99f865e106916a9295548e381de47 Mon Sep 17 00:00:00 2001
+Date: Mon, 30 Jun 2025 16:35:00 +0400
+Subject: clk: qcom: gcc-ipq5018: fix GE PHY reset
+
+The MISC reset is supposed to trigger a resets across the MDC, DSP, and
+RX & TX clocks of the IPQ5018 internal GE PHY. So let's set the bitmask
+of the reset definition accordingly in the GCC as per the downstream
+driver.
+
+Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/00743c3e82fa87cba4460e7a2ba32f473a9ce932
+
+---
+ drivers/clk/qcom/gcc-ipq5018.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+(limited to 'drivers/clk/qcom/gcc-ipq5018.c')
+
+--- a/drivers/clk/qcom/gcc-ipq5018.c
++++ b/drivers/clk/qcom/gcc-ipq5018.c
+@@ -3660,7 +3660,7 @@ static const struct qcom_reset_map gcc_i
+ [GCC_WCSS_AXI_S_ARES] = { 0x59008, 6 },
+ [GCC_WCSS_Q6_BCR] = { 0x18004, 0 },
+ [GCC_WCSSAON_RESET] = { 0x59010, 0},
+- [GCC_GEPHY_MISC_ARES] = { 0x56004, 0 },
++ [GCC_GEPHY_MISC_ARES] = { 0x56004, .bitmask = GENMASK(3, 0) },
+ };
+
+ static const struct of_device_id gcc_ipq5018_match_table[] = {
--- /dev/null
+From d46502279a11b48ede1d8bf65a229c8231bf0602 Mon Sep 17 00:00:00 2001
+Date: Fri, 13 Jun 2025 05:55:08 +0400
+Subject: net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support
+
+The IPQ5018 SoC contains a single internal Gigabit Ethernet PHY which
+provides an MDI interface directly to an RJ45 connector or an external
+switch over a PHY to PHY link.
+
+The PHY supports 10BASE-T/100BASE-TX/1000BASE-T link modes in SGMII
+interface mode, CDT, auto-negotiation and 802.3az EEE.
+
+Let's add support for this PHY in the at803x driver as it falls within
+the Qualcomm Atheros OUI.
+
+---
+ drivers/net/phy/qcom/at803x.c | 167 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 167 insertions(+)
+
+(limited to 'drivers/net/phy/qcom/at803x.c')
+
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -19,6 +19,7 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/of.h>
+ #include <linux/phylink.h>
++#include <linux/reset.h>
+ #include <linux/sfp.h>
+ #include <dt-bindings/net/qca-ar803x.h>
+
+@@ -93,6 +94,8 @@
+ #define ATH8035_PHY_ID 0x004dd072
+ #define AT8030_PHY_ID_MASK 0xffffffef
+
++#define IPQ5018_PHY_ID 0x004dd0c0
++
+ #define QCA9561_PHY_ID 0x004dd042
+
+ #define AT803X_PAGE_FIBER 0
+@@ -105,6 +108,48 @@
+ /* disable hibernation mode */
+ #define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
+
++#define IPQ5018_PHY_FIFO_CONTROL 0x19
++#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0)
++
++#define IPQ5018_PHY_DEBUG_EDAC 0x4380
++#define IPQ5018_PHY_MMD1_MDAC 0x8100
++#define IPQ5018_PHY_DAC_MASK GENMASK(15, 8)
++
++/* MDAC and EDAC values for short cable length */
++#define IPQ5018_PHY_DEBUG_EDAC_VAL 0x10
++#define IPQ5018_PHY_MMD1_MDAC_VAL 0x10
++
++#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000
++#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001
++#define IPQ5018_PHY_PCS_EEE_TX_TIMER 0x8008
++#define IPQ5018_PHY_PCS_EEE_RX_TIMER 0x8009
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3 0x8074
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4 0x8075
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5 0x8076
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6 0x8077
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7 0x8078
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9 0x807a
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13 0x807e
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL14 0x807f
++
++#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1
++#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6
++#define IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL 0x7880
++#define IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL 0xc8
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL 0xc040
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL 0xa060
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL 0xc040
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL 0xa060
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL 0xc24c
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL 0xc060
++#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL 0xb060
++#define IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL 0x90b0
++
++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1
++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7, 4)
++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50
++#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080
++
+ MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+@@ -130,6 +175,11 @@ struct at803x_context {
+ u16 led_control;
+ };
+
++struct ipq5018_priv {
++ struct reset_control *rst;
++ bool set_short_cable_dac;
++};
++
+ static int at803x_write_page(struct phy_device *phydev, int page)
+ {
+ int mask;
+@@ -960,6 +1010,109 @@ static int at8035_probe(struct phy_devic
+ return at8035_parse_dt(phydev);
+ }
+
++static int ipq5018_cable_test_start(struct phy_device *phydev)
++{
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL4,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL5,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL6,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL7,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL9,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL13,
++ IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
++ IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL);
++
++ /* we do all the (time consuming) work later */
++ return 0;
++}
++
++static int ipq5018_config_init(struct phy_device *phydev)
++{
++ struct ipq5018_priv *priv = phydev->priv;
++ u16 val;
++
++ /*
++ * set LDO efuse: first temporarily store ANA_DAC_FILTER value from
++ * debug register as it will be reset once the ANA_LDO_EFUSE register
++ * is written to
++ */
++ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
++ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
++ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
++ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
++ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
++
++ /* set 8023AZ EEE TX and RX timer values */
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER,
++ IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER,
++ IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL);
++
++ /* set MSE threshold values */
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
++ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
++ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
++
++ /* PHY DAC values are optional and only set in a PHY to PHY link architecture */
++ if (priv->set_short_cable_dac) {
++ /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
++ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
++ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL);
++
++ /* setting EDAC (Error-detection and Correction) in debug register */
++ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
++ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL);
++ }
++
++ return 0;
++}
++
++static void ipq5018_link_change_notify(struct phy_device *phydev)
++{
++ /*
++ * Reset the FIFO buffer upon link disconnects to clear any residual data
++ * which may cause issues with the FIFO which it cannot recover from.
++ */
++ mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr,
++ IPQ5018_PHY_FIFO_CONTROL, IPQ5018_PHY_FIFO_RESET,
++ phydev->link ? IPQ5018_PHY_FIFO_RESET : 0);
++}
++
++static int ipq5018_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ struct ipq5018_priv *priv;
++ int ret;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->set_short_cable_dac = of_property_read_bool(dev->of_node,
++ "qcom,dac-preset-short-cable");
++
++ priv->rst = devm_reset_control_array_get_exclusive(dev);
++ if (IS_ERR(priv->rst))
++ return dev_err_probe(dev, PTR_ERR(priv->rst),
++ "failed to acquire reset\n");
++
++ ret = reset_control_reset(priv->rst);
++ if (ret)
++ return dev_err_probe(dev, ret, "failed to reset\n");
++
++ phydev->priv = priv;
++
++ return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -1052,6 +1205,19 @@ static struct phy_driver at803x_driver[]
+ .soft_reset = genphy_soft_reset,
+ .config_aneg = at803x_config_aneg,
+ }, {
++ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
++ .name = "Qualcomm Atheros IPQ5018 internal PHY",
++ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
++ .probe = ipq5018_probe,
++ .config_init = ipq5018_config_init,
++ .link_change_notify = ipq5018_link_change_notify,
++ .read_status = at803x_read_status,
++ .config_intr = at803x_config_intr,
++ .handle_interrupt = at803x_handle_interrupt,
++ .cable_test_start = ipq5018_cable_test_start,
++ .cable_test_get_status = qca808x_cable_test_get_status,
++ .soft_reset = genphy_soft_reset,
++}, {
+ /* Qualcomm Atheros QCA9561 */
+ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
+ .name = "Qualcomm Atheros QCA9561 built-in PHY",
+@@ -1077,6 +1243,7 @@ static const struct mdio_device_id __may
+ { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
+ { }
+ };
--- /dev/null
+From 1e2261a669a9596ba435c6fe524e026bac0f0e2f Mon Sep 17 00:00:00 2001
+Date: Mon, 30 Jun 2025 16:35:01 +0400
+Subject: arm64: dts: qcom: ipq5018: Add MDIO buses
+
+IPQ5018 contains two mdio buses of which one bus is used to control the
+SoC's internal GE PHY, while the other bus is connected to external PHYs
+or switches.
+
+There's already support for IPQ5018 in the mdio-ipq4019 driver, so let's
+simply add the mdio nodes for them.
+
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi')
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -185,6 +185,30 @@
+ status = "disabled";
+ };
+
++ mdio0: mdio@88000 {
++ compatible = "qcom,ipq5018-mdio";
++ reg = <0x00088000 0x64>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ clocks = <&gcc GCC_MDIO0_AHB_CLK>;
++ clock-names = "gcc_mdio_ahb_clk";
++
++ status = "disabled";
++ };
++
++ mdio1: mdio@90000 {
++ compatible = "qcom,ipq5018-mdio";
++ reg = <0x00090000 0x64>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ clocks = <&gcc GCC_MDIO1_AHB_CLK>;
++ clock-names = "gcc_mdio_ahb_clk";
++
++ status = "disabled";
++ };
++
+ qfprom: qfprom@a0000 {
+ compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
+ reg = <0x000a0000 0x1000>;
--- /dev/null
+From f5f2b835e316df29b89e28ed7e467df473932e8d Mon Sep 17 00:00:00 2001
+Date: Mon, 30 Jun 2025 16:35:02 +0400
+Subject: arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus
+
+The IPQ5018 SoC contains an internal GE PHY, always at phy address 7.
+As such, let's add the GE PHY node to the SoC dtsi.
+
+The LDO controller found in the SoC must be enabled to provide constant
+low voltages to the PHY. The mdio-ipq4019 driver already has support
+for this, so adding the appropriate TCSR register offset.
+
+In addition, the GE PHY outputs both the RX and TX clocks to the GCC
+which gate controls them and routes them back to the PHY itself.
+So let's create two DT fixed clocks and register them in the GCC node.
+
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 26 +++++++++++++++++++++++---
+ 1 file changed, 23 insertions(+), 3 deletions(-)
+
+(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi')
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -17,6 +17,18 @@
+ #size-cells = <2>;
+
+ clocks {
++ gephy_rx_clk: gephy-rx-clk {
++ compatible = "fixed-clock";
++ clock-frequency = <125000000>;
++ #clock-cells = <0>;
++ };
++
++ gephy_tx_clk: gephy-tx-clk {
++ compatible = "fixed-clock";
++ clock-frequency = <125000000>;
++ #clock-cells = <0>;
++ };
++
+ sleep_clk: sleep-clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+@@ -187,7 +199,8 @@
+
+ mdio0: mdio@88000 {
+ compatible = "qcom,ipq5018-mdio";
+- reg = <0x00088000 0x64>;
++ reg = <0x00088000 0x64>,
++ <0x019475c4 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+@@ -195,6 +208,13 @@
+ clock-names = "gcc_mdio_ahb_clk";
+
+ status = "disabled";
++
++ ge_phy: ethernet-phy@7 {
++ compatible = "ethernet-phy-id004d.d0c0";
++ reg = <7>;
++
++ resets = <&gcc GCC_GEPHY_MISC_ARES>;
++ };
+ };
+
+ mdio1: mdio@90000 {
+@@ -346,8 +366,8 @@
+ <&pcie0_phy>,
+ <&pcie1_phy>,
+ <0>,
+- <0>,
+- <0>,
++ <&gephy_rx_clk>,
++ <&gephy_tx_clk>,
+ <0>,
+ <0>;
+ #clock-cells = <1>;
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -341,6 +341,16 @@
+@@ -385,6 +385,16 @@
reg = <0x01937000 0x21000>;
};
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -296,6 +296,30 @@
+@@ -340,6 +340,30 @@
#thermal-sensor-cells = <1>;
};
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -257,6 +257,14 @@
+@@ -301,6 +301,14 @@
};
};
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -420,6 +420,16 @@
+@@ -464,6 +464,16 @@
status = "disabled";
};
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -444,6 +444,21 @@
+@@ -488,6 +488,21 @@
status = "disabled";
};
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -459,6 +459,36 @@
+@@ -503,6 +503,36 @@
status = "disabled";
};
+ #clock-cells = <0>;
+ };
+
- sleep_clk: sleep-clk {
+ gephy_rx_clk: gephy-rx-clk {
compatible = "fixed-clock";
- #clock-cells = <0>;
-@@ -185,6 +191,17 @@
+ clock-frequency = <125000000>;
+@@ -229,6 +235,17 @@
status = "disabled";
};
+++ /dev/null
-From 19600588e6403ff9f6c1e985fc025afb9160a56f Mon Sep 17 00:00:00 2001
-Date: Wed, 28 May 2025 08:37:25 +0400
-Subject: [PATCH v2 2/5] clk: qcom: gcc-ipq5018: fix GE PHY reset
-MIME-Version: 1.0
-Content-Type: text/plain; charset="utf-8"
-Content-Transfer-Encoding: 7bit
-
-The MISC reset is supposed to trigger a resets across the MDC, DSP, and
-RX & TX clocks of the IPQ5018 internal GE PHY. So let's set the bitmask
-of the reset definition accordingly in the GCC as per the downstream
-driver.
-
-Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/00743c3e82fa87cba4460e7a2ba32f473a9ce932
-
----
- drivers/clk/qcom/gcc-ipq5018.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/clk/qcom/gcc-ipq5018.c
-+++ b/drivers/clk/qcom/gcc-ipq5018.c
-@@ -3660,7 +3660,7 @@ static const struct qcom_reset_map gcc_i
- [GCC_WCSS_AXI_S_ARES] = { 0x59008, 6 },
- [GCC_WCSS_Q6_BCR] = { 0x18004, 0 },
- [GCC_WCSSAON_RESET] = { 0x59010, 0},
-- [GCC_GEPHY_MISC_ARES] = { 0x56004, 0 },
-+ [GCC_GEPHY_MISC_ARES] = { 0x56004, .bitmask = GENMASK(3, 0) },
- };
-
- static const struct of_device_id gcc_ipq5018_match_table[] = {
+++ /dev/null
-From 9a89cb300c1ed5b90bae5684c88c85895a15c849 Mon Sep 17 00:00:00 2001
-Date: Mon, 02 Jun 2025 12:50:39 +0400
-Subject: [PATCH v3 3/5] net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support
-MIME-Version: 1.0
-Content-Type: text/plain; charset="utf-8"
-Content-Transfer-Encoding: 7bit
-
-The IPQ5018 SoC contains a single internal Gigabit Ethernet PHY which
-provides an MDI interface directly to an RJ45 connector or an external
-switch over a PHY to PHY link.
-
-The PHY supports 10/100/1000 mbps link modes, CDT, auto-negotiation and
-802.3az EEE.
-
-Let's add support for this PHY in the at803x driver as it falls within
-the Qualcomm Atheros OUI.
-
----
- drivers/net/phy/qcom/Kconfig | 2 +-
- drivers/net/phy/qcom/at803x.c | 185 ++++++++++++++++++++++++++++++++++++++++--
- 2 files changed, 178 insertions(+), 9 deletions(-)
-
---- a/drivers/net/phy/qcom/Kconfig
-+++ b/drivers/net/phy/qcom/Kconfig
-@@ -7,7 +7,7 @@ config AT803X_PHY
- select QCOM_NET_PHYLIB
- depends on REGULATOR
- help
-- Currently supports the AR8030, AR8031, AR8033, AR8035 model
-+ Currently supports the AR8030, AR8031, AR8033, AR8035, IPQ5018 model
-
- config QCA83XX_PHY
- tristate "Qualcomm Atheros QCA833x PHYs"
---- a/drivers/net/phy/qcom/at803x.c
-+++ b/drivers/net/phy/qcom/at803x.c
-@@ -7,19 +7,24 @@
- */
-
--#include <linux/phy.h>
--#include <linux/module.h>
--#include <linux/string.h>
--#include <linux/netdevice.h>
-+#include <linux/bitfield.h>
-+#include <linux/clk.h>
-+#include <linux/clk-provider.h>
- #include <linux/etherdevice.h>
- #include <linux/ethtool_netlink.h>
--#include <linux/bitfield.h>
--#include <linux/regulator/of_regulator.h>
--#include <linux/regulator/driver.h>
--#include <linux/regulator/consumer.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/module.h>
-+#include <linux/netdevice.h>
- #include <linux/of.h>
-+#include <linux/phy.h>
- #include <linux/phylink.h>
-+#include <linux/regmap.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/regulator/driver.h>
-+#include <linux/regulator/of_regulator.h>
-+#include <linux/reset.h>
- #include <linux/sfp.h>
-+#include <linux/string.h>
- #include <dt-bindings/net/qca-ar803x.h>
-
- #include "qcom.h"
-@@ -93,6 +98,8 @@
- #define ATH8035_PHY_ID 0x004dd072
- #define AT8030_PHY_ID_MASK 0xffffffef
-
-+#define IPQ5018_PHY_ID 0x004dd0c0
-+
- #define QCA9561_PHY_ID 0x004dd042
-
- #define AT803X_PAGE_FIBER 0
-@@ -105,6 +112,50 @@
- /* disable hibernation mode */
- #define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
-
-+#define IPQ5018_PHY_FIFO_CONTROL 0x19
-+#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0)
-+
-+#define IPQ5018_PHY_DEBUG_EDAC 0x4380
-+#define IPQ5018_PHY_MMD1_MDAC 0x8100
-+#define IPQ5018_PHY_DAC_MASK GENMASK(15, 8)
-+
-+/* MDAC and EDAC values for short cable length */
-+#define IPQ5018_PHY_DEBUG_EDAC_VAL 0x10
-+#define IPQ5018_PHY_MMD1_MDAC_VAL 0x10
-+
-+#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000
-+#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001
-+#define IPQ5018_PHY_PCS_AZ_CTRL1 0x8008
-+#define IPQ5018_PHY_PCS_AZ_CTRL2 0x8009
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3 0x8074
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4 0x8075
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5 0x8076
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6 0x8077
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7 0x8078
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9 0x807a
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13 0x807e
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL14 0x807f
-+
-+#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1
-+#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6
-+#define IPQ5018_PHY_PCS_AZ_CTRL1_VAL 0x7880
-+#define IPQ5018_PHY_PCS_AZ_CTRL2_VAL 0xc8
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL 0xc040
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL 0xa060
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL 0xc040
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL 0xa060
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL 0xc24c
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL 0xc060
-+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL 0xb060
-+#define IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL 0x90b0
-+
-+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1
-+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7, 4)
-+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50
-+#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080
-+
-+#define IPQ5018_TCSR_ETH_LDO_READY BIT(0)
-+
- MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-@@ -130,6 +181,11 @@ struct at803x_context {
- u16 led_control;
- };
-
-+struct ipq5018_priv {
-+ struct reset_control *rst;
-+ bool set_short_cable_dac;
-+};
-+
- static int at803x_write_page(struct phy_device *phydev, int page)
- {
- int mask;
-@@ -960,6 +1016,105 @@ static int at8035_probe(struct phy_devic
- return at8035_parse_dt(phydev);
- }
-
-+static int ipq5018_cable_test_start(struct phy_device *phydev)
-+{
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL4,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL5,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL6,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL7,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL9,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL13,
-+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
-+ IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL);
-+
-+ /* we do all the (time consuming) work later */
-+ return 0;
-+}
-+
-+static int ipq5018_config_init(struct phy_device *phydev)
-+{
-+ struct ipq5018_priv *priv = phydev->priv;
-+ u16 val = 0;
-+
-+ /*
-+ * set LDO efuse: first temporarily store ANA_DAC_FILTER value from
-+ * debug register as it will be reset once the ANA_LDO_EFUSE register
-+ * is written to
-+ */
-+ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
-+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
-+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
-+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
-+ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
-+
-+ /* set 8023AZ CTRL values */
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_AZ_CTRL1,
-+ IPQ5018_PHY_PCS_AZ_CTRL1_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_AZ_CTRL2,
-+ IPQ5018_PHY_PCS_AZ_CTRL2_VAL);
-+
-+ /* set MSE threshold values */
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
-+ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
-+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
-+ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
-+
-+ /* PHY DAC values are optional and only set in a PHY to PHY link architecture */
-+ if (priv->set_short_cable_dac) {
-+ /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
-+ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
-+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL);
-+
-+ /* setting EDAC (Error-detection and Correction) in debug register */
-+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
-+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL);
-+ }
-+
-+ return 0;
-+}
-+
-+static void ipq5018_link_change_notify(struct phy_device *phydev)
-+{
-+ mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr,
-+ IPQ5018_PHY_FIFO_CONTROL, IPQ5018_PHY_FIFO_RESET,
-+ phydev->link ? IPQ5018_PHY_FIFO_RESET : 0);
-+}
-+
-+static int ipq5018_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct ipq5018_priv *priv;
-+ int ret;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->set_short_cable_dac = of_property_read_bool(dev->of_node,
-+ "qcom,dac-preset-short-cable");
-+
-+ priv->rst = devm_reset_control_array_get_exclusive(dev);
-+ if (IS_ERR_OR_NULL(priv->rst))
-+ return dev_err_probe(dev, PTR_ERR(priv->rst),
-+ "failed to acquire reset\n");
-+
-+ ret = reset_control_reset(priv->rst);
-+ if (ret)
-+ return dev_err_probe(dev, ret, "failed to reset\n");
-+
-+ phydev->priv = priv;
-+
-+ return 0;
-+}
-+
- static struct phy_driver at803x_driver[] = {
- {
- /* Qualcomm Atheros AR8035 */
-@@ -1052,6 +1207,19 @@ static struct phy_driver at803x_driver[]
- .soft_reset = genphy_soft_reset,
- .config_aneg = at803x_config_aneg,
- }, {
-+ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
-+ .name = "Qualcomm Atheros IPQ5018 internal PHY",
-+ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
-+ .probe = ipq5018_probe,
-+ .config_init = ipq5018_config_init,
-+ .link_change_notify = ipq5018_link_change_notify,
-+ .read_status = at803x_read_status,
-+ .config_intr = at803x_config_intr,
-+ .handle_interrupt = at803x_handle_interrupt,
-+ .cable_test_start = ipq5018_cable_test_start,
-+ .cable_test_get_status = qca808x_cable_test_get_status,
-+ .soft_reset = genphy_soft_reset,
-+}, {
- /* Qualcomm Atheros QCA9561 */
- PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
- .name = "Qualcomm Atheros QCA9561 built-in PHY",
-@@ -1077,6 +1245,7 @@ static const struct mdio_device_id __may
- { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
- { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
- { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
-+ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
- { }
- };
+++ /dev/null
-From 97a159dd7747724619e54cb3460d9b8d4ed08be7 Mon Sep 17 00:00:00 2001
-Date: Mon, 02 Jun 2025 12:50:40 +0400
-Subject: [PATCH v3 4/5] arm64: dts: qcom: ipq5018: Add MDIO buses
-MIME-Version: 1.0
-Content-Type: text/plain; charset="utf-8"
-Content-Transfer-Encoding: 7bit
-
-IPQ5018 contains two mdio buses of which one bus is used to control the
-SoC's internal GE PHY, while the other bus is connected to external PHYs
-or switches.
-
-There's already support for IPQ5018 in the mdio-ipq4019 driver, so let's
-simply add the mdio nodes for them.
-
----
- arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
- 1 file changed, 24 insertions(+)
-
---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -191,6 +191,30 @@
- status = "disabled";
- };
-
-+ mdio0: mdio@88000 {
-+ compatible = "qcom,ipq5018-mdio";
-+ reg = <0x00088000 0x64>;
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ clocks = <&gcc GCC_MDIO0_AHB_CLK>;
-+ clock-names = "gcc_mdio_ahb_clk";
-+
-+ status = "disabled";
-+ };
-+
-+ mdio1: mdio@90000 {
-+ compatible = "qcom,ipq5018-mdio";
-+ reg = <0x00090000 0x64>;
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ clocks = <&gcc GCC_MDIO1_AHB_CLK>;
-+ clock-names = "gcc_mdio_ahb_clk";
-+
-+ status = "disabled";
-+ };
-+
- cmn_pll: clock-controller@9b000 {
- compatible = "qcom,ipq9574-cmn-pll";
- reg = <0x0009b000 0x800>;
+++ /dev/null
-From 1b733e878ac1292c6e0f2e9a49685b80c35619b0 Mon Sep 17 00:00:00 2001
-Date: Mon, 02 Jun 2025 12:50:41 +0400
-Subject: [PATCH v3 5/5] arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus
-MIME-Version: 1.0
-Content-Type: text/plain; charset="utf-8"
-Content-Transfer-Encoding: 7bit
-
-The IPQ5018 SoC contains an internal GE PHY, always at phy address 7.
-As such, let's add the GE PHY node to the SoC dtsi.
-
-The LDO controller found in the SoC must be enabled to provide constant
-low voltages to the PHY. The mdio-ipq4019 driver already has support
-for this, so adding the appropriate TCSR register offset.
-
-In addition, the GE PHY outputs both the RX and TX clocks to the GCC
-which gate controls them and routes them back to the PHY itself.
-So let's create two DT fixed clocks and register them in the GCC node.
-
----
- arch/arm64/boot/dts/qcom/ipq5018.dtsi | 26 +++++++++++++++++++++++---
- 1 file changed, 23 insertions(+), 3 deletions(-)
-
---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
-@@ -23,6 +23,18 @@
- #clock-cells = <0>;
- };
-
-+ gephy_rx_clk: gephy-rx-clk {
-+ compatible = "fixed-clock";
-+ clock-frequency = <125000000>;
-+ #clock-cells = <0>;
-+ };
-+
-+ gephy_tx_clk: gephy-tx-clk {
-+ compatible = "fixed-clock";
-+ clock-frequency = <125000000>;
-+ #clock-cells = <0>;
-+ };
-+
- sleep_clk: sleep-clk {
- compatible = "fixed-clock";
- #clock-cells = <0>;
-@@ -193,7 +205,8 @@
-
- mdio0: mdio@88000 {
- compatible = "qcom,ipq5018-mdio";
-- reg = <0x00088000 0x64>;
-+ reg = <0x00088000 0x64>,
-+ <0x019475c4 0x4>;
- #address-cells = <1>;
- #size-cells = <0>;
-
-@@ -201,6 +214,13 @@
- clock-names = "gcc_mdio_ahb_clk";
-
- status = "disabled";
-+
-+ ge_phy: ethernet-phy@7 {
-+ compatible = "ethernet-phy-id004d.d0c0";
-+ reg = <7>;
-+
-+ resets = <&gcc GCC_GEPHY_MISC_ARES>;
-+ };
- };
-
- mdio1: mdio@90000 {
-@@ -395,8 +415,8 @@
- <&pcie0_phy>,
- <&pcie1_phy>,
- <0>,
-- <0>,
-- <0>,
-+ <&gephy_rx_clk>,
-+ <&gephy_tx_clk>,
- <0>,
- <0>;
- #clock-cells = <1>;