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");