You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
6.7 KiB
270 lines
6.7 KiB
/*
|
|
* Copyright (c) Company 2018-2019. All rights reserved.
|
|
* Description: GMAC network driver
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/clk.h>
|
|
#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);
|
|
}
|
|
}
|