net: axienet: Re-initialize MDIO registers properly after reset

The MDIO clock divisor register setting was only applied on the initial
startup when the driver was loaded. However, this setting is cleared
when the device is reset, such as would occur when the interface was
taken down and brought up again, and so the MDIO bus would be
non-functional afterwards.

Split up the MDIO bus setup and enable into separate functions and
re-enable the bus after a device reset, to ensure that the MDIO
registers are set properly. This also allows us to remove direct access
to MDIO registers in xilinx_axienet_main.c and centralize them all in
xilinx_axienet_mdio.c.

Also, lock the MDIO bus lock around the device reset process, to avoid
MDIO accesses from occurring while the MDIO is disabled during the reset.

Signed-off-by: Robert Hancock <hancock@sedsystems.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Robert Hancock 2019-06-06 16:28:11 -06:00 committed by David S. Miller
parent e7a3d116fa
commit 7789e9ed05
3 changed files with 57 additions and 38 deletions

View File

@ -505,8 +505,9 @@ static inline void axienet_iow(struct axienet_local *lp, off_t offset,
} }
/* Function prototypes visible in xilinx_axienet_mdio.c for other files */ /* Function prototypes visible in xilinx_axienet_mdio.c for other files */
int axienet_mdio_enable(struct axienet_local *lp);
void axienet_mdio_disable(struct axienet_local *lp);
int axienet_mdio_setup(struct axienet_local *lp); int axienet_mdio_setup(struct axienet_local *lp);
int axienet_mdio_wait_until_ready(struct axienet_local *lp);
void axienet_mdio_teardown(struct axienet_local *lp); void axienet_mdio_teardown(struct axienet_local *lp);
#endif /* XILINX_AXI_ENET_H */ #endif /* XILINX_AXI_ENET_H */

View File

