Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / drivers / net / dwc_eth_qos.c
index 590e756..60dfd17 100644 (file)
  *    supports a single RGMII PHY. This configuration also has SW control over
  *    all clock and reset signals to the HW block.
  */
-
 #include <common.h>
 #include <clk.h>
+#include <cpu_func.h>
 #include <dm.h>
 #include <errno.h>
+#include <malloc.h>
 #include <memalign.h>
 #include <miiphy.h>
 #include <net.h>
@@ -95,6 +96,7 @@ struct eqos_mac_regs {
 #define EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK                 3
 #define EQOS_MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED          0
 #define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB          2
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV           1
 
 #define EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT                 0
 #define EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK                  0xff
@@ -108,6 +110,7 @@ struct eqos_mac_regs {
 #define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT                        16
 #define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT                 8
 #define EQOS_MAC_MDIO_ADDRESS_CR_20_35                 2
+#define EQOS_MAC_MDIO_ADDRESS_CR_250_300               5
 #define EQOS_MAC_MDIO_ADDRESS_SKAP                     BIT(4)
 #define EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT                        2
 #define EQOS_MAC_MDIO_ADDRESS_GOC_READ                 3
@@ -260,6 +263,29 @@ struct eqos_desc {
 
 struct eqos_config {
        bool reg_access_always_ok;
+       int mdio_wait;
+       int swr_wait;
+       int config_mac;
+       int config_mac_mdio;
+       phy_interface_t (*interface)(struct udevice *dev);
+       struct eqos_ops *ops;
+};
+
+struct eqos_ops {
+       void (*eqos_inval_desc)(void *desc);
+       void (*eqos_flush_desc)(void *desc);
+       void (*eqos_inval_buffer)(void *buf, size_t size);
+       void (*eqos_flush_buffer)(void *buf, size_t size);
+       int (*eqos_probe_resources)(struct udevice *dev);
+       int (*eqos_remove_resources)(struct udevice *dev);
+       int (*eqos_stop_resets)(struct udevice *dev);
+       int (*eqos_start_resets)(struct udevice *dev);
+       void (*eqos_stop_clks)(struct udevice *dev);
+       int (*eqos_start_clks)(struct udevice *dev);
+       int (*eqos_calibrate_pads)(struct udevice *dev);
+       int (*eqos_disable_calibration)(struct udevice *dev);
+       int (*eqos_set_tx_clk_speed)(struct udevice *dev);
+       ulong (*eqos_get_tick_clk_rate)(struct udevice *dev);
 };
 
 struct eqos_priv {
@@ -276,9 +302,12 @@ struct eqos_priv {
        struct clk clk_rx;
        struct clk clk_ptp_ref;
        struct clk clk_tx;
+       struct clk clk_ck;
        struct clk clk_slave_bus;
        struct mii_dev *mii;
        struct phy_device *phy;
+       int phyaddr;
+       u32 max_speed;
        void *descs;
        struct eqos_desc *tx_descs;
        struct eqos_desc *rx_descs;
@@ -327,7 +356,7 @@ static void eqos_free_descs(void *descs)
 #endif
 }
 
-static void eqos_inval_desc(void *desc)
+static void eqos_inval_desc_tegra186(void *desc)
 {
 #ifndef CONFIG_SYS_NONCACHED_MEMORY
        unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1);
@@ -338,14 +367,36 @@ static void eqos_inval_desc(void *desc)
 #endif
 }
 
-static void eqos_flush_desc(void *desc)
+static void eqos_inval_desc_stm32(void *desc)
+{
+#ifndef CONFIG_SYS_NONCACHED_MEMORY
+       unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+       unsigned long end = roundup((unsigned long)desc + EQOS_DESCRIPTOR_SIZE,
+                                   ARCH_DMA_MINALIGN);
+
+       invalidate_dcache_range(start, end);
+#endif
+}
+
+static void eqos_flush_desc_tegra186(void *desc)
 {
 #ifndef CONFIG_SYS_NONCACHED_MEMORY
        flush_cache((unsigned long)desc, EQOS_DESCRIPTOR_SIZE);
 #endif
 }
 
-static void eqos_inval_buffer(void *buf, size_t size)
+static void eqos_flush_desc_stm32(void *desc)
+{
+#ifndef CONFIG_SYS_NONCACHED_MEMORY
+       unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+       unsigned long end = roundup((unsigned long)desc + EQOS_DESCRIPTOR_SIZE,
+                                   ARCH_DMA_MINALIGN);
+
+       flush_dcache_range(start, end);
+#endif
+}
+
+static void eqos_inval_buffer_tegra186(void *buf, size_t size)
 {
        unsigned long start = (unsigned long)buf & ~(ARCH_DMA_MINALIGN - 1);
        unsigned long end = ALIGN(start + size, ARCH_DMA_MINALIGN);
@@ -353,11 +404,29 @@ static void eqos_inval_buffer(void *buf, size_t size)
        invalidate_dcache_range(start, end);
 }
 
-static void eqos_flush_buffer(void *buf, size_t size)
+static void eqos_inval_buffer_stm32(void *buf, size_t size)
+{
+       unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+       unsigned long end = roundup((unsigned long)buf + size,
+                                   ARCH_DMA_MINALIGN);
+
+       invalidate_dcache_range(start, end);
+}
+
+static void eqos_flush_buffer_tegra186(void *buf, size_t size)
 {
        flush_cache((unsigned long)buf, size);
 }
 
+static void eqos_flush_buffer_stm32(void *buf, size_t size)
+{
+       unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+       unsigned long end = roundup((unsigned long)buf + size,
+                                   ARCH_DMA_MINALIGN);
+
+       flush_dcache_range(start, end);
+}
+
 static int eqos_mdio_wait_idle(struct eqos_priv *eqos)
 {
        return wait_for_bit_le32(&eqos->mac_regs->mdio_address,
@@ -386,14 +455,14 @@ static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad,
                EQOS_MAC_MDIO_ADDRESS_C45E;
        val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) |
                (mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) |
-               (EQOS_MAC_MDIO_ADDRESS_CR_20_35 <<
+               (eqos->config->config_mac_mdio <<
                 EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) |
                (EQOS_MAC_MDIO_ADDRESS_GOC_READ <<
                 EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) |
                EQOS_MAC_MDIO_ADDRESS_GB;
        writel(val, &eqos->mac_regs->mdio_address);
 
-       udelay(10);
+       udelay(eqos->config->mdio_wait);
 
        ret = eqos_mdio_wait_idle(eqos);
        if (ret) {
@@ -432,14 +501,14 @@ static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
                EQOS_MAC_MDIO_ADDRESS_C45E;
        val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) |
                (mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) |
-               (EQOS_MAC_MDIO_ADDRESS_CR_20_35 <<
+               (eqos->config->config_mac_mdio <<
                 EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) |
                (EQOS_MAC_MDIO_ADDRESS_GOC_WRITE <<
                 EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) |
                EQOS_MAC_MDIO_ADDRESS_GB;
        writel(val, &eqos->mac_regs->mdio_address);
 
-       udelay(10);
+       udelay(eqos->config->mdio_wait);
 
        ret = eqos_mdio_wait_idle(eqos);
        if (ret) {
@@ -509,7 +578,54 @@ err:
        return ret;
 }
 
-void eqos_stop_clks_tegra186(struct udevice *dev)
+static int eqos_start_clks_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+       int ret;
+
+       debug("%s(dev=%p):\n", __func__, dev);
+
+       ret = clk_enable(&eqos->clk_master_bus);
+       if (ret < 0) {
+               pr_err("clk_enable(clk_master_bus) failed: %d", ret);
+               goto err;
+       }
+
+       ret = clk_enable(&eqos->clk_rx);
+       if (ret < 0) {
+               pr_err("clk_enable(clk_rx) failed: %d", ret);
+               goto err_disable_clk_master_bus;
+       }
+
+       ret = clk_enable(&eqos->clk_tx);
+       if (ret < 0) {
+               pr_err("clk_enable(clk_tx) failed: %d", ret);
+               goto err_disable_clk_rx;
+       }
+
+       if (clk_valid(&eqos->clk_ck)) {
+               ret = clk_enable(&eqos->clk_ck);
+               if (ret < 0) {
+                       pr_err("clk_enable(clk_ck) failed: %d", ret);
+                       goto err_disable_clk_tx;
+               }
+       }
+
+       debug("%s: OK\n", __func__);
+       return 0;
+
+err_disable_clk_tx:
+       clk_disable(&eqos->clk_tx);
+err_disable_clk_rx:
+       clk_disable(&eqos->clk_rx);
+err_disable_clk_master_bus:
+       clk_disable(&eqos->clk_master_bus);
+err:
+       debug("%s: FAILED: %d\n", __func__, ret);
+       return ret;
+}
+
+static void eqos_stop_clks_tegra186(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
 
@@ -524,6 +640,21 @@ void eqos_stop_clks_tegra186(struct udevice *dev)
        debug("%s: OK\n", __func__);
 }
 
+static void eqos_stop_clks_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+
+       debug("%s(dev=%p):\n", __func__, dev);
+
+       clk_disable(&eqos->clk_tx);
+       clk_disable(&eqos->clk_rx);
+       clk_disable(&eqos->clk_master_bus);
+       if (clk_valid(&eqos->clk_ck))
+               clk_disable(&eqos->clk_ck);
+
+       debug("%s: OK\n", __func__);
+}
+
 static int eqos_start_resets_tegra186(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -563,6 +694,34 @@ static int eqos_start_resets_tegra186(struct udevice *dev)
        return 0;
 }
 
+static int eqos_start_resets_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+       int ret;
+
+       debug("%s(dev=%p):\n", __func__, dev);
+       if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) {
+               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
+               if (ret < 0) {
+                       pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d",
+                              ret);
+                       return ret;
+               }
+
+               udelay(2);
+
+               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0);
+               if (ret < 0) {
+                       pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d",
+                              ret);
+                       return ret;
+               }
+       }
+       debug("%s: OK\n", __func__);
+
+       return 0;
+}
+
 static int eqos_stop_resets_tegra186(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -573,6 +732,23 @@ static int eqos_stop_resets_tegra186(struct udevice *dev)
        return 0;
 }
 
+static int eqos_stop_resets_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+       int ret;
+
+       if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) {
+               ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1);
+               if (ret < 0) {
+                       pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d",
+                              ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int eqos_calibrate_pads_tegra186(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -632,6 +808,23 @@ static ulong eqos_get_tick_clk_rate_tegra186(struct udevice *dev)
        return clk_get_rate(&eqos->clk_slave_bus);
 }
 
+static ulong eqos_get_tick_clk_rate_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+
+       return clk_get_rate(&eqos->clk_master_bus);
+}
+
+static int eqos_calibrate_pads_stm32(struct udevice *dev)
+{
+       return 0;
+}
+
+static int eqos_disable_calibration_stm32(struct udevice *dev)
+{
+       return 0;
+}
+
 static int eqos_set_full_duplex(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -726,6 +919,11 @@ static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev)
        return 0;
 }
 
+static int eqos_set_tx_clk_speed_stm32(struct udevice *dev)
+{
+       return 0;
+}
+
 static int eqos_adjust_link(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -766,23 +964,23 @@ static int eqos_adjust_link(struct udevice *dev)
        }
 
        if (en_calibration) {
-               ret = eqos_calibrate_pads_tegra186(dev);
+               ret = eqos->config->ops->eqos_calibrate_pads(dev);
                if (ret < 0) {
-                       pr_err("eqos_calibrate_pads_tegra186() failed: %d", ret);
+                       pr_err("eqos_calibrate_pads() failed: %d",
+                              ret);
                        return ret;
                }
        } else {
-               ret = eqos_disable_calibration_tegra186(dev);
+               ret = eqos->config->ops->eqos_disable_calibration(dev);
                if (ret < 0) {
-                       pr_err("eqos_disable_calibration_tegra186() failed: %d",
-                             ret);
+                       pr_err("eqos_disable_calibration() failed: %d",
+                              ret);
                        return ret;
                }
        }
-
-       ret = eqos_set_tx_clk_speed_tegra186(dev);
+       ret = eqos->config->ops->eqos_set_tx_clk_speed(dev);
        if (ret < 0) {
-               pr_err("eqos_set_tx_clk_speed_tegra186() failed: %d", ret);
+               pr_err("eqos_set_tx_clk_speed() failed: %d", ret);
                return ret;
        }
 
@@ -846,15 +1044,15 @@ static int eqos_start(struct udevice *dev)
        eqos->tx_desc_idx = 0;
        eqos->rx_desc_idx = 0;
 
-       ret = eqos_start_clks_tegra186(dev);
+       ret = eqos->config->ops->eqos_start_clks(dev);
        if (ret < 0) {
-               pr_err("eqos_start_clks_tegra186() failed: %d", ret);
+               pr_err("eqos_start_clks() failed: %d", ret);
                goto err;
        }
 
-       ret = eqos_start_resets_tegra186(dev);
+       ret = eqos->config->ops->eqos_start_resets(dev);
        if (ret < 0) {
-               pr_err("eqos_start_resets_tegra186() failed: %d", ret);
+               pr_err("eqos_start_resets() failed: %d", ret);
                goto err_stop_clks;
        }
 
@@ -863,32 +1061,50 @@ static int eqos_start(struct udevice *dev)
        eqos->reg_access_ok = true;
 
        ret = wait_for_bit_le32(&eqos->dma_regs->mode,
-                               EQOS_DMA_MODE_SWR, false, 10, false);
+                               EQOS_DMA_MODE_SWR, false,
+                               eqos->config->swr_wait, false);
        if (ret) {
                pr_err("EQOS_DMA_MODE_SWR stuck");
                goto err_stop_resets;
        }
 
-       ret = eqos_calibrate_pads_tegra186(dev);
+       ret = eqos->config->ops->eqos_calibrate_pads(dev);
        if (ret < 0) {
-               pr_err("eqos_calibrate_pads_tegra186() failed: %d", ret);
+               pr_err("eqos_calibrate_pads() failed: %d", ret);
                goto err_stop_resets;
        }
+       rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
 
-       rate = eqos_get_tick_clk_rate_tegra186(dev);
        val = (rate / 1000000) - 1;
        writel(val, &eqos->mac_regs->us_tic_counter);
 
-       eqos->phy = phy_connect(eqos->mii, 0, dev, 0);
+       /*
+        * if PHY was already connected and configured,
+        * don't need to reconnect/reconfigure again
+        */
        if (!eqos->phy) {
-               pr_err("phy_connect() failed");
-               goto err_stop_resets;
-       }
-       ret = phy_config(eqos->phy);
-       if (ret < 0) {
-               pr_err("phy_config() failed: %d", ret);
-               goto err_shutdown_phy;
+               eqos->phy = phy_connect(eqos->mii, eqos->phyaddr, dev,
+                                       eqos->config->interface(dev));
+               if (!eqos->phy) {
+                       pr_err("phy_connect() failed");
+                       goto err_stop_resets;
+               }
+
+               if (eqos->max_speed) {
+                       ret = phy_set_supported(eqos->phy, eqos->max_speed);
+                       if (ret) {
+                               pr_err("phy_set_supported() failed: %d", ret);
+                               goto err_shutdown_phy;
+                       }
+               }
+
+               ret = phy_config(eqos->phy);
+               if (ret < 0) {
+                       pr_err("phy_config() failed: %d", ret);
+                       goto err_shutdown_phy;
+               }
        }
+
        ret = phy_startup(eqos->phy);
        if (ret < 0) {
                pr_err("phy_startup() failed: %d", ret);
@@ -993,7 +1209,7 @@ static int eqos_start(struct udevice *dev)
        clrsetbits_le32(&eqos->mac_regs->rxq_ctrl0,
                        EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK <<
                        EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT,
-                       EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB <<
+                       eqos->config->config_mac <<
                        EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
 
        /* Set TX flow control parameters */
@@ -1072,9 +1288,9 @@ static int eqos_start(struct udevice *dev)
                struct eqos_desc *rx_desc = &(eqos->rx_descs[i]);
                rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf +
                                             (i * EQOS_MAX_PACKET_SIZE));
-               rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+               rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+               eqos->config->ops->eqos_flush_desc(rx_desc);
        }
-       flush_cache((unsigned long)eqos->descs, EQOS_DESCRIPTORS_SIZE);
 
        writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress);
        writel((ulong)eqos->tx_descs, &eqos->dma_regs->ch0_txdesc_list_address);
@@ -1113,17 +1329,16 @@ static int eqos_start(struct udevice *dev)
 
 err_shutdown_phy:
        phy_shutdown(eqos->phy);
-       eqos->phy = NULL;
 err_stop_resets:
-       eqos_stop_resets_tegra186(dev);
+       eqos->config->ops->eqos_stop_resets(dev);
 err_stop_clks:
-       eqos_stop_clks_tegra186(dev);
+       eqos->config->ops->eqos_stop_clks(dev);
 err:
        pr_err("FAILED: %d", ret);
        return ret;
 }
 
-void eqos_stop(struct udevice *dev)
+static void eqos_stop(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
        int i;
@@ -1170,15 +1385,14 @@ void eqos_stop(struct udevice *dev)
 
        if (eqos->phy) {
                phy_shutdown(eqos->phy);
-               eqos->phy = NULL;
        }
-       eqos_stop_resets_tegra186(dev);
-       eqos_stop_clks_tegra186(dev);
+       eqos->config->ops->eqos_stop_resets(dev);
+       eqos->config->ops->eqos_stop_clks(dev);
 
        debug("%s: OK\n", __func__);
 }
 
-int eqos_send(struct udevice *dev, void *packet, int length)
+static int eqos_send(struct udevice *dev, void *packet, int length)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
        struct eqos_desc *tx_desc;
@@ -1188,7 +1402,7 @@ int eqos_send(struct udevice *dev, void *packet, int length)
              length);
 
        memcpy(eqos->tx_dma_buf, packet, length);
-       eqos_flush_buffer(eqos->tx_dma_buf, length);
+       eqos->config->ops->eqos_flush_buffer(eqos->tx_dma_buf, length);
 
        tx_desc = &(eqos->tx_descs[eqos->tx_desc_idx]);
        eqos->tx_desc_idx++;
@@ -1203,12 +1417,13 @@ int eqos_send(struct udevice *dev, void *packet, int length)
         */
        mb();
        tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length;
-       eqos_flush_desc(tx_desc);
+       eqos->config->ops->eqos_flush_desc(tx_desc);
 
-       writel((ulong)(tx_desc + 1), &eqos->dma_regs->ch0_txdesc_tail_pointer);
+       writel((ulong)(&(eqos->tx_descs[eqos->tx_desc_idx])),
+               &eqos->dma_regs->ch0_txdesc_tail_pointer);
 
        for (i = 0; i < 1000000; i++) {
-               eqos_inval_desc(tx_desc);
+               eqos->config->ops->eqos_inval_desc(tx_desc);
                if (!(readl(&tx_desc->des3) & EQOS_DESC3_OWN))
                        return 0;
                udelay(1);
@@ -1219,7 +1434,7 @@ int eqos_send(struct udevice *dev, void *packet, int length)
        return -ETIMEDOUT;
 }
 
-int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
+static int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
        struct eqos_desc *rx_desc;
@@ -1228,6 +1443,7 @@ int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
        debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags);
 
        rx_desc = &(eqos->rx_descs[eqos->rx_desc_idx]);
+       eqos->config->ops->eqos_inval_desc(rx_desc);
        if (rx_desc->des3 & EQOS_DESC3_OWN) {
                debug("%s: RX packet not available\n", __func__);
                return -EAGAIN;
@@ -1238,12 +1454,12 @@ int eqos_recv(struct udevice *dev, int flags, uchar **packetp)
        length = rx_desc->des3 & 0x7fff;
        debug("%s: *packetp=%p, length=%d\n", __func__, *packetp, length);
 
-       eqos_inval_buffer(*packetp, length);
+       eqos->config->ops->eqos_inval_buffer(*packetp, length);
 
        return length;
 }
 
-int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
+static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
        uchar *packet_expected;
@@ -1260,6 +1476,11 @@ int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
        }
 
        rx_desc = &(eqos->rx_descs[eqos->rx_desc_idx]);
+
+       rx_desc->des0 = 0;
+       mb();
+       eqos->config->ops->eqos_flush_desc(rx_desc);
+       eqos->config->ops->eqos_inval_buffer(packet, length);
        rx_desc->des0 = (u32)(ulong)packet;
        rx_desc->des1 = 0;
        rx_desc->des2 = 0;
@@ -1268,8 +1489,8 @@ int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
         * writes to the rest of the descriptor too.
         */
        mb();
-       rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
-       eqos_flush_desc(rx_desc);
+       rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+       eqos->config->ops->eqos_flush_desc(rx_desc);
 
        writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
 
@@ -1304,7 +1525,7 @@ static int eqos_probe_resources_core(struct udevice *dev)
                ret = -ENOMEM;
                goto err_free_descs;
        }
-       debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf);
+       debug("%s: tx_dma_buf=%p\n", __func__, eqos->tx_dma_buf);
 
        eqos->rx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_RX_BUFFER_SIZE);
        if (!eqos->rx_dma_buf) {
@@ -1312,7 +1533,7 @@ static int eqos_probe_resources_core(struct udevice *dev)
                ret = -ENOMEM;
                goto err_free_tx_dma_buf;
        }
-       debug("%s: tx_dma_buf=%p\n", __func__, eqos->tx_dma_buf);
+       debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf);
 
        eqos->rx_pkt = malloc(EQOS_MAX_PACKET_SIZE);
        if (!eqos->rx_pkt) {
@@ -1322,6 +1543,9 @@ static int eqos_probe_resources_core(struct udevice *dev)
        }
        debug("%s: rx_pkt=%p\n", __func__, eqos->rx_pkt);
 
+       eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf,
+                       EQOS_MAX_PACKET_SIZE * EQOS_DESCRIPTORS_RX);
+
        debug("%s: OK\n", __func__);
        return 0;
 
@@ -1424,6 +1648,109 @@ err_free_reset_eqos:
        return ret;
 }
 
+/* board-specific Ethernet Interface initializations. */
+__weak int board_interface_eth_init(struct udevice *dev,
+                                   phy_interface_t interface_type)
+{
+       return 0;
+}
+
+static int eqos_probe_resources_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+       int ret;
+       phy_interface_t interface;
+       struct ofnode_phandle_args phandle_args;
+
+       debug("%s(dev=%p):\n", __func__, dev);
+
+       interface = eqos->config->interface(dev);
+
+       if (interface == PHY_INTERFACE_MODE_NONE) {
+               pr_err("Invalid PHY interface\n");
+               return -EINVAL;
+       }
+
+       ret = board_interface_eth_init(dev, interface);
+       if (ret)
+               return -EINVAL;
+
+       eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0);
+
+       ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus);
+       if (ret) {
+               pr_err("clk_get_by_name(master_bus) failed: %d", ret);
+               goto err_probe;
+       }
+
+       ret = clk_get_by_name(dev, "mac-clk-rx", &eqos->clk_rx);
+       if (ret) {
+               pr_err("clk_get_by_name(rx) failed: %d", ret);
+               goto err_free_clk_master_bus;
+       }
+
+       ret = clk_get_by_name(dev, "mac-clk-tx", &eqos->clk_tx);
+       if (ret) {
+               pr_err("clk_get_by_name(tx) failed: %d", ret);
+               goto err_free_clk_rx;
+       }
+
+       /*  Get ETH_CLK clocks (optional) */
+       ret = clk_get_by_name(dev, "eth-ck", &eqos->clk_ck);
+       if (ret)
+               pr_warn("No phy clock provided %d", ret);
+
+       eqos->phyaddr = -1;
+       ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+                                        &phandle_args);
+       if (!ret) {
+               /* search "reset-gpios" in phy node */
+               ret = gpio_request_by_name_nodev(phandle_args.node,
+                                                "reset-gpios", 0,
+                                                &eqos->phy_reset_gpio,
+                                                GPIOD_IS_OUT |
+                                                GPIOD_IS_OUT_ACTIVE);
+               if (ret)
+                       pr_warn("gpio_request_by_name(phy reset) not provided %d",
+                               ret);
+
+               eqos->phyaddr = ofnode_read_u32_default(phandle_args.node,
+                                                       "reg", -1);
+       }
+
+       debug("%s: OK\n", __func__);
+       return 0;
+
+err_free_clk_rx:
+       clk_free(&eqos->clk_rx);
+err_free_clk_master_bus:
+       clk_free(&eqos->clk_master_bus);
+err_probe:
+
+       debug("%s: returns %d\n", __func__, ret);
+       return ret;
+}
+
+static phy_interface_t eqos_get_interface_stm32(struct udevice *dev)
+{
+       const char *phy_mode;
+       phy_interface_t interface = PHY_INTERFACE_MODE_NONE;
+
+       debug("%s(dev=%p):\n", __func__, dev);
+
+       phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode",
+                              NULL);
+       if (phy_mode)
+               interface = phy_get_interface_by_name(phy_mode);
+
+       return interface;
+}
+
+static phy_interface_t eqos_get_interface_tegra186(struct udevice *dev)
+{
+       return PHY_INTERFACE_MODE_MII;
+}
+
 static int eqos_remove_resources_tegra186(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1442,6 +1769,25 @@ static int eqos_remove_resources_tegra186(struct udevice *dev)
        return 0;
 }
 
+static int eqos_remove_resources_stm32(struct udevice *dev)
+{
+       struct eqos_priv *eqos = dev_get_priv(dev);
+
+       debug("%s(dev=%p):\n", __func__, dev);
+
+       clk_free(&eqos->clk_tx);
+       clk_free(&eqos->clk_rx);
+       clk_free(&eqos->clk_master_bus);
+       if (clk_valid(&eqos->clk_ck))
+               clk_free(&eqos->clk_ck);
+
+       if (dm_gpio_is_valid(&eqos->phy_reset_gpio))
+               dm_gpio_free(dev, &eqos->phy_reset_gpio);
+
+       debug("%s: OK\n", __func__);
+       return 0;
+}
+
 static int eqos_probe(struct udevice *dev)
 {
        struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1468,15 +1814,16 @@ static int eqos_probe(struct udevice *dev)
                return ret;
        }
 
-       ret = eqos_probe_resources_tegra186(dev);
+       ret = eqos->config->ops->eqos_probe_resources(dev);
        if (ret < 0) {
-               pr_err("eqos_probe_resources_tegra186() failed: %d", ret);
+               pr_err("eqos_probe_resources() failed: %d", ret);
                goto err_remove_resources_core;
        }
 
        eqos->mii = mdio_alloc();
        if (!eqos->mii) {
                pr_err("mdio_alloc() failed");
+               ret = -ENOMEM;
                goto err_remove_resources_tegra;
        }
        eqos->mii->read = eqos_mdio_read;
@@ -1496,7 +1843,7 @@ static int eqos_probe(struct udevice *dev)
 err_free_mdio:
        mdio_free(eqos->mii);
 err_remove_resources_tegra:
-       eqos_remove_resources_tegra186(dev);
+       eqos->config->ops->eqos_remove_resources(dev);
 err_remove_resources_core:
        eqos_remove_resources_core(dev);
 
@@ -1512,7 +1859,8 @@ static int eqos_remove(struct udevice *dev)
 
        mdio_unregister(eqos->mii);
        mdio_free(eqos->mii);
-       eqos_remove_resources_tegra186(dev);
+       eqos->config->ops->eqos_remove_resources(dev);
+
        eqos_probe_resources_core(dev);
 
        debug("%s: OK\n", __func__);
@@ -1528,8 +1876,58 @@ static const struct eth_ops eqos_ops = {
        .write_hwaddr = eqos_write_hwaddr,
 };
 
+static struct eqos_ops eqos_tegra186_ops = {
+       .eqos_inval_desc = eqos_inval_desc_tegra186,
+       .eqos_flush_desc = eqos_flush_desc_tegra186,
+       .eqos_inval_buffer = eqos_inval_buffer_tegra186,
+       .eqos_flush_buffer = eqos_flush_buffer_tegra186,
+       .eqos_probe_resources = eqos_probe_resources_tegra186,
+       .eqos_remove_resources = eqos_remove_resources_tegra186,
+       .eqos_stop_resets = eqos_stop_resets_tegra186,
+       .eqos_start_resets = eqos_start_resets_tegra186,
+       .eqos_stop_clks = eqos_stop_clks_tegra186,
+       .eqos_start_clks = eqos_start_clks_tegra186,
+       .eqos_calibrate_pads = eqos_calibrate_pads_tegra186,
+       .eqos_disable_calibration = eqos_disable_calibration_tegra186,
+       .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_tegra186,
+       .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_tegra186
+};
+
 static const struct eqos_config eqos_tegra186_config = {
        .reg_access_always_ok = false,
+       .mdio_wait = 10,
+       .swr_wait = 10,
+       .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+       .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_20_35,
+       .interface = eqos_get_interface_tegra186,
+       .ops = &eqos_tegra186_ops
+};
+
+static struct eqos_ops eqos_stm32_ops = {
+       .eqos_inval_desc = eqos_inval_desc_stm32,
+       .eqos_flush_desc = eqos_flush_desc_stm32,
+       .eqos_inval_buffer = eqos_inval_buffer_stm32,
+       .eqos_flush_buffer = eqos_flush_buffer_stm32,
+       .eqos_probe_resources = eqos_probe_resources_stm32,
+       .eqos_remove_resources = eqos_remove_resources_stm32,
+       .eqos_stop_resets = eqos_stop_resets_stm32,
+       .eqos_start_resets = eqos_start_resets_stm32,
+       .eqos_stop_clks = eqos_stop_clks_stm32,
+       .eqos_start_clks = eqos_start_clks_stm32,
+       .eqos_calibrate_pads = eqos_calibrate_pads_stm32,
+       .eqos_disable_calibration = eqos_disable_calibration_stm32,
+       .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_stm32,
+       .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_stm32
+};
+
+static const struct eqos_config eqos_stm32_config = {
+       .reg_access_always_ok = false,
+       .mdio_wait = 10000,
+       .swr_wait = 50,
+       .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
+       .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+       .interface = eqos_get_interface_stm32,
+       .ops = &eqos_stm32_ops
 };
 
 static const struct udevice_id eqos_ids[] = {
@@ -1537,6 +1935,11 @@ static const struct udevice_id eqos_ids[] = {
                .compatible = "nvidia,tegra186-eqos",
                .data = (ulong)&eqos_tegra186_config
        },
+       {
+               .compatible = "snps,dwmac-4.20a",
+               .data = (ulong)&eqos_stm32_config
+       },
+
        { }
 };