net: stmmac: Enable LicheePi 4A ethernet drivers 91/310991/3
authorMichal Wilczynski <m.wilczynski@samsung.com>
Fri, 10 May 2024 11:16:07 +0000 (13:16 +0200)
committerMichal Wilczynski <m.wilczynski@samsung.com>
Mon, 13 May 2024 12:13:42 +0000 (14:13 +0200)
Ported from vendor kernel [1].

[1] https://gitee.com/thead-yocto/kernel.git

Change-Id: I81aa81094fa68fe5a206035040aac2ff3ed2fdbb
Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/dwmac-light.c [new file with mode: 0644]

index 25f2d42de406d698f00dbafd6d814ee14cd24829..564dfd208e4289fae0e341aa09a16e80b77a9405 100644 (file)
@@ -239,6 +239,16 @@ config DWMAC_INTEL_PLAT
          the stmmac device driver. This driver is used for the Intel Keem Bay
          SoC.
 
+config DWMAC_LIGHT
+       tristate "Thead Light DWMAC support"
+       depends on OF && (ARCH_THEAD || COMPILE_TEST)
+       help
+         Support for ethernet controller on Thead SOCs.
+
+         This selects Thead SoC glue layer support for the stmmac
+         device driver. This driver is used for the Thead ice and light series
+         SOCs GMAC ethernet controller.
+
 config DWMAC_TEGRA
        tristate "NVIDIA Tegra MGBE support"
        depends on ARCH_TEGRA || COMPILE_TEST
index 5b57aee19267ff2dddc2e38fe61ed5c08f66c1e0..e36e85173720b006a1d11802541788821f48ee00 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_DWMAC_SUNXI)     += dwmac-sunxi.o
 obj-$(CONFIG_DWMAC_SUN8I)      += dwmac-sun8i.o
 obj-$(CONFIG_DWMAC_DWC_QOS_ETH)        += dwmac-dwc-qos-eth.o
 obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o
+obj-$(CONFIG_DWMAC_LIGHT)      += dwmac-light.o
 obj-$(CONFIG_DWMAC_GENERIC)    += dwmac-generic.o
 obj-$(CONFIG_DWMAC_IMX8)       += dwmac-imx.o
 obj-$(CONFIG_DWMAC_TEGRA)      += dwmac-tegra.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-light.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-light.c
new file mode 100644 (file)
index 0000000..14f51b5
--- /dev/null
@@ -0,0 +1,993 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "stmmac_platform.h"
+
+/* clock registers */
+#define GMAC_CLK_CFG0  0x00
+#define GMAC_CLK_CFG1  0x04
+#define GMAC_CLK_CFG2  0x08
+#define GMAC_CLK_CFG3  0x0C
+#define GMAC_CLK_CFG4  0x10
+#define GMAC_CLK_CFG5  0x14
+#define GMAC_CLK_CFG6  0x18
+
+/* phy interface */
+#define DWMAC_PHYIF_MII_GMII   0
+#define DWMAC_PHYIF_RGMII      1
+#define DWMAC_PHYIF_RMII       4
+/* register bit fields, bit[3]: reserved, bit[2:0]: phy interface */
+#define DWMAC_PHYIF_MASK       0x7
+#define DWMAC_PHYIF_BIT_WIDTH  4
+
+/* TXCLK direction, 1:input, 0:output */
+#define TXCLK_DIR_OUTPUT       0
+#define TXCLK_DIR_INPUT                1
+
+#define GMAC_CLK_PLLOUT_250M   250000000
+#define GMAC_GMII_RGMII_RATE   125000000
+#define GMAC_MII_RATE          25000000
+/* clock divider for speed */
+#define GMAC_CLKDIV_125M       (GMAC_CLK_PLLOUT_250M / GMAC_GMII_RGMII_RATE)
+#define GMAC_CLKDIV_25M                (GMAC_CLK_PLLOUT_250M / GMAC_MII_RATE)
+#define GMAC_PTP_CLK_RATE      50000000 //50MHz
+
+struct thead_dwmac_ops {
+       void (*set_clk_source)(struct plat_stmmacenet_data *plat_dat);
+       void (*set_clk_pll)(struct plat_stmmacenet_data *plat_dat);
+       void (*set_clk_div)(struct plat_stmmacenet_data *plat_dat, unsigned int speed);
+       void (*enable_clk)(struct plat_stmmacenet_data *plat_dat);
+       void (*set_ptp_div)(struct plat_stmmacenet_data *plat_dat,unsigned int ptp_clk_rate);
+};
+
+struct thead_dwmac_priv_data {
+       int id;
+       struct device *dev;
+       void __iomem *phy_if_reg;
+       void __iomem *txclk_dir_reg;
+       void __iomem *gmac_clk_reg;
+       phy_interface_t interface;
+       struct clk *gmac_pll_clk;
+       unsigned long gmac_pll_clk_freq;
+       struct clk *gmac_axi_aclk;
+       struct clk *gmac_axi_pclk;
+       const struct thead_dwmac_ops *ops;
+       struct plat_stmmacenet_data *plat_dat;
+};
+
+#define  pm_debug dev_dbg      // for suspend/resume interface debug info show,replace to dev_info
+
+/* set GMAC PHY interface, 0:MII/GMII, 1:RGMII, 4:RMII */
+static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       void __iomem *phy_if_reg = thead_plat_dat->phy_if_reg;
+       phy_interface_t interface = thead_plat_dat->interface;
+       struct device *dev = thead_plat_dat->dev;
+       //int devid = thead_plat_dat->id;
+       unsigned int phyif = PHY_INTERFACE_MODE_MII;
+       uint32_t reg;
+
+       if (phy_if_reg == NULL)
+               return;
+
+       switch (interface)
+       {
+       case PHY_INTERFACE_MODE_MII:
+       case PHY_INTERFACE_MODE_GMII:
+               phyif = DWMAC_PHYIF_MII_GMII;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               phyif = DWMAC_PHYIF_RGMII;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               phyif = DWMAC_PHYIF_RMII;
+               break;
+       default:
+               dev_err(dev, "phy interface %d not supported\n", interface);
+               return;
+       };
+
+       reg = readl(phy_if_reg);
+       //This reg defined bit not related to devid
+       reg &= ~(DWMAC_PHYIF_MASK );
+       reg |= (phyif & DWMAC_PHYIF_MASK) ;
+       dev_info(dev,"set phy_if_reg val 0x%x \n",reg);
+       writel(reg, phy_if_reg);
+}
+
+/*
+ * set GMAC TXCLK direction
+ *     MII        : TXCLK is input
+ *     GMII/RGMII : TXCLK is output
+ */
+static void thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       void __iomem *txclk_dir_reg = thead_plat_dat->txclk_dir_reg;
+       phy_interface_t interface = thead_plat_dat->interface;
+       struct device *dev = thead_plat_dat->dev;
+       unsigned int txclk_dir = TXCLK_DIR_INPUT;
+
+       if (txclk_dir_reg == NULL)
+               return;
+
+       switch (interface)
+       {
+       case PHY_INTERFACE_MODE_MII:
+       case PHY_INTERFACE_MODE_RMII:
+               txclk_dir = TXCLK_DIR_INPUT;
+               break;
+       case PHY_INTERFACE_MODE_GMII:
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               txclk_dir = TXCLK_DIR_OUTPUT;
+               break;
+       default:
+               dev_err(dev, "phy interface %d not supported\n", interface);
+               return;
+       };
+
+       writel(txclk_dir, txclk_dir_reg);
+}
+
+static void thead_dwmac_ice_set_clk_source(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       phy_interface_t interface = thead_plat_dat->interface;
+       struct device *dev = thead_plat_dat->dev;
+       uint32_t reg;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       reg = readl(gmac_clk_reg + GMAC_CLK_CFG0);
+
+       /* RX clock source */
+       reg |= BIT(7);  /* gmac_rx_clk_sel: extern pin */
+
+       /* TX clock source */
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               reg |= BIT(1);  /* gmac_tx_clk_sel: extern pin */
+               reg &= ~BIT(2); /* gmac_tx_clk_gbit_sel: u_tx_clk_mux */
+       } else if (interface == PHY_INTERFACE_MODE_GMII) {
+               reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */
+               reg |= BIT(2);  /* gmac_tx_clk_gbit_sel: GMAC PLL */
+       } else if (interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+               reg &= ~BIT(5); /* gmac_tx_clk_out_sel: GMAC PLL */
+               reg |= BIT(2);  /* gmac_tx_clk_gbit_sel: GMAC PLL */
+       } else {
+               dev_err(dev, "phy interface %d not supported\n", interface);
+               return;
+       }
+
+       writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
+}
+
+
+/* set clock source */
+static void thead_dwmac_set_clock_delay(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       struct device *dev = thead_plat_dat->dev;
+       unsigned int delay;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       if (of_property_read_u32(dev->of_node, "rx-clk-delay",
+                                &delay) == 0) {
+               /* RX clk delay */
+               writel(delay, gmac_clk_reg + GMAC_CLK_CFG1);
+               pr_info("RX clk delay: 0x%X\n", delay);
+       }
+
+       if (of_property_read_u32(dev->of_node, "tx-clk-delay",
+                                &delay) == 0) {
+               /* TX clk delay */
+               writel(delay, gmac_clk_reg + GMAC_CLK_CFG2);
+               pr_info("TX clk delay: 0x%X\n", delay);
+       }
+}
+
+/* set gmac pll divider (u_pll_clk_div) to get 250MHz clock */
+static void thead_dwmac_ice_set_pll(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       phy_interface_t interface = thead_plat_dat->interface;
+       unsigned int src_freq = thead_plat_dat->gmac_pll_clk_freq;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned int reg;
+       unsigned int div;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               /* for mii, no internal pll is used */
+               return;
+       } else if (interface == PHY_INTERFACE_MODE_GMII
+               || interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+
+               /* check clock */
+               if ((src_freq == 0) || (src_freq % GMAC_CLK_PLLOUT_250M != 0)) {
+                       pr_err("error! invalid gmac pll freq %d\n", src_freq);
+                       return;
+               }
+               div = src_freq / GMAC_CLK_PLLOUT_250M;
+
+               /* disable pll_clk_div */
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG3);
+               reg &= ~BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
+
+               /* modify divider */
+               writel(div, gmac_clk_reg + GMAC_CLK_CFG3);
+
+               /* enable pll_clk_div */
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG3);
+               reg |= BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
+       } else {
+               pr_err("phy interface %d not supported\n", interface);
+               return;
+       }
+}
+
+/* set gmac speed */
+static void thead_dwmac_ice_set_clk_div(struct plat_stmmacenet_data *plat_dat, unsigned int speed)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       phy_interface_t interface = thead_plat_dat->interface;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned int reg;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               /* For MII, no internal PLL is used */
+               return;
+       } else if (interface == PHY_INTERFACE_MODE_GMII
+               || interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+
+               /* disable gtx_clk_div */
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG4);
+               reg &= ~BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG4);
+
+               /*
+                * modify divider
+                */
+               /* gtx_clk_div */
+               if (speed == SPEED_1000) {
+                       writel(GMAC_CLKDIV_125M, gmac_clk_reg + GMAC_CLK_CFG4);
+               } else if (speed == SPEED_100) {
+                       writel(GMAC_CLKDIV_25M, gmac_clk_reg + GMAC_CLK_CFG4);
+               } else {
+                       writel(GMAC_CLKDIV_25M / 10, gmac_clk_reg + GMAC_CLK_CFG4);
+               }
+
+               /* enable gtx_clk_div */
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG4);
+               reg |= BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG4);
+       } else {
+               pr_err("phy interface %d not supported\n", interface);
+               return;
+       }
+}
+
+static void thead_dwmac_light_set_clk_div(struct plat_stmmacenet_data *plat_dat, unsigned int speed)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       phy_interface_t interface = thead_plat_dat->interface;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned long src_freq = thead_plat_dat->gmac_pll_clk_freq;
+       unsigned int div;
+       unsigned int reg;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               /* For MII, no internal PLL is used */
+               return;
+       } else if (interface == PHY_INTERFACE_MODE_GMII
+               || interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+
+               /* check clock */
+               if ((src_freq == 0) || (src_freq % GMAC_GMII_RGMII_RATE != 0 ||
+                   src_freq % GMAC_MII_RATE != 0)) {
+                       pr_err("error! invalid gmac pll freq %lu\n", src_freq);
+                       return;
+               }
+
+               /* disable gtx_clk_div */
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG3);
+               reg &= ~BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
+
+               /*
+                * modify divider
+                */
+               /* gtx_clk_div */
+               if (speed == SPEED_1000)
+                       div = src_freq / GMAC_GMII_RGMII_RATE;
+               else if (speed == SPEED_100)
+                       div = src_freq / GMAC_MII_RATE;
+               else
+                       div = (src_freq * 10) / GMAC_MII_RATE;
+               writel(div, gmac_clk_reg + GMAC_CLK_CFG3);
+
+               /* enable gtx_clk_div */
+               reg = div | BIT(31);
+               writel(reg, gmac_clk_reg + GMAC_CLK_CFG3);
+       } else {
+               pr_err("phy interface %d not supported\n", interface);
+               return;
+       }
+}
+
+static void thead_dwmac_light_set_ptp_clk_div(struct plat_stmmacenet_data *plat_dat,unsigned int ptp_clk_rate)
+{
+       unsigned int div;
+       unsigned int reg;
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned long src_freq = thead_plat_dat->gmac_pll_clk_freq;
+
+       if (gmac_clk_reg == NULL)
+               return;
+       if(!ptp_clk_rate || !src_freq)
+       {
+               pr_warn("invalid gmac pll freq %lu or ptp_clk_rate %d\n", src_freq,ptp_clk_rate);
+               return;
+       }
+       /* disable clk_div */
+       reg = readl(gmac_clk_reg + GMAC_CLK_CFG5);
+       reg &= ~BIT(31);
+       writel(reg, gmac_clk_reg + GMAC_CLK_CFG5);
+
+       div = src_freq / ptp_clk_rate;
+       writel(div,gmac_clk_reg + GMAC_CLK_CFG5);
+
+       /* enable clk_div */
+       reg = div | BIT(31);
+       writel(reg, gmac_clk_reg + GMAC_CLK_CFG5);
+       return ;
+}
+
+/* enable gmac clock */
+static void thead_dwmac_ice_enable_clk(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       phy_interface_t interface = thead_plat_dat->interface;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       struct device *dev = thead_plat_dat->dev;
+       unsigned int reg;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       reg = readl(gmac_clk_reg + GMAC_CLK_CFG0);
+
+       /* enable gmac_hclk */
+       reg |= BIT(14);
+
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               reg |= BIT(8);  /* enable gmac_rx_clk */
+               reg |= BIT(3);  /* enable gmac_tx_clk */
+       } else if (interface == PHY_INTERFACE_MODE_GMII) {
+               reg |= BIT(8);  /* enable gmac_rx_clk */
+               reg |= BIT(3);  /* enable gmac_tx_clk */
+               reg |= BIT(6);  /* enable gmac_tx_clk_out */
+       } else if (interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+               reg |= BIT(8);  /* enable gmac_rx_clk */
+               reg |= BIT(3);  /* enable gmac_tx_clk */
+               reg |= BIT(6);  /* enable gmac_tx_clk_out */
+               reg |= BIT(9);  /* enable gmac_rx_clk_n */
+               reg |= BIT(4);  /* enable gmac_tx_clk_n */
+       } else {
+               dev_err(dev, "phy interface %d not supported\n", interface);
+               return;
+       }
+
+       writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
+}
+
+static void thead_dwmac_light_enable_clk(struct plat_stmmacenet_data *plat_dat)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = plat_dat->bsp_priv;
+       phy_interface_t interface = thead_plat_dat->interface;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       struct device *dev = thead_plat_dat->dev;
+       unsigned int reg = 0;
+
+       if (gmac_clk_reg == NULL)
+               return;
+
+       /* use internal pll */
+       writel(BIT(0), gmac_clk_reg + GMAC_CLK_CFG6);
+
+       if (interface == PHY_INTERFACE_MODE_MII) {
+               reg |= BIT(4);  /* enable gmac_rx_clk */
+               reg |= BIT(1);  /* enable gmac_tx_clk */
+       } else if (interface == PHY_INTERFACE_MODE_GMII) {
+               reg |= BIT(4);  /* enable gmac_rx_clk */
+               reg |= BIT(1);  /* enable gmac_tx_clk */
+               reg |= BIT(4);  /* enable gmac_tx_clk_out */
+       } else if (interface == PHY_INTERFACE_MODE_RGMII
+               || interface == PHY_INTERFACE_MODE_RGMII_ID
+               || interface == PHY_INTERFACE_MODE_RGMII_RXID
+               || interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+               reg |= BIT(4);  /* enable gmac_rx_clk */
+               reg |= BIT(1);  /* enable gmac_tx_clk */
+               reg |= BIT(3);  /* enable gmac_tx_clk_out */
+               reg |= BIT(5);  /* enable gmac_rx_clk_n */
+               reg |= BIT(2);  /* enable gmac_tx_clk_n */
+       } else {
+               dev_err(dev, "phy interface %d not supported\n", interface);
+               return;
+       }
+
+       reg |= BIT(6); /* ephy ref clk */
+       writel(reg, gmac_clk_reg + GMAC_CLK_CFG0);
+}
+static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       //struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *res;
+       void __iomem *ptr;
+       //struct clk *clktmp;
+       //int ret;
+
+       thead_plat_dat->id = of_alias_get_id(np, "ethernet");
+       if (thead_plat_dat->id < 0) {
+               thead_plat_dat->id = 0;
+       }
+       dev_info(dev, "id: %d\n", thead_plat_dat->id);
+
+       thead_plat_dat->interface = device_get_phy_mode(&pdev->dev);
+       if (thead_plat_dat->interface < 0)
+               return -ENODEV;
+       dev_info(dev, "phy interface: %d\n", thead_plat_dat->interface);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_if_reg");
+       if ((res != NULL) && (resource_type(res) == IORESOURCE_MEM)) {
+               ptr = devm_ioremap(dev, res->start, resource_size(res));
+               if (!ptr) {
+                       dev_err(dev, "phy interface register not exist, skipped it\n");
+               } else {
+                       thead_plat_dat->phy_if_reg = ptr;
+               }
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txclk_dir_reg");
+       ptr = devm_ioremap_resource(dev, res);
+       if (IS_ERR(ptr)) {
+               dev_err(dev, "txclk_dir register not exist, skipped it\n");
+       } else {
+               thead_plat_dat->txclk_dir_reg = ptr;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_mgr_reg");
+       ptr = devm_ioremap_resource(dev, res);
+       if (IS_ERR(ptr)) {
+               dev_err(dev, "gmac_clk register not exist, skipped it\n");
+       } else {
+               thead_plat_dat->gmac_clk_reg = ptr;
+       }
+#if 0
+       /* get gmac pll clk */
+       clktmp = devm_clk_get(dev, "gmac_pll_clk");
+       if (IS_ERR(clktmp)) {
+               dev_err(dev, "gmac_pll_clk not exist, skipped it\n");
+       } else {
+               thead_plat_dat->gmac_pll_clk = clktmp;
+
+               ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk);
+               if (ret) {
+                       dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n");
+                       return -1;
+               }
+
+               thead_plat_dat->gmac_pll_clk_freq =
+                               clk_get_rate(thead_plat_dat->gmac_pll_clk);
+       }
+
+
+       thead_dwmac_set_phy_if(plat_dat);
+       thead_dwmac_set_txclk_dir(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_source)
+               thead_plat_dat->ops->set_clk_source(plat_dat);
+
+       thead_dwmac_set_clock_delay(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_pll)
+               thead_plat_dat->ops->set_clk_pll(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_div)
+               thead_plat_dat->ops->set_clk_div(plat_dat, SPEED_1000);
+
+       if (thead_plat_dat->ops->enable_clk)
+               thead_plat_dat->ops->enable_clk(plat_dat);
+#endif
+       return 0;
+}
+
+static void thead_dwmac_fix_speed(void *bsp_priv, unsigned int speed, unsigned int mode)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
+
+       if (thead_plat_dat->ops->set_clk_div)
+               thead_plat_dat->ops->set_clk_div(plat_dat, speed);
+}
+
+/**
+ * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins
+ * @mcast_bins: Multicast filtering bins
+ * Description:
+ * this function validates the number of Multicast filtering bins specified
+ * by the configuration through the device tree. The Synopsys GMAC supports
+ * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC
+ * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds
+ * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is
+ * invalid and will cause the filtering algorithm to use Multicast
+ * promiscuous mode.
+ */
+static int dwmac1000_validate_mcast_bins(int mcast_bins)
+{
+       int x = mcast_bins;
+
+       switch (x) {
+       case HASH_TABLE_SIZE:
+       case 128:
+       case 256:
+               break;
+       default:
+               x = 0;
+               pr_info("Hash table entries set to unexpected value %d",
+                       mcast_bins);
+               break;
+       }
+       return x;
+}
+
+/**
+ * dwmac1000_validate_ucast_entries - validate the Unicast address entries
+ * @ucast_entries: number of Unicast address entries
+ * Description:
+ * This function validates the number of Unicast address entries supported
+ * by a particular Synopsys 10/100/1000 controller. The Synopsys controller
+ * supports 1..32, 64, or 128 Unicast filter entries for it's Unicast filter
+ * logic. This function validates a valid, supported configuration is
+ * selected, and defaults to 1 Unicast address if an unsupported
+ * configuration is selected.
+ */
+static int dwmac1000_validate_ucast_entries(int ucast_entries)
+{
+       int x = ucast_entries;
+
+       switch (x) {
+       case 1 ... 32:
+       case 64:
+       case 128:
+               break;
+       default:
+               x = 1;
+               pr_info("Unicast table entries set to unexpected value %d\n",
+                       ucast_entries);
+               break;
+       }
+       return x;
+}
+static void __maybe_unused thead_dwmac_dump_priv_reg(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned int reg = 0;
+       int i;
+       dev_info(dev,"dump gmac_clk_reg %p\n",gmac_clk_reg);
+       if(gmac_clk_reg == NULL)
+               return ;
+       for(i=0; i< 0x1c; i+=4)
+       {
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG0+i);
+               pr_info("%08x ",reg);
+       }
+       pr_info("\n");
+       reg = readl(thead_plat_dat->phy_if_reg);
+       pr_info("phy_if_reg %08x ",reg);
+       reg = readl(thead_plat_dat->txclk_dir_reg);
+       pr_info("txclk_dir_reg %08x ",reg);
+}
+
+int thead_dwmac_clk_enable(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk);
+       if (ret) {
+               dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n");
+               return -1;
+       }
+       ret = clk_prepare_enable(thead_plat_dat->gmac_axi_aclk);
+       if (ret) {
+               clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+               dev_err(dev, "Failed to enable clk 'gmac_axi_aclk'\n");
+               return -1;
+       }
+       ret = clk_prepare_enable(thead_plat_dat->gmac_axi_pclk);
+       if (ret) {
+               clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
+               clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+               dev_err(dev, "Failed to enable clk 'gmac_axi_pclk'\n");
+               return -1;
+       }
+
+       return ret;
+}
+
+int thead_dwmac_clk_init(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
+       int ret = 0;
+       pm_debug(dev,"enter %s()\n",__func__);
+
+       thead_dwmac_set_phy_if(plat_dat);
+       thead_dwmac_set_txclk_dir(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_source)
+               thead_plat_dat->ops->set_clk_source(plat_dat);
+
+       thead_dwmac_set_clock_delay(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_pll)
+               thead_plat_dat->ops->set_clk_pll(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_div)
+               thead_plat_dat->ops->set_clk_div(plat_dat, SPEED_1000);
+
+       if (thead_plat_dat->ops->enable_clk)
+               thead_plat_dat->ops->enable_clk(plat_dat);
+
+       if (thead_plat_dat->ops->set_ptp_div)
+               thead_plat_dat->ops->set_ptp_div(plat_dat,plat_dat->clk_ptp_rate);
+       //thead_dwmac_dump_priv_reg(pdev,bsp_priv);
+       return ret;
+}
+void thead_dwmac_clk_disable(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       pm_debug(dev,"enter %s()\n",__func__);
+       //thead_dwmac_dump_priv_reg(pdev,bsp_priv);
+
+       clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+       clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
+       clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+
+       return ;
+}
+
+static int thead_dwmac_probe(struct platform_device *pdev)
+{
+       struct plat_stmmacenet_data *plat_dat;
+       struct stmmac_resources stmmac_res;
+       struct thead_dwmac_priv_data *thead_plat_dat;
+       struct device *dev = &pdev->dev;
+       const struct thead_dwmac_ops *data;
+       struct device_node *np = pdev->dev.of_node;
+       int ret;
+
+       thead_plat_dat = devm_kzalloc(dev, sizeof(*thead_plat_dat), GFP_KERNEL);
+       if (thead_plat_dat == NULL) {
+               dev_err(&pdev->dev, "allocate memory failed\n");
+               return -ENOMEM;
+       }
+
+       ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+       if (ret)
+               return ret;
+
+       if (pdev->dev.of_node) {
+               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);
+               }
+
+               data = of_device_get_match_data(&pdev->dev);
+               if (!data) {
+                       dev_err(&pdev->dev, "failed to get match data\n");
+                       ret = -EINVAL;
+                       return ret;
+               }
+
+               thead_plat_dat->ops = data;
+       } else {
+               plat_dat = dev_get_platdata(&pdev->dev);
+               if (!plat_dat) {
+                       dev_err(&pdev->dev, "no platform data provided\n");
+                       return  -EINVAL;
+               }
+
+               /* Set default value for multicast hash bins */
+               plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
+
+               /* Set default value for unicast filter entries */
+               plat_dat->unicast_filter_entries = 1;
+       }
+
+
+
+       /* populate bsp private data */
+       thead_plat_dat->dev = &pdev->dev;
+       plat_dat->bsp_priv = thead_plat_dat;
+       plat_dat->fix_mac_speed = thead_dwmac_fix_speed;
+       plat_dat->init = thead_dwmac_clk_init;
+       of_property_read_u32(np, "max-frame-size", &plat_dat->maxmtu);
+       of_property_read_u32(np, "snps,multicast-filter-bins",
+                            &plat_dat->multicast_filter_bins);
+       of_property_read_u32(np, "snps,perfect-filter-entries",
+                            &plat_dat->unicast_filter_entries);
+       plat_dat->unicast_filter_entries = dwmac1000_validate_ucast_entries(
+                                      plat_dat->unicast_filter_entries);
+       plat_dat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
+                                     plat_dat->multicast_filter_bins);
+       plat_dat->has_gmac = 1;
+       plat_dat->pmt = 1;
+       thead_plat_dat->plat_dat = plat_dat;
+
+       /* get gmac pll clk */
+       thead_plat_dat->gmac_pll_clk = devm_clk_get(dev, "gmac_pll_clk");
+       if (IS_ERR(thead_plat_dat->gmac_pll_clk)) {
+               dev_err(dev, "gmac_pll_clk not exist, dts error\n");
+               goto err_remove_config_dt;
+       }
+
+       thead_plat_dat->gmac_axi_aclk = devm_clk_get(dev, "axi_aclk");
+       if (IS_ERR(thead_plat_dat->gmac_axi_aclk)) {
+               dev_err(dev, "gmac axi_aclk not exist, skipped it\n");
+       }
+       thead_plat_dat->gmac_axi_pclk = devm_clk_get(dev, "axi_pclk");
+       if (IS_ERR(thead_plat_dat->gmac_axi_pclk)) {
+               dev_err(dev, "gmac axi_pclk not exist, skipped it\n");
+       }
+
+
+       thead_plat_dat->gmac_pll_clk_freq =
+                       clk_get_rate(thead_plat_dat->gmac_pll_clk);
+       dev_info(dev,"get_rate gmac_pll_clk_freq %ld \n",thead_plat_dat->gmac_pll_clk_freq);
+
+       ret = thead_dwmac_init(pdev, plat_dat->bsp_priv);
+       if (ret)
+               goto err_remove_config_dt;
+
+       plat_dat->clk_ptp_rate = GMAC_PTP_CLK_RATE;
+
+       ret = thead_dwmac_clk_enable(pdev, plat_dat->bsp_priv);
+       if (ret)
+                       goto err_remove_config_dt;
+
+       /* Custom initialisation (if needed) -- init clks*/
+       if (plat_dat->init) {
+               ret = plat_dat->init(pdev, plat_dat->bsp_priv);
+               if (ret)
+                       goto err_exit;
+       }
+
+       ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+       if (ret)
+               goto err_exit;
+
+       return 0;
+
+err_exit:
+       if (plat_dat->exit)
+               plat_dat->exit(pdev, plat_dat->bsp_priv);
+       thead_dwmac_clk_disable(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+       if (pdev->dev.of_node)
+               stmmac_remove_config_dt(pdev, plat_dat);
+
+       return ret;
+}
+
+/**
+ * stmmac_pltfr_suspend
+ * @dev: device pointer
+ * Description: this function is invoked when suspend the driver and it direcly
+ * call the main suspend function and then, if required, on some platform, it
+ * can call an exit helper.
+ */
+static int __maybe_unused thead_dwmac_suspend(struct device *dev)
+{
+       int ret;
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = stmmac_suspend(dev);
+       if (priv->plat->exit)
+               priv->plat->exit(pdev, priv->plat->bsp_priv);
+
+       return ret;
+}
+
+/**
+ * thead_dwmac_resume
+ * @dev: device pointer
+ * Description: this function is invoked when resume the driver before calling
+ * the main resume function, on some platforms, it can call own init helper
+ * if required.
+ */
+static int __maybe_unused thead_dwmac_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+
+       if (priv->plat->init)
+               priv->plat->init(pdev, priv->plat->bsp_priv);
+
+       return stmmac_resume(dev);
+}
+
+static int __maybe_unused thead_dwmac_runtime_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+       stmmac_bus_clks_config(priv, false);
+       thead_dwmac_clk_disable(pdev, priv->plat->bsp_priv);
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_runtime_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = stmmac_bus_clks_config(priv, true);
+       if(ret)
+               return ret;
+       ret = thead_dwmac_clk_enable(pdev, priv->plat->bsp_priv);
+       if(ret)
+               return ret;
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_noirq_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       if (!netif_running(ndev))
+               return 0;
+
+       if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
+               /* Disable clock in case of PWM is off */
+               clk_disable_unprepare(priv->plat->clk_ptp_ref);
+
+               ret = pm_runtime_force_suspend(dev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_noirq_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       if (!netif_running(ndev))
+               return 0;
+
+       if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
+               /* enable the clk previously disabled */
+               ret = pm_runtime_force_resume(dev);
+               if (ret)
+                       return ret;
+
+               ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+               if (ret < 0) {
+                       netdev_warn(priv->dev,
+                                   "failed to enable PTP reference clock: %pe\n",
+                                   ERR_PTR(ret));
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/*similar with stmmac_pltfr_pm_ops,but clks enable/disable add this drv need */
+const struct dev_pm_ops thead_dwmac_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(thead_dwmac_suspend, thead_dwmac_resume)
+       SET_RUNTIME_PM_OPS(thead_dwmac_runtime_suspend, thead_dwmac_runtime_resume, NULL)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(thead_dwmac_noirq_suspend, thead_dwmac_noirq_resume)
+};
+
+
+static struct thead_dwmac_ops thead_ice_dwmac_data = {
+       .set_clk_source = thead_dwmac_ice_set_clk_source,
+       .set_clk_pll = thead_dwmac_ice_set_pll,
+       .set_clk_div = thead_dwmac_ice_set_clk_div,
+       .enable_clk = thead_dwmac_ice_enable_clk,
+};
+
+static struct thead_dwmac_ops thead_light_dwmac_data = {
+       .set_clk_div = thead_dwmac_light_set_clk_div,
+       .enable_clk = thead_dwmac_light_enable_clk,
+       .set_ptp_div = thead_dwmac_light_set_ptp_clk_div,
+};
+
+static const struct of_device_id thead_dwmac_match[] = {
+       { .compatible = "thead,ice-dwmac", .data = &thead_ice_dwmac_data },
+       { .compatible = "thead,light-dwmac", .data = &thead_light_dwmac_data },
+       { }
+};
+MODULE_DEVICE_TABLE(of, thead_dwmac_match);
+
+static struct platform_driver thead_dwmac_driver = {
+       .probe  = thead_dwmac_probe,
+       .remove_new = stmmac_pltfr_remove,
+       .driver = {
+               .name           = "light_dwmac_eth",
+               .pm             = &thead_dwmac_pm_ops,
+               .of_match_table = of_match_ptr(thead_dwmac_match),
+       },
+};
+module_platform_driver(thead_dwmac_driver);
+
+MODULE_AUTHOR("THEAD");
+MODULE_DESCRIPTION("T-HEAD dwmac driver");
+MODULE_LICENSE("GPL v2");