@ -914,27 +914,23 @@ static void axienet_dma_err_handler(unsigned long data);
*/ */
static int axienet_open(struct net_device *ndev) static int axienet_open(struct net_device *ndev)
{ {
int ret, mdio_mcreg; int ret;
struct axienet_local *lp = netdev_priv(ndev); struct axienet_local *lp = netdev_priv(ndev);
struct phy_device *phydev = NULL; struct phy_device *phydev = NULL;
dev_dbg(&ndev->dev, "axienet_open()\n"); dev_dbg(&ndev->dev, "axienet_open()\n");
mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
return ret;
/* Disable the MDIO interface till Axi Ethernet Reset is completed. /* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core * When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. If MDIO is not disabled when the reset * including the MDIO. MDIO must be disabled before resetting
* process is started, MDIO will be broken afterwards. * and re-enabled afterwards.
* Hold MDIO bus lock to avoid MDIO accesses during the reset.
*/ */
axienet_iow(lp, XAE_MDIO_MC_OFFSET, mutex_lock(&lp->mii_bus->mdio_lock);
(mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK))); axienet_mdio_disable(lp);
axienet_device_reset(ndev); axienet_device_reset(ndev);
/* Enable the MDIO */ ret = axienet_mdio_enable(lp);
axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg); mutex_unlock(&lp->mii_bus->mdio_lock);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1316,28 +1312,24 @@ static void axienet_dma_err_handler(unsigned long data)
{ {
u32 axienet_status; u32 axienet_status;
u32 cr, i; u32 cr, i;
int mdio_mcreg;
struct axienet_local *lp = (struct axienet_local *) data; struct axienet_local *lp = (struct axienet_local *) data;
struct net_device *ndev = lp->ndev; struct net_device *ndev = lp->ndev;
struct axidma_bd *cur_p; struct axidma_bd *cur_p;
axienet_setoptions(ndev, lp->options & axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
axienet_mdio_wait_until_ready(lp);
/* Disable the MDIO interface till Axi Ethernet Reset is completed. /* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core * When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. So if MDIO is not disabled when the reset * including the MDIO. MDIO must be disabled before resetting
* process is started, MDIO will be broken afterwards. * and re-enabled afterwards.
* Hold MDIO bus lock to avoid MDIO accesses during the reset.
*/ */
axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg & mutex_lock(&lp->mii_bus->mdio_lock);
~XAE_MDIO_MC_MDIOEN_MASK)); axienet_mdio_disable(lp);
__axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET); __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
__axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET); __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
axienet_mdio_enable(lp);
axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg); mutex_unlock(&lp->mii_bus->mdio_lock);
axienet_mdio_wait_until_ready(lp);
for (i = 0; i < TX_BD_NUM; i++) { for (i = 0; i < TX_BD_NUM; i++) {
cur_p = &lp->tx_bd_v[i]; cur_p = &lp->tx_bd_v[i];

View File

@ -5,6 +5,7 @@
* Copyright (c) 2009 Secret Lab Technologies, Ltd. * Copyright (c) 2009 Secret Lab Technologies, Ltd.
* Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>
* Copyright (c) 2010 - 2011 PetaLogix * Copyright (c) 2010 - 2011 PetaLogix
* Copyright (c) 2019 SED Systems, a division of Calian Ltd.
* Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
*/ */
@ -20,7 +21,7 @@
#define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */ #define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */
/* Wait till MDIO interface is ready to accept a new transaction.*/ /* Wait till MDIO interface is ready to accept a new transaction.*/
int axienet_mdio_wait_until_ready(struct axienet_local *lp) static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
{ {
u32 val; u32 val;
@ -113,21 +114,17 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
} }
/** /**
* axienet_mdio_setup - MDIO setup function * axienet_mdio_enable - MDIO hardware setup function
* @lp: Pointer to axienet local data structure. * @lp: Pointer to axienet local data structure.
* *
* Return: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when * Return: 0 on success, -ETIMEDOUT on a timeout.
* mdiobus_alloc (to allocate memory for mii bus structure) fails.
* *
* Sets up the MDIO interface by initializing the MDIO clock and enabling the * Sets up the MDIO interface by initializing the MDIO clock and enabling the
* MDIO interface in hardware. Register the MDIO interface. * MDIO interface in hardware.
**/ **/
int axienet_mdio_setup(struct axienet_local *lp) int axienet_mdio_enable(struct axienet_local *lp)
{ {
int ret;
u32 clk_div, host_clock; u32 clk_div, host_clock;
struct mii_bus *bus;
struct device_node *mdio_node;
if (lp->clk) { if (lp->clk) {
host_clock = clk_get_rate(lp->clk); host_clock = clk_get_rate(lp->clk);
@ -142,8 +139,8 @@ int axienet_mdio_setup(struct axienet_local *lp)
netdev_warn(lp->ndev, "Could not find CPU device node.\n"); netdev_warn(lp->ndev, "Could not find CPU device node.\n");
host_clock = DEFAULT_HOST_CLOCK; host_clock = DEFAULT_HOST_CLOCK;
} else { } else {
ret = of_property_read_u32(np1, "clock-frequency", int ret = of_property_read_u32(np1, "clock-frequency",
&host_clock); &host_clock);
if (ret) { if (ret) {
netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n"); netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n");
host_clock = DEFAULT_HOST_CLOCK; host_clock = DEFAULT_HOST_CLOCK;
@ -191,10 +188,39 @@ int axienet_mdio_setup(struct axienet_local *lp)
"Setting MDIO clock divisor to %u/%u Hz host clock.\n", "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
clk_div, host_clock); clk_div, host_clock);
axienet_iow(lp, XAE_MDIO_MC_OFFSET, axienet_iow(lp, XAE_MDIO_MC_OFFSET, clk_div | XAE_MDIO_MC_MDIOEN_MASK);
(((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
ret = axienet_mdio_wait_until_ready(lp); return axienet_mdio_wait_until_ready(lp);
}
/**
* axienet_mdio_disable - MDIO hardware disable function
* @lp: Pointer to axienet local data structure.
*
* Disable the MDIO interface in hardware.
**/
void axienet_mdio_disable(struct axienet_local *lp)
{
axienet_iow(lp, XAE_MDIO_MC_OFFSET, 0);
}
/**
* axienet_mdio_setup - MDIO setup function
* @lp: Pointer to axienet local data structure.
*
* Return: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
* mdiobus_alloc (to allocate memory for mii bus structure) fails.
*
* Sets up the MDIO interface by initializing the MDIO clock and enabling the
* MDIO interface in hardware. Register the MDIO interface.
**/
int axienet_mdio_setup(struct axienet_local *lp)
{
struct device_node *mdio_node;
struct mii_bus *bus;
int ret;
ret = axienet_mdio_enable(lp);
if (ret < 0) if (ret < 0)
return ret; return ret;