/* * Copyright (c) Company 2018-2019. All rights reserved. * Description: GMAC network driver */ #include #include #include #include #include "gmac.h" #ifdef GMAC_HAS_INTERNAL_PHY /* register REG_CRG_FEPHY */ #define REG_CRG_FEPHY 0x0388 #define BIT_FEPHY_CLK BIT(0) #define BIT_FEPHY_RST BIT(4) /* register REG_PERI_FEPHY */ #define REG_PERI_FEPHY 0x0118 #define BIT_MASK_FEPHY_ADDR 0x1F #define BIT_MASK_FEPHY_WOL BIT(5) /* register REG_PERI_FEPHY_LDO */ #define REG_PERI_FEPHY_LDO 0x0844 #define BIT_LDO_EN BIT(4) #define BIT_LDO_ENZ BIT(5) #define BIT_LDO_RSTN BIT(6) #define BIT_IDDQ_MODE BIT(7) #define LDO_VSET_MASK 0xF #define LDO_VSET_VAL 0x8 #endif #define GMAC_RESET_TIMEOUT 1000 void gmac_mac_core_reset(const struct gmac_netdev_local *priv) { int timeout = GMAC_RESET_TIMEOUT; if (priv == NULL || priv->port_rst == NULL) return; /* undo reset */ reset_control_deassert(priv->port_rst); /* * When GMAC is not in an idle state, we could not reset GMAC * successfully. And it may cause GMAC stalling in an abnormal state if * we turn off its clock when GMAC is initiating a bus access. Thus * we diable tx/rx and desc_read_write here to ensure that GMAC no * longer initiates new bus access. */ gmac_hw_desc_disable(priv); gmac_port_disable(priv); msleep(1); /* * When GMAC is not in an idle state, We could not successfully * reset it, thus here we reset the GMAC repeatedly and read the * RX_FQ_START_ADDR register to check whether the GMAC is successfully * reset. */ while (timeout--) { /* soft reset mac port */ reset_control_assert(priv->port_rst); msleep(1); /* undo reset */ reset_control_deassert(priv->port_rst); if (readl(priv->gmac_iobase + RX_FQ_START_ADDR) == 0) { break; } } if (timeout < 0) { pr_err("gmac reset failed!\n"); } return; } void gmac_hw_internal_phy_reset(struct gmac_netdev_local *priv) { #ifdef GMAC_HAS_INTERNAL_PHY unsigned int v; if (priv == NULL) return; /* disable MDCK clock to make sure FEPHY reset success */ clk_disable_unprepare(priv->clk); v = readl(priv->crg_iobase + REG_CRG_FEPHY); v &= ~BIT_FEPHY_CLK; writel(v, priv->crg_iobase + REG_CRG_FEPHY); /* disable clk */ v = readl(priv->peri_iobase + REG_PERI_FEPHY_LDO); /* set internal FEPHY LDO_VSET value, LDO output 1.1V */ v = (v & ~LDO_VSET_MASK) | LDO_VSET_VAL; #if defined(CONFIG_ARCH_FONE) /* enalbe internal FEPHY LDO_EN for hifone_b02 */ v |= BIT_LDO_EN; #endif writel(v, priv->peri_iobase + REG_PERI_FEPHY_LDO); v = readl(priv->crg_iobase + REG_CRG_FEPHY); v |= BIT_FEPHY_CLK; /* use 25MHz clock, enable clk */ writel((u32)v, priv->crg_iobase + REG_CRG_FEPHY); udelay(10); /* 10 us */ /* suppose internal phy can only be used as mac0's phy */ v = readl(priv->peri_iobase + REG_PERI_FEPHY); v &= ~BIT_MASK_FEPHY_ADDR; v |= (priv->phy_addr & BIT_MASK_FEPHY_ADDR); v |= BIT_MASK_FEPHY_WOL; writel(v, priv->peri_iobase + REG_PERI_FEPHY); v = readl(priv->crg_iobase + REG_CRG_FEPHY); v |= BIT_FEPHY_RST; /* set reset bit */ writel((u32)v, priv->crg_iobase + REG_CRG_FEPHY); udelay(10); /* 10 us */ v = readl(priv->crg_iobase + REG_CRG_FEPHY); v &= ~BIT_FEPHY_RST; /* clear reset bit */ writel((u32)v, priv->crg_iobase + REG_CRG_FEPHY); msleep(20); /* delay at least 20ms for MDIO operation */ clk_prepare_enable(priv->clk); #endif } void gmac_hw_phy_reset(struct gmac_netdev_local *priv) { if (priv == NULL) return; if (priv->internal_phy) gmac_hw_internal_phy_reset(priv); else gmac_hw_external_phy_reset(priv); } static void gmac_phy_reset_assert(struct reset_control *rst, bool rst_when_set) { if (rst_when_set) reset_control_assert(rst); else reset_control_deassert(rst); } static void gmac_phy_reset_deassert(struct reset_control *rst, bool rst_when_set) { if (rst_when_set) reset_control_deassert(rst); else reset_control_assert(rst); } #define RESET_DATA 1 #define GPIO_DIR 0x400 void gmac_hw_external_phy_reset(const struct gmac_netdev_local *priv) { bool rst_when_set = false; if (priv == NULL) return; /* RST_BIT, write 0 to reset phy, write 1 to cancel reset */ rst_when_set = true; if (priv->phy_rst != NULL) { gmac_phy_reset_deassert(priv->phy_rst, rst_when_set); msleep(10); /* 10 ms */ gmac_phy_reset_assert(priv->phy_rst, rst_when_set); /* delay some time to ensure reset ok, * this depends on PHY hardware feature */ msleep(20); /* 20 ms */ gmac_phy_reset_deassert(priv->phy_rst, rst_when_set); /* delay some time to ensure later MDIO access */ msleep(170); /* 170 ms delay for rtl8211fs after reset */ } else if (priv->gpio_base) { #ifdef GMAC_RESET_PHY_BY_GPIO /* use gpio to control mac's phy reset */ void __iomem *gpio_base; u32 gpio_bit; u32 v; gpio_base = (void __iomem *)IO_ADDRESS( (unsigned int)priv->gpio_base); gpio_bit = priv->gpio_bit; /* config gpip[x] dir to output */ v = readb(gpio_base + GPIO_DIR); v |= (1 << gpio_bit); writeb(v, gpio_base + GPIO_DIR); /* gpiox[x] set to reset, then delay 200ms */ writeb(RESET_DATA << gpio_bit, gpio_base + (4 << gpio_bit)); /* 4 */ msleep(20); /* 20 ms */ /* then,cancel reset,and should delay 200ms */ writeb((!RESET_DATA) << gpio_bit, gpio_base + (4 << gpio_bit)); /* 4 */ msleep(20); /* 20 ms */ writeb(RESET_DATA << gpio_bit, gpio_base + (4 << gpio_bit)); /* 4 */ /* add some delay in case mdio cann't access now! */ msleep(30); /* 30 ms */ #endif } } void gmac_internal_phy_clk_disable(struct gmac_netdev_local *priv) { #ifdef GMAC_HAS_INTERNAL_PHY u32 v; if (priv == NULL) return; v = readl(priv->crg_iobase + REG_CRG_FEPHY); v &= ~BIT_FEPHY_CLK; v |= BIT_FEPHY_RST; writel(v, priv->crg_iobase + REG_CRG_FEPHY); /* inside fephy clk disable */ #endif } void gmac_internal_phy_clk_enable(struct gmac_netdev_local *priv) { #ifdef GMAC_HAS_INTERNAL_PHY u32 v; if (priv == NULL) return; v = readl(priv->crg_iobase + REG_CRG_FEPHY); v |= BIT_FEPHY_CLK; writel(v, priv->crg_iobase + REG_CRG_FEPHY); /* inside fephy clk enable */ #endif } void gmac_hw_all_clk_disable(struct gmac_netdev_local *priv) { if (priv == NULL || priv->netdev == NULL || priv->macif_clk == NULL || priv->clk == NULL) return; if (netif_running(priv->netdev)) { clk_disable_unprepare(priv->macif_clk); clk_disable_unprepare(priv->clk); } if (priv->internal_phy && !priv->phy_wol_enable) gmac_internal_phy_clk_disable(priv); } void gmac_hw_all_clk_enable(struct gmac_netdev_local *priv) { if (priv == NULL || priv->netdev == NULL || priv->macif_clk == NULL || priv->clk == NULL) return; if (priv->internal_phy) gmac_internal_phy_clk_enable(priv); if (netif_running(priv->netdev)) { clk_prepare_enable(priv->macif_clk); clk_prepare_enable(priv->clk); } }