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

/*
* 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);
}
}