--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-19, Linaro Limited
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include "stmmac.h"
+#include "stmmac_platform.h"
+
+#define RGMII_IO_MACRO_CONFIG          0x0
+#define SDCC_HC_REG_DLL_CONFIG         0x4
+#define SDCC_HC_REG_DDR_CONFIG         0xC
+#define SDCC_HC_REG_DLL_CONFIG2                0x10
+#define SDC4_STATUS                    0x14
+#define SDCC_USR_CTL                   0x18
+#define RGMII_IO_MACRO_CONFIG2         0x1C
+#define RGMII_IO_MACRO_DEBUG1          0x20
+#define EMAC_SYSTEM_LOW_POWER_DEBUG    0x28
+
+/* RGMII_IO_MACRO_CONFIG fields */
+#define RGMII_CONFIG_FUNC_CLK_EN               BIT(30)
+#define RGMII_CONFIG_POS_NEG_DATA_SEL          BIT(23)
+#define RGMII_CONFIG_GPIO_CFG_RX_INT           GENMASK(21, 20)
+#define RGMII_CONFIG_GPIO_CFG_TX_INT           GENMASK(19, 17)
+#define RGMII_CONFIG_MAX_SPD_PRG_9             GENMASK(16, 8)
+#define RGMII_CONFIG_MAX_SPD_PRG_2             GENMASK(7, 6)
+#define RGMII_CONFIG_INTF_SEL                  GENMASK(5, 4)
+#define RGMII_CONFIG_BYPASS_TX_ID_EN           BIT(3)
+#define RGMII_CONFIG_LOOPBACK_EN               BIT(2)
+#define RGMII_CONFIG_PROG_SWAP                 BIT(1)
+#define RGMII_CONFIG_DDR_MODE                  BIT(0)
+
+/* SDCC_HC_REG_DLL_CONFIG fields */
+#define SDCC_DLL_CONFIG_DLL_RST                        BIT(30)
+#define SDCC_DLL_CONFIG_PDN                    BIT(29)
+#define SDCC_DLL_CONFIG_MCLK_FREQ              GENMASK(26, 24)
+#define SDCC_DLL_CONFIG_CDR_SELEXT             GENMASK(23, 20)
+#define SDCC_DLL_CONFIG_CDR_EXT_EN             BIT(19)
+#define SDCC_DLL_CONFIG_CK_OUT_EN              BIT(18)
+#define SDCC_DLL_CONFIG_CDR_EN                 BIT(17)
+#define SDCC_DLL_CONFIG_DLL_EN                 BIT(16)
+#define SDCC_DLL_MCLK_GATING_EN                        BIT(5)
+#define SDCC_DLL_CDR_FINE_PHASE                        GENMASK(3, 2)
+
+/* SDCC_HC_REG_DDR_CONFIG fields */
+#define SDCC_DDR_CONFIG_PRG_DLY_EN             BIT(31)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY       GENMASK(26, 21)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE  GENMASK(29, 27)
+#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN    BIT(30)
+#define SDCC_DDR_CONFIG_PRG_RCLK_DLY           GENMASK(8, 0)
+
+/* SDCC_HC_REG_DLL_CONFIG2 fields */
+#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS         BIT(21)
+#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC                GENMASK(17, 10)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL  GENMASK(3, 2)
+#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW   BIT(1)
+#define SDCC_DLL_CONFIG2_DDR_CAL_EN            BIT(0)
+
+/* SDC4_STATUS bits */
+#define SDC4_STATUS_DLL_LOCK                   BIT(7)
+
+/* RGMII_IO_MACRO_CONFIG2 fields */
+#define RGMII_CONFIG2_RSVD_CONFIG15            GENMASK(31, 17)
+#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG                BIT(16)
+#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN     BIT(13)
+#define RGMII_CONFIG2_CLK_DIVIDE_SEL           BIT(12)
+#define RGMII_CONFIG2_RX_PROG_SWAP             BIT(7)
+#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL      BIT(6)
+#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN    BIT(5)
+
+struct ethqos_emac_por {
+       unsigned int offset;
+       unsigned int value;
+};
+
+struct qcom_ethqos {
+       struct platform_device *pdev;
+       void __iomem *rgmii_base;
+
+       unsigned int rgmii_clk_rate;
+       struct clk *rgmii_clk;
+       unsigned int speed;
+
+       const struct ethqos_emac_por *por;
+       unsigned int num_por;
+};
+
+static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
+{
+       return readl(ethqos->rgmii_base + offset);
+}
+
+static void rgmii_writel(struct qcom_ethqos *ethqos,
+                        int value, unsigned int offset)
+{
+       writel(value, ethqos->rgmii_base + offset);
+}
+
+static void rgmii_updatel(struct qcom_ethqos *ethqos,
+                         int mask, int val, unsigned int offset)
+{
+       unsigned int temp;
+
+       temp =  rgmii_readl(ethqos, offset);
+       temp = (temp & ~(mask)) | val;
+       rgmii_writel(ethqos, temp, offset);
+}
+
+static void rgmii_dump(struct qcom_ethqos *ethqos)
+{
+       dev_dbg(ðqos->pdev->dev, "Rgmii register dump\n");
+       dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_CONFIG: %x\n",
+               rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG));
+       dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DLL_CONFIG: %x\n",
+               rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG));
+       dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DDR_CONFIG: %x\n",
+               rgmii_readl(ethqos, SDCC_HC_REG_DDR_CONFIG));
+       dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DLL_CONFIG2: %x\n",
+               rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG2));
+       dev_dbg(ðqos->pdev->dev, "SDC4_STATUS: %x\n",
+               rgmii_readl(ethqos, SDC4_STATUS));
+       dev_dbg(ðqos->pdev->dev, "SDCC_USR_CTL: %x\n",
+               rgmii_readl(ethqos, SDCC_USR_CTL));
+       dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_CONFIG2: %x\n",
+               rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG2));
+       dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_DEBUG1: %x\n",
+               rgmii_readl(ethqos, RGMII_IO_MACRO_DEBUG1));
+       dev_dbg(ðqos->pdev->dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %x\n",
+               rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG));
+}
+
+/* Clock rates */
+#define RGMII_1000_NOM_CLK_FREQ                        (250 * 1000 * 1000UL)
+#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ      (50 * 1000 * 1000UL)
+#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ        (5 * 1000 * 1000UL)
+
+static void
+ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed)
+{
+       switch (speed) {
+       case SPEED_1000:
+               ethqos->rgmii_clk_rate =  RGMII_1000_NOM_CLK_FREQ;
+               break;
+
+       case SPEED_100:
+               ethqos->rgmii_clk_rate =  RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
+               break;
+
+       case SPEED_10:
+               ethqos->rgmii_clk_rate =  RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
+               break;
+       }
+
+       clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate);
+}
+
+static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos)
+{
+       rgmii_updatel(ethqos, RGMII_CONFIG_FUNC_CLK_EN,
+                     RGMII_CONFIG_FUNC_CLK_EN, RGMII_IO_MACRO_CONFIG);
+}
+
+static const struct ethqos_emac_por emac_v2_3_0_por[] = {
+       { .offset = RGMII_IO_MACRO_CONFIG,      .value = 0x00C01343 },
+       { .offset = SDCC_HC_REG_DLL_CONFIG,     .value = 0x2004642C },
+       { .offset = SDCC_HC_REG_DDR_CONFIG,     .value = 0x00000000 },
+       { .offset = SDCC_HC_REG_DLL_CONFIG2,    .value = 0x00200000 },
+       { .offset = SDCC_USR_CTL,               .value = 0x00010800 },
+       { .offset = RGMII_IO_MACRO_CONFIG2,     .value = 0x00002060 },
+};
+
+static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
+{
+       unsigned int val;
+       int retry = 1000;
+
+       /* Set CDR_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EN,
+                     SDCC_DLL_CONFIG_CDR_EN, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Set CDR_EXT_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EXT_EN,
+                     SDCC_DLL_CONFIG_CDR_EXT_EN, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Clear CK_OUT_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
+                     0, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Set DLL_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN,
+                     SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
+
+       rgmii_updatel(ethqos, SDCC_DLL_MCLK_GATING_EN,
+                     0, SDCC_HC_REG_DLL_CONFIG);
+
+       rgmii_updatel(ethqos, SDCC_DLL_CDR_FINE_PHASE,
+                     0, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Wait for CK_OUT_EN clear */
+       do {
+               val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG);
+               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+               if (!val)
+                       break;
+               mdelay(1);
+               retry--;
+       } while (retry > 0);
+       if (!retry)
+               dev_err(ðqos->pdev->dev, "Clear CK_OUT_EN timedout\n");
+
+       /* Set CK_OUT_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
+                     SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Wait for CK_OUT_EN set */
+       retry = 1000;
+       do {
+               val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG);
+               val &= SDCC_DLL_CONFIG_CK_OUT_EN;
+               if (val)
+                       break;
+               mdelay(1);
+               retry--;
+       } while (retry > 0);
+       if (!retry)
+               dev_err(ðqos->pdev->dev, "Set CK_OUT_EN timedout\n");
+
+       /* Set DDR_CAL_EN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_CAL_EN,
+                     SDCC_DLL_CONFIG2_DDR_CAL_EN, SDCC_HC_REG_DLL_CONFIG2);
+
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DLL_CLOCK_DIS,
+                     0, SDCC_HC_REG_DLL_CONFIG2);
+
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_MCLK_FREQ_CALC,
+                     0x1A << 10, SDCC_HC_REG_DLL_CONFIG2);
+
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL,
+                     BIT(2), SDCC_HC_REG_DLL_CONFIG2);
+
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW,
+                     SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW,
+                     SDCC_HC_REG_DLL_CONFIG2);
+
+       return 0;
+}
+
+static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos)
+{
+       /* Disable loopback mode */
+       rgmii_updatel(ethqos, RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN,
+                     0, RGMII_IO_MACRO_CONFIG2);
+
+       /* Select RGMII, write 0 to interface select */
+       rgmii_updatel(ethqos, RGMII_CONFIG_INTF_SEL,
+                     0, RGMII_IO_MACRO_CONFIG);
+
+       switch (ethqos->speed) {
+       case SPEED_1000:
+               rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
+                             RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
+                             0, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
+                             RGMII_CONFIG_POS_NEG_DATA_SEL,
+                             RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
+                             RGMII_CONFIG_PROG_SWAP, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+                             RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+                             RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
+                             RGMII_CONFIG2_RX_PROG_SWAP,
+                             RGMII_IO_MACRO_CONFIG2);
+
+               /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_RCLK_DLY,
+                             57, SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_DLY_EN,
+                             SDCC_DDR_CONFIG_PRG_DLY_EN,
+                             SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
+                             RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG);
+               break;
+
+       case SPEED_100:
+               rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
+                             RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
+                             RGMII_CONFIG_BYPASS_TX_ID_EN,
+                             RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
+                             0, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
+                             0, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+                             RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+                             RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_2,
+                             BIT(6), RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               /* Write 0x5 to PRG_RCLK_DLY_CODE */
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+                             (BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+                             SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+                             SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+                             SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+                             SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
+                             RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG);
+               break;
+
+       case SPEED_10:
+               rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
+                             RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
+                             RGMII_CONFIG_BYPASS_TX_ID_EN,
+                             RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
+                             0, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
+                             0, RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_9,
+                             BIT(12) | GENMASK(9, 8),
+                             RGMII_IO_MACRO_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
+                             0, RGMII_IO_MACRO_CONFIG2);
+               /* Write 0x5 to PRG_RCLK_DLY_CODE */
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
+                             (BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+                             SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+                             SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+                             SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+                             SDCC_HC_REG_DDR_CONFIG);
+               rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
+                             RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG);
+               break;
+       default:
+               dev_err(ðqos->pdev->dev,
+                       "Invalid speed %d\n", ethqos->speed);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ethqos_configure(struct qcom_ethqos *ethqos)
+{
+       volatile unsigned int dll_lock;
+       unsigned int i, retry = 1000;
+
+       /* Reset to POR values and enable clk */
+       for (i = 0; i < ethqos->num_por; i++)
+               rgmii_writel(ethqos, ethqos->por[i].value,
+                            ethqos->por[i].offset);
+       ethqos_set_func_clk_en(ethqos);
+
+       /* Initialize the DLL first */
+
+       /* Set DLL_RST */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST,
+                     SDCC_DLL_CONFIG_DLL_RST, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Set PDN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN,
+                     SDCC_DLL_CONFIG_PDN, SDCC_HC_REG_DLL_CONFIG);
+
+       /* Clear DLL_RST */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST, 0,
+                     SDCC_HC_REG_DLL_CONFIG);
+
+       /* Clear PDN */
+       rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN, 0,
+                     SDCC_HC_REG_DLL_CONFIG);
+
+       if (ethqos->speed != SPEED_100 && ethqos->speed != SPEED_10) {
+               /* Set DLL_EN */
+               rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN,
+                             SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
+
+               /* Set CK_OUT_EN */
+               rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
+                             SDCC_DLL_CONFIG_CK_OUT_EN,
+                             SDCC_HC_REG_DLL_CONFIG);
+
+               /* Set USR_CTL bit 26 with mask of 3 bits */
+               rgmii_updatel(ethqos, GENMASK(26, 24), BIT(26), SDCC_USR_CTL);
+
+               /* wait for DLL LOCK */
+               do {
+                       mdelay(1);
+                       dll_lock = rgmii_readl(ethqos, SDC4_STATUS);
+                       if (dll_lock & SDC4_STATUS_DLL_LOCK)
+                               break;
+               } while (retry > 0);
+               if (!retry)
+                       dev_err(ðqos->pdev->dev,
+                               "Timeout while waiting for DLL lock\n");
+       }
+
+       if (ethqos->speed == SPEED_1000)
+               ethqos_dll_configure(ethqos);
+
+       ethqos_rgmii_macro_init(ethqos);
+
+       return 0;
+}
+
+static void ethqos_fix_mac_speed(void *priv, unsigned int speed)
+{
+       struct qcom_ethqos *ethqos = priv;
+
+       ethqos->speed = speed;
+       ethqos_update_rgmii_clk(ethqos, speed);
+       ethqos_configure(ethqos);
+}
+
+static int qcom_ethqos_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct plat_stmmacenet_data *plat_dat;
+       struct stmmac_resources stmmac_res;
+       struct qcom_ethqos *ethqos;
+       struct resource *res;
+       int ret;
+
+       ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+       if (ret)
+               return ret;
+
+       plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
+       if (IS_ERR(plat_dat)) {
+               dev_err(&pdev->dev, "dt configuration failed\n");
+               return PTR_ERR(plat_dat);
+       }
+
+       ethqos = devm_kzalloc(&pdev->dev, sizeof(*ethqos), GFP_KERNEL);
+       if (!ethqos) {
+               ret = -ENOMEM;
+               goto err_mem;
+       }
+
+       ethqos->pdev = pdev;
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rgmii");
+       ethqos->rgmii_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ethqos->rgmii_base)) {
+               dev_err(&pdev->dev, "Can't get rgmii base\n");
+               ret = PTR_ERR(ethqos->rgmii_base);
+               goto err_mem;
+       }
+
+       ethqos->por = of_device_get_match_data(&pdev->dev);
+
+       ethqos->rgmii_clk = devm_clk_get(&pdev->dev, "rgmii");
+       if (!ethqos->rgmii_clk) {
+               ret = -ENOMEM;
+               goto err_mem;
+       }
+
+       ret = clk_prepare_enable(ethqos->rgmii_clk);
+       if (ret)
+               goto err_mem;
+
+       ethqos->speed = SPEED_1000;
+       ethqos_update_rgmii_clk(ethqos, SPEED_1000);
+       ethqos_set_func_clk_en(ethqos);
+
+       plat_dat->bsp_priv = ethqos;
+       plat_dat->fix_mac_speed = ethqos_fix_mac_speed;
+       plat_dat->has_gmac4 = 1;
+       plat_dat->pmt = 1;
+       plat_dat->tso_en = of_property_read_bool(np, "snps,tso");
+
+       ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+       if (ret)
+               goto err_clk;
+
+       rgmii_dump(ethqos);
+
+       return ret;
+
+err_clk:
+       clk_disable_unprepare(ethqos->rgmii_clk);
+
+err_mem:
+       stmmac_remove_config_dt(pdev, plat_dat);
+
+       return ret;
+}
+
+static int qcom_ethqos_remove(struct platform_device *pdev)
+{
+       struct qcom_ethqos *ethqos;
+       int ret;
+
+       ethqos = get_stmmac_bsp_priv(&pdev->dev);
+       if (!ethqos)
+               return -ENODEV;
+
+       ret = stmmac_pltfr_remove(pdev);
+       clk_disable_unprepare(ethqos->rgmii_clk);
+
+       return ret;
+}
+
+static const struct of_device_id qcom_ethqos_match[] = {
+       { .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_por},
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_ethqos_match);
+
+static struct platform_driver qcom_ethqos_driver = {
+       .probe  = qcom_ethqos_probe,
+       .remove = qcom_ethqos_remove,
+       .driver = {
+               .name           = "qcom-ethqos",
+               .pm             = &stmmac_pltfr_pm_ops,
+               .of_match_table = of_match_ptr(qcom_ethqos_match),
+       },
+};
+module_platform_driver(qcom_ethqos_driver);
+
+MODULE_DESCRIPTION("Qualcomm ETHQOS driver");
+MODULE_LICENSE("GPL v2");