ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/net/ethernet/stmicro/stmmac/dwmac-asr.c b/marvell/linux/drivers/net/ethernet/stmicro/stmmac/dwmac-asr.c
new file mode 100644
index 0000000..c4eac12
--- /dev/null
+++ b/marvell/linux/drivers/net/ethernet/stmicro/stmmac/dwmac-asr.c
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dwmac-stm32.c - DWMAC Specific Glue layer for ASR SOC
+ *
+ * Copyright (C) ASR Microelectronics 2022
+ * Author:  Fei Lv <feilv@asrmicro.com>.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/stmmac.h>
+#include <linux/cputype.h>
+#include <soc/asr/regs-addr.h>
+
+#include "stmmac_platform.h"
+
+#define APMU_XGMAC_CLK_RST_CTRL 	0x3d4
+#define XGMAC_PHY_INTR_EN		(0x1 << 12)
+#define XGMAC_PHY_LPI_INTR_EN		(0x1 << 11)
+#define XGMAC_RGMII_TXC_SRC_SEL		(0x1 << 8)
+#define XGMAC_INTF_SEL_SHIFT		2
+#define XGMAC_INTF_SEL_MASK		GENMASK(3, XGMAC_INTF_SEL_SHIFT)
+#define XGMAC_INTF_SEL_RGMII		(0x1 << XGMAC_INTF_SEL_SHIFT)
+#define APMU_XGMAC_RGMII_RX_DLINE	0x3d8
+#define APMU_XGMAC_RGMII_TX_DLINE	0x3dc
+#define APMU_HSIO_RC_SAVE		0x3f8
+#define APB_SPARE24_REG			0x15c
+
+/*
+ * mapping table:
+ *
+ * 0x0c_0000 ~ 0x0c_00ac ==> 0xc010_0000 ~ 0xc010_00ac
+ * 0x0c_1c20 ~ 0x0c_1c40 ==> 0xc010_0120 ~ 0xc010_0140
+ * 0x0e_0000 ~ 0x0e_0044 ==> 0xc010_0200 ~ 0xc010_0244
+ * 0x78_0000 ~ 0x78_003c ==> 0xc010_0300 ~ 0xc010_033c
+ * 0x7c_0000 ~ 0x7c_003c ==> 0xc010_0400 ~ 0xc010_043c
+ * 0x7e_0000 ~ 0x7e_0028 ==> 0xc010_0500 ~ 0xc010_0528
+ * 0x80_0800 ~ 0x80_0b00 ==> 0xc010_0800 ~ 0xc010_0b00
+ * 0xc0_1000 ~ 0xc0_1fff ==> 0xc010_1000 ~ 0xc010_1fff
+*/
+#define VR_XS_PCS_DIG_CTRL1	0x200
+#define VR_XS_PCS_KR_CTRL	0x21c
+#define PMA_BASE		0x800
+#define SR_MII_CTRL		0x400
+#define VR_MII_AN_CTRL		0x504
+#define SR_XS_PCS_CTRL2		0x01c
+#define SR_XS_PCS_CTRL1		0x000
+#define SR_XS_PCS_STS1		0x004
+#define VR_XS_PCS_DIG_STS	0x240
+#define SR_XS_PCS_TP_CTRL	0x0a8
+#define VR_XS_PCS_KR_CTRL	0x21c
+#define SR_XS_PCS_TP_ERRCTR	0x0ac
+#define VR_XS_PCS_EEE_MCTRL	0x218
+
+struct asr_dwmac {
+	struct clk *clk_eth_ck;
+	u32 mode_reg;		 /* MAC glue-logic mode register */
+	struct regmap *regmap;
+	u32 speed;
+	const struct asr_xgmac_ops *ops;
+	struct device *dev;
+	struct plat_stmmacenet_data *plat;
+
+	int phy_intr_disable;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *rgmii_pins;
+	int rst_gpio, ldo_gpio, ldo_gpio2;
+
+	/* rgmii clock settings */
+	int rgmii_tx_clk_soc;
+	u32 rgmii_tx_delay_code;
+	u32 rgmii_tx_delay_step;
+	u32 rgmii_rx_delay_code;
+	u32 rgmii_rx_delay_step;
+};
+
+struct asr_xgmac_ops {
+	int (*set_mode)(struct plat_stmmacenet_data *plat_dat);
+	int (*clk_prepare)(struct asr_dwmac *dwmac, bool prepare);
+	int (*suspend)(struct asr_dwmac *dwmac);
+	void (*resume)(struct asr_dwmac *dwmac);
+	int (*parse_data)(struct asr_dwmac *dwmac,
+			  struct device *dev);
+};
+
+#if 0
+static void asr_xgmac_phy_reg_dump(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	int ret, val;
+	int i;
+
+	for (i = 0; i <= 0xac; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x120; i <= 0x140; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x200; i <= 0x244; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x300; i <= 0x33c; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"VS_MMD1:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x400; i <= 0x43c; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"VS_MII_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x500; i <= 0x528; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"VS_MII_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+	for (i = 0x800; i <= 0xb00; i = i + 4) {
+		ret = regmap_read(dwmac->regmap, i, &val);
+		dev_info(dwmac->dev,"PMA:0x%x:0x%x\n", 0xC0100000 + i, val);
+	}
+}
+#endif
+
+static void asr_dwmac_set_clk_rst(struct plat_stmmacenet_data *plat_dat)
+{
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	u32 val;
+
+	val = readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL);
+	val |= 0x3;
+	writel(val, apmu_base + APMU_XGMAC_CLK_RST_CTRL);
+}
+
+static int asr_dwmac_get_phy_rc(struct plat_stmmacenet_data *plat_dat)
+{
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	u32 val;
+	(void)plat_dat;
+
+	val = readl(apmu_base + APMU_HSIO_RC_SAVE);
+	return val;
+}
+
+static int asr_dwmac_enable_rc_r_cal(struct plat_stmmacenet_data *plat_dat)
+{
+	void __iomem *apb_spare = regs_addr_get_va(REGS_ADDR_APBS);
+	u32 val;
+	(void)plat_dat;
+
+	val = readl(apb_spare + APB_SPARE24_REG);
+	if ((val & (0x3 << 14)) == (0x3 << 14))
+		return 0;
+
+	writel(val | BIT(0), apb_spare + APB_SPARE24_REG);
+	do {
+		val = readl(apb_spare + APB_SPARE24_REG);
+		if ((val & (0x3 << 14)) == (0x3 << 14))
+			break;
+	} while (1);
+
+	return 0;
+}
+
+static void asr_dwmac_set_rgmii_rx_dline(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	u32 val, dline;
+
+	dline = dwmac->rgmii_rx_delay_step;
+	dline |= (dwmac->rgmii_rx_delay_code << 8);
+	val = readl(apmu_base + APMU_XGMAC_RGMII_RX_DLINE);
+	val &= ~(0xff << 8 | 0xff);
+	val |= dline;
+	val |= BIT(31);
+	writel(val, apmu_base + APMU_XGMAC_RGMII_RX_DLINE);
+	pr_info("==> APMU_XGMAC_RGMII_RX_DLINE: 0x%x rgmii_rx_delay_code=%d\n",
+		readl(apmu_base + APMU_XGMAC_RGMII_RX_DLINE),
+		dwmac->rgmii_rx_delay_code);
+}
+
+static void asr_dwmac_set_rgmii_tx_dline(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	u32 val, dline;
+
+	dline = dwmac->rgmii_tx_delay_step;
+	dline |= (dwmac->rgmii_tx_delay_code << 8);
+	val = readl(apmu_base + APMU_XGMAC_RGMII_TX_DLINE);
+	val &= ~(0xff << 8 | 0xff);
+	val |= dline;
+	val |= BIT(31);
+	writel(val, apmu_base + APMU_XGMAC_RGMII_TX_DLINE);
+	pr_info("==> APMU_XGMAC_RGMII_TX_DLINE: 0x%x rgmii_tx_delay_code=%d\n",
+		readl(apmu_base + APMU_XGMAC_RGMII_TX_DLINE),
+		dwmac->rgmii_tx_delay_code);
+}
+
+static void asr_dwmac_select_interface(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	u32 val;
+
+	val = readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL);
+	val &= ~(XGMAC_INTF_SEL_MASK | XGMAC_RGMII_TXC_SRC_SEL);
+	/* RGMII settings */
+	if (plat_dat->interface == PHY_INTERFACE_MODE_RGMII) {
+		pinctrl_select_state(dwmac->pinctrl, dwmac->rgmii_pins);
+		val |= XGMAC_INTF_SEL_RGMII;
+		if (dwmac->rgmii_tx_clk_soc)
+			val |= XGMAC_RGMII_TXC_SRC_SEL;
+		asr_dwmac_set_rgmii_rx_dline(plat_dat);
+		asr_dwmac_set_rgmii_tx_dline(plat_dat);
+	}
+	if (dwmac->phy_intr_disable)
+		val &= ~XGMAC_PHY_INTR_EN;
+	else
+		val |= XGMAC_PHY_INTR_EN;
+	writel(val, apmu_base + APMU_XGMAC_CLK_RST_CTRL);
+
+	pr_info("==> APMU_XGMAC_CLK_RST_CTRL: 0x%x\n",
+		readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL));
+}
+
+static int asr_dwmac_init(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	int ret;
+
+	dwmac->plat = plat_dat;
+	asr_dwmac_set_clk_rst(plat_dat);
+
+	if (dwmac->ops->set_mode) {
+		ret = dwmac->ops->set_mode(plat_dat);
+		if (ret)
+			return ret;
+	}
+
+	if (dwmac->ops->clk_prepare)
+		ret = dwmac->ops->clk_prepare(dwmac, true);
+
+	return ret;
+}
+
+static int asr_xgmac_clk_prepare(struct asr_dwmac *dwmac, bool prepare)
+{
+	int ret = 0;
+
+	if (!dwmac->clk_eth_ck)
+		return ret;
+
+	if (prepare)
+		ret = clk_prepare_enable(dwmac->clk_eth_ck);
+	else
+		clk_disable_unprepare(dwmac->clk_eth_ck);
+
+	return ret;
+}
+
+static int asr_xgmac_set_sgmii_2500basex(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	int speed = dwmac->speed;
+	int val, ret;
+
+	/* disable USXGMII mode */
+	ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
+	val &= ~(0x1 << 9);
+	ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
+
+	if (plat_dat->interface == PHY_INTERFACE_MODE_2500BASEX) {
+		/* Select 10GBASE-X PCS Type */
+		ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL2, &val);
+		val &= ~0xf;
+		val |= 0xe;
+		ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL2, val);
+	} else {
+		/* Select 10GBASE-X PCS Type */
+		ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL2, &val);
+		val &= ~0xf;
+		val |= 0x1;
+		ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL2, val);
+
+		/* select SGMII mode */
+		ret = regmap_read(dwmac->regmap, VR_MII_AN_CTRL, &val);
+		val &= ~(0x3 << 1);
+		val |= 0x2 << 1;
+		ret = regmap_write(dwmac->regmap, VR_MII_AN_CTRL, val);
+
+		/* disable rpcs_clk in sgmii mode */
+		ret = regmap_write(dwmac->regmap, PMA_BASE, 0x15551555);
+
+		/* Enable 2.5G GMII Mode if needed */
+		ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
+		if (speed == SPEED_2500)
+			val |= 0x1 << 2;
+		else
+			val &= ~(0x1 << 2);
+		ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
+	}
+
+	switch (speed) {
+	case SPEED_2500:
+	case SPEED_1000:
+		/* set 2.5G/1G speed */
+		ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
+		val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
+		val |=  0x1 << 6;
+		ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);	
+		break;
+	case SPEED_100:
+		/* set 100M speed */
+		ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
+		val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
+		val |=  0x1 << 13;
+		ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);	
+		break;
+	case SPEED_10:
+		/* set 10M speed */
+		ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
+		val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
+		ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);	
+		break;
+	default:
+		dev_err(dwmac->dev, "unsupported sgmii speed %d\n", speed);
+		break;
+	}
+
+	/* PCS POWER down */
+	ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL1, &val);
+	val |= 0x1 << 11;
+	ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL1, val);
+
+	if (cpu_is_asr1901()) {
+		/* get rc value from HSIO RC save register */
+		val = asr_dwmac_get_phy_rc(plat_dat);
+		val &= 0xffff;
+		ret = regmap_write(dwmac->regmap, PMA_BASE + 0x28, val);
+	} else {
+		asr_dwmac_enable_rc_r_cal(plat_dat);
+	}
+
+	/* turn off ssc */
+	ret = regmap_read(dwmac->regmap, PMA_BASE + 0x1c, &val);
+	val &= ~(0xf << 24);
+	ret = regmap_write(dwmac->regmap, PMA_BASE + 0x1c, val);
+
+	/* wait till PSEQ_STATE are not 3'b100 */
+	do {
+		ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_STS, &val);
+		if ((val & (0x7 << 2)) != 0x10)
+			break;
+	} while (1);
+	
+	/* PCS POWER UP */
+	ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL1, &val);
+	val &= ~(0x1 << 11);
+	ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL1, val);
+
+	/* wait PLL lock */
+	do {
+		ret = regmap_read(dwmac->regmap, PMA_BASE + 8, &val);
+		if (val & (0x1 << 24))
+			break;
+	} while (1);
+	dev_info(dwmac->dev,"PLL is locked, phy mode: %s\n",
+		phy_modes(plat_dat->interface));
+
+	/* clear bit15/bit11 to enable pu_tx and rx data */
+	ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, 0x0);
+	return ret;
+}
+
+static int asr_xgmac_set_usxgmii(struct plat_stmmacenet_data *plat_dat)
+{
+	struct asr_dwmac *dwmac = plat_dat->bsp_priv;
+	int speed = dwmac->speed;
+	int val, ret;
+
+	/* disable xgxs_clk for usxgmii mode */
+	ret = regmap_write(dwmac->regmap, PMA_BASE, 0x55455545);
+
+	/* enable USXGMII mode */
+	ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
+	val |= 0x1 << 9;
+	ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
+
+	switch (speed) {
+	case SPEED_2500:
+		/* enable 2.5G-SXGMII */
+		ret = regmap_read(dwmac->regmap, VR_XS_PCS_KR_CTRL, &val);
+		val &= ~(0x7 << 10);
+		val |= 0x2 << 10;
+		ret = regmap_write(dwmac->regmap, VR_XS_PCS_KR_CTRL, val);	
+
+		/* set 2.5G speed */
+		ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
+		val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
+		val |=  0x1 << 5;
+		ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
+		break;
+	case SPEED_5000:
+		/* enable 5G-SXGMII */
+		ret = regmap_read(dwmac->regmap, VR_XS_PCS_KR_CTRL, &val);
+		val &= ~(0x7 << 10);
+		val |= 0x1 << 10;
+		ret = regmap_write(dwmac->regmap, VR_XS_PCS_KR_CTRL, val);
+
+		/* set 5G speed */
+		ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
+		val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
+		val |=  0x1 << 5 | 0x1 << 13;
+		ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
+		break;
+	default:
+		dev_err(dwmac->dev, "unsupported usxgmii speed %d\n", speed);
+		break;
+	}
+
+	/* force mpu to 0*/
+	ret = regmap_read(dwmac->regmap, PMA_BASE + 0x44, &val);
+	val |= 0x1 << 9;
+	ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, val);
+
+	if (cpu_is_asr1901()) {
+		/* get rc value from HSIO RC save register */
+		val = asr_dwmac_get_phy_rc(plat_dat);
+		val &= 0xffff;
+		ret = regmap_write(dwmac->regmap, PMA_BASE + 0x28, val);
+	} else {
+		asr_dwmac_enable_rc_r_cal(plat_dat);
+	}
+
+	/* release mpu to 1 */
+	ret = regmap_read(dwmac->regmap, PMA_BASE + 0x44, &val);
+	val &= ~(0x1 << 9);
+	ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, val);
+
+	/* do xpcs soft reset to change mode */
+	ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
+	val |= 0x1 << 15;
+	ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);	
+
+	/* wait PLL lock */
+	do {
+		ret = regmap_read(dwmac->regmap, PMA_BASE + 8, &val);
+		if (val & (0x1 << 24))
+			break;
+	} while (1);
+	dev_info(dwmac->dev,"USXGMII: pll is locked\n");
+
+	/* Enable TX PMA output */
+	ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, 0x0);
+	return ret;	
+}
+
+static int asr_xgmac_set_mode(struct plat_stmmacenet_data *plat_dat)
+{
+	/* select RGMII or XGMII/GMII */
+	asr_dwmac_select_interface(plat_dat);
+
+	switch (plat_dat->interface) {
+	case PHY_INTERFACE_MODE_RGMII:
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		asr_xgmac_set_sgmii_2500basex(plat_dat);
+		break;
+	case PHY_INTERFACE_MODE_USXGMII:
+		asr_xgmac_set_usxgmii(plat_dat);
+		break;
+	default:
+		pr_debug("SYSCFG init :  Do not manage %d interface\n",
+			 plat_dat->interface);
+		/* Do not manage others interfaces */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void asr_xgmac_fix_mac_speed(void *priv, unsigned int speed)
+{
+	struct asr_dwmac *dwmac = priv;
+	struct plat_stmmacenet_data *plat_dat = dwmac->plat;
+
+	dwmac->speed = speed;
+	asr_xgmac_set_mode(plat_dat);
+}
+
+static void asr_dwmac_clk_disable(struct asr_dwmac *dwmac)
+{
+	if (dwmac->ops->clk_prepare)
+		dwmac->ops->clk_prepare(dwmac, false);
+}
+
+static int asr_dwmac_parse_data(struct asr_dwmac *dwmac,
+				  struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	int err;
+
+	if (dwmac->ops->parse_data) {
+		err = dwmac->ops->parse_data(dwmac, dev);
+		if (err)
+			return err;
+	}
+
+	/* Get mode register */
+	dwmac->regmap = syscon_regmap_lookup_by_phandle(np, "xgmac,phy");
+	if (IS_ERR(dwmac->regmap))
+		return PTR_ERR(dwmac->regmap);
+
+	err = of_property_read_u32_index(np, "xgmac,phy", 1, &dwmac->mode_reg);
+	if (err)
+		dev_err(dev, "Can't get sysconfig mode offset (%d)\n", err);
+
+	return err;
+}
+
+static int asr_xgmac_parse_data(struct asr_dwmac *dwmac,
+			       struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	u32 tx_delay_code, rx_delay_code;
+	u32 tx_delay_step, rx_delay_step;
+
+	dwmac->pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR(dwmac->pinctrl))
+		dev_err(dev, "could not get pinctrl handle\n");
+	else {
+		dwmac->rgmii_pins = pinctrl_lookup_state(dwmac->pinctrl, "rgmii");
+		if (IS_ERR(dwmac->rgmii_pins))
+			dev_err(dev, "could not get rgmii pinstate\n");
+	}
+
+	dwmac->clk_eth_ck = devm_clk_get(dev, "xgmac-clk");
+	if (IS_ERR(dwmac->clk_eth_ck)) {
+		dev_warn(dev, "No phy clock provided...\n");
+		dwmac->clk_eth_ck = NULL;
+	}
+
+	dwmac->phy_intr_disable =
+		of_property_read_bool(np, "xgmac,no-phy-intterrupt");
+	dwmac->rgmii_tx_clk_soc =
+		of_property_read_bool(np, "xgmac,rgmii-tx-clk-soc");
+	if (!of_property_read_u32(np, "xgmac,tx-delay", &tx_delay_code)) {
+		if (tx_delay_code < 255) {
+			dwmac->rgmii_tx_delay_code = tx_delay_code;
+		} else {
+			dev_err(dev, "Invalid TX clock delay: %dps\n",
+				tx_delay_code);
+			return -EINVAL;
+		}
+
+		if (!of_property_read_u32(np, "xgmac,tx-delay-step",
+					  &tx_delay_step)) {
+			if (tx_delay_step < 4) {
+				dwmac->rgmii_tx_delay_step = tx_delay_step;
+			} else {
+				dev_err(dev, "Invalid TX delay step: %dps\n",
+					tx_delay_step);
+				return -EINVAL;
+			}
+		} else {
+			dwmac->rgmii_tx_delay_step = 0;
+		}
+	} else {
+		dwmac->rgmii_tx_delay_code = 0;
+	}
+
+	if (!of_property_read_u32(np, "xgmac,rx-delay", &rx_delay_code)) {
+		if (rx_delay_code < 255) {
+			dwmac->rgmii_rx_delay_code = rx_delay_code;
+		} else {
+			dev_err(dev, "Invalid RX clock delay: %dps\n", rx_delay_code);
+			return -EINVAL;
+		}
+		if (!of_property_read_u32(np, "xgmac,rx-delay-step",
+					  &rx_delay_step)) {
+			if (rx_delay_step < 4) {
+				dwmac->rgmii_rx_delay_step = rx_delay_step;
+			} else {
+				dev_err(dev, "Invalid RX delay step: %dps\n",
+					rx_delay_step);
+				return -EINVAL;
+			}
+		} else {
+			dwmac->rgmii_rx_delay_step = 0;
+		}
+	} else {
+		dwmac->rgmii_rx_delay_code = 0;
+	}
+
+	return 0;
+}
+
+static int asr_dwmac_gpio_ctrl(struct asr_dwmac *dwmac,
+				  struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	int rst_gpio, ldo_gpio, ldo_gpio2;
+	int low_active_rst;
+	u32 delays_rst[3];
+	int ret;
+
+	rst_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+	if (rst_gpio >= 0) {
+		low_active_rst = of_property_read_bool(np, "reset-active-low");
+		of_property_read_u32_array(np, "reset-delays-us", delays_rst, 3);
+
+		ret = gpio_request(rst_gpio, "xgmac-reset");
+		if (ret < 0) {
+			printk("xgmac: reset-gpio=%d request failed ret=%d\n",
+				rst_gpio, ret);
+			return ret;
+		}
+		dwmac->rst_gpio = rst_gpio;
+	}
+
+	ldo_gpio = of_get_named_gpio(np, "3v3-ldo-gpio", 0);
+	if (ldo_gpio >= 0) {
+		ret = gpio_request(ldo_gpio, "xgmac-ldo-3v3");
+		if (ret < 0) {
+			printk("xgmac: xgmac-ldo-3v3=%d request failed ret=%d\n",
+				ldo_gpio, ret);
+			return ret;
+		}
+
+		dwmac->ldo_gpio = ldo_gpio;
+		gpio_direction_output(ldo_gpio, 1);
+	}
+
+	ldo_gpio2 = of_get_named_gpio(np, "0v95-ldo-gpio", 0);
+	if (ldo_gpio2 >= 0) {
+		ret = gpio_request(ldo_gpio2, "xgmac-ldo-0v95");
+		if (ret < 0) {
+			printk("xgmac: xgmac-ldo-0v95=%d request failed ret=%d\n",
+				ldo_gpio, ret);
+			return ret;
+		}
+
+		dwmac->ldo_gpio2 = ldo_gpio2;
+		gpio_direction_output(ldo_gpio2, 1);
+	}
+
+	/* reset */
+	if (delays_rst[0]) {
+		gpio_direction_output(rst_gpio, low_active_rst ? 1 : 0);
+		msleep(DIV_ROUND_UP(delays_rst[0], 1000));
+	}
+	gpio_direction_output(rst_gpio, low_active_rst ? 0 : 1);
+	if (delays_rst[1])
+		msleep(DIV_ROUND_UP(delays_rst[1], 1000));
+	gpio_direction_output(rst_gpio, low_active_rst ? 1 : 0);
+	if (delays_rst[2])
+		msleep(DIV_ROUND_UP(delays_rst[2], 1000));
+
+	dev_info(dev, "===> gpio init finished\n");
+	return 0;
+}
+
+static int asr_dwmac_probe(struct platform_device *pdev)
+{
+	struct plat_stmmacenet_data *plat_dat;
+	struct stmmac_resources stmmac_res;
+	struct asr_dwmac *dwmac;
+	const struct asr_xgmac_ops *data;
+	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))
+		return PTR_ERR(plat_dat);
+
+	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
+	if (!dwmac) {
+		ret = -ENOMEM;
+		goto err_remove_config_dt;
+	}
+
+	data = of_device_get_match_data(&pdev->dev);
+	if (!data) {
+		dev_err(&pdev->dev, "no of match data provided\n");
+		ret = -EINVAL;
+		goto err_remove_config_dt;
+	}
+
+	dwmac->ops = data;
+	dwmac->dev = &pdev->dev;
+	dwmac->speed = SPEED_1000;
+
+	ret = asr_dwmac_parse_data(dwmac, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to parse OF data\n");
+		goto err_remove_config_dt;
+	}
+
+	plat_dat->bsp_priv = dwmac;
+	plat_dat->fix_mac_speed = asr_xgmac_fix_mac_speed;
+
+	ret = asr_dwmac_gpio_ctrl(dwmac, &pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = asr_dwmac_init(plat_dat);
+	if (ret)
+		goto err_remove_config_dt;
+
+#ifdef STMMAC_DIS_SPH
+	plat_dat->sph_disable = 1;
+#endif
+
+	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (ret)
+		goto err_clk_disable;
+
+	return 0;
+
+err_clk_disable:
+	asr_dwmac_clk_disable(dwmac);
+err_remove_config_dt:
+	stmmac_remove_config_dt(pdev, plat_dat);
+
+	return ret;
+}
+
+static int asr_dwmac_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct stmmac_priv *priv = netdev_priv(ndev);
+	int ret = stmmac_dvr_remove(&pdev->dev);
+
+	asr_dwmac_clk_disable(priv->plat->bsp_priv);
+	return ret;
+}
+
+static int asr_xgmac_suspend(struct asr_dwmac *dwmac)
+{
+	int ret = 0;
+
+	if (dwmac->clk_eth_ck)
+		clk_disable_unprepare(dwmac->clk_eth_ck);
+
+	return ret;
+}
+
+static void asr_xgmac_resume(struct asr_dwmac *dwmac)
+{
+	return;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int asr_dwmac_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct stmmac_priv *priv = netdev_priv(ndev);
+	struct asr_dwmac *dwmac = priv->plat->bsp_priv;
+
+	int ret;
+
+	ret = stmmac_suspend(dev);
+
+	if (dwmac->ops->suspend)
+		ret = dwmac->ops->suspend(dwmac);
+
+	return ret;
+}
+
+static int asr_dwmac_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct stmmac_priv *priv = netdev_priv(ndev);
+	struct asr_dwmac *dwmac = priv->plat->bsp_priv;
+	int ret;
+
+	if (dwmac->ops->resume)
+		dwmac->ops->resume(dwmac);
+
+	ret = asr_dwmac_init(priv->plat);
+	if (ret)
+		return ret;
+
+	ret = stmmac_resume(dev);
+
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(asr_dwmac_pm_ops,
+	asr_dwmac_suspend, asr_dwmac_resume);
+
+static struct asr_xgmac_ops asr_xgmac_dwmac_data = {
+	.set_mode = asr_xgmac_set_mode,
+	.clk_prepare = asr_xgmac_clk_prepare,
+	.suspend = asr_xgmac_suspend,
+	.resume = asr_xgmac_resume,
+	.parse_data = asr_xgmac_parse_data,
+};
+
+static const struct of_device_id asr_dwmac_match[] = {
+	{ .compatible = "asr,dwc-xgmac", .data = &asr_xgmac_dwmac_data},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, asr_dwmac_match);
+
+static struct platform_driver asr_dwmac_driver = {
+	.probe  = asr_dwmac_probe,
+	.remove = asr_dwmac_remove,
+	.driver = {
+		.name           = "asr-xgmac",
+		.pm		= &asr_dwmac_pm_ops,
+		.of_match_table = asr_dwmac_match,
+	},
+};
+module_platform_driver(asr_dwmac_driver);
+
+MODULE_AUTHOR("Fei Lv <feilv@asrmicro.com>");
+MODULE_DESCRIPTION("ASR Microelectronics DWMAC Specific Glue layer");
+MODULE_LICENSE("GPL v2");