Commit cc59dbcc authored by David S. Miller's avatar David S. Miller

Merge branch 'net-bcmgenet-restore-internal-EPHY-support'

Doug Berger says:

====================
net: bcmgenet: restore internal EPHY support (part 2)

This is a follow up to my previous submission (see [1]).

The first commit provides what is intended to be a complete solution
for the issues that can result from insufficient clocking of the MAC
during reset of its state machines. It should be backported to the
stable releases.

It is intended to replace the partial solution of commit 1f515486
("net: bcmgenet: soft reset 40nm EPHYs before MAC init") which is
reverted by the second commit of this series and should not be back-
ported as noted in [2].

The third commit corrects a timing hazard with a polled PHY that can
occur when the MAC resumes and also when a v3 internal EPHY is reset
by the change in commit 25382b99 ("net: bcmgenet: reset 40nm EPHY
on energy detect"). It is expected that commit 25382b99 be back-
ported to stable first before backporting this commit.

[1] https://lkml.org/lkml/2019/10/16/1706
[2] https://lkml.org/lkml/2019/10/31/749
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2836654a 0686bd9d
......@@ -1996,8 +1996,6 @@ static void reset_umac(struct bcmgenet_priv *priv)
/* issue soft reset with (rg)mii loopback to ensure a stable rxclk */
bcmgenet_umac_writel(priv, CMD_SW_RESET | CMD_LCL_LOOP_EN, UMAC_CMD);
udelay(2);
bcmgenet_umac_writel(priv, 0, UMAC_CMD);
}
static void bcmgenet_intr_disable(struct bcmgenet_priv *priv)
......@@ -2614,8 +2612,10 @@ static void bcmgenet_irq_task(struct work_struct *work)
spin_unlock_irq(&priv->lock);
if (status & UMAC_IRQ_PHY_DET_R &&
priv->dev->phydev->autoneg != AUTONEG_ENABLE)
priv->dev->phydev->autoneg != AUTONEG_ENABLE) {
phy_init_hw(priv->dev->phydev);
genphy_config_aneg(priv->dev->phydev);
}
/* Link UP/DOWN event */
if (status & UMAC_IRQ_LINK_EVENT)
......@@ -2879,12 +2879,6 @@ static int bcmgenet_open(struct net_device *dev)
if (priv->internal_phy)
bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
ret = bcmgenet_mii_connect(dev);
if (ret) {
netdev_err(dev, "failed to connect to PHY\n");
goto err_clk_disable;
}
/* take MAC out of reset */
bcmgenet_umac_reset(priv);
......@@ -2894,12 +2888,6 @@ static int bcmgenet_open(struct net_device *dev)
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
priv->crc_fwd_en = !!(reg & CMD_CRC_FWD);
ret = bcmgenet_mii_config(dev, true);
if (ret) {
netdev_err(dev, "unsupported PHY\n");
goto err_disconnect_phy;
}
bcmgenet_set_hw_addr(priv, dev->dev_addr);
if (priv->internal_phy) {
......@@ -2915,7 +2903,7 @@ static int bcmgenet_open(struct net_device *dev)
ret = bcmgenet_init_dma(priv);
if (ret) {
netdev_err(dev, "failed to initialize DMA\n");
goto err_disconnect_phy;
goto err_clk_disable;
}
/* Always enable ring 16 - descriptor ring */
......@@ -2938,19 +2926,25 @@ static int bcmgenet_open(struct net_device *dev)
goto err_irq0;
}
ret = bcmgenet_mii_probe(dev);
if (ret) {
netdev_err(dev, "failed to connect to PHY\n");
goto err_irq1;
}
bcmgenet_netif_start(dev);
netif_tx_start_all_queues(dev);
return 0;
err_irq1:
free_irq(priv->irq1, priv);
err_irq0:
free_irq(priv->irq0, priv);
err_fini_dma:
bcmgenet_dma_teardown(priv);
bcmgenet_fini_dma(priv);
err_disconnect_phy:
phy_disconnect(dev->phydev);
err_clk_disable:
if (priv->internal_phy)
bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
......@@ -3631,8 +3625,6 @@ static int bcmgenet_resume(struct device *d)
if (priv->internal_phy)
bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
phy_init_hw(dev->phydev);
bcmgenet_umac_reset(priv);
init_umac(priv);
......@@ -3641,7 +3633,10 @@ static int bcmgenet_resume(struct device *d)
if (priv->wolopts)
clk_disable_unprepare(priv->clk_wol);
phy_init_hw(dev->phydev);
/* Speed settings must be restored */
genphy_config_aneg(dev->phydev);
bcmgenet_mii_config(priv->dev, false);
bcmgenet_set_hw_addr(priv, dev->dev_addr);
......
......@@ -720,8 +720,8 @@ GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_connect(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
......
......@@ -173,46 +173,6 @@ static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_connect(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
return 0;
}
int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
......@@ -221,8 +181,38 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
int bmcr = -1;
int ret;
u32 reg;
/* MAC clocking workaround during reset of umac state machines */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
if (reg & CMD_SW_RESET) {
/* An MII PHY must be isolated to prevent TXC contention */
if (priv->phy_interface == PHY_INTERFACE_MODE_MII) {
ret = phy_read(phydev, MII_BMCR);
if (ret >= 0) {
bmcr = ret;
ret = phy_write(phydev, MII_BMCR,
bmcr | BMCR_ISOLATE);
}
if (ret) {
netdev_err(dev, "failed to isolate PHY\n");
return ret;
}
}
/* Switch MAC clocking to RGMII generated clock */
bcmgenet_sys_writel(priv, PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
/* Ensure 5 clks with Rx disabled
* followed by 5 clks with Reset asserted
*/
udelay(4);
reg &= ~(CMD_SW_RESET | CMD_LCL_LOOP_EN);
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
/* Ensure 5 more clocks before Rx is enabled */
udelay(2);
}
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
......@@ -254,6 +244,9 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
phy_set_max_speed(phydev, SPEED_100);
bcmgenet_sys_writel(priv,
PORT_MODE_EXT_EPHY, SYS_PORT_CTRL);
/* Restore the MII PHY after isolation */
if (bmcr >= 0)
phy_write(phydev, MII_BMCR, bmcr);
break;
case PHY_INTERFACE_MODE_REVMII:
......@@ -306,7 +299,60 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
}
if (init) {
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
int bcmgenet_mii_probe(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(dev->phydev);
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
......@@ -316,10 +362,7 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
* those versions of GENET.
*/
if (priv->internal_phy && !GENET_IS_V5(priv))
phydev->irq = PHY_IGNORE_INTERRUPT;
dev_info(kdev, "configuring instance for %s\n", phy_name);
}
dev->phydev->irq = PHY_IGNORE_INTERRUPT;
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment