Merge branch 'net-axienet-dynamically-enable-mdio-interface'

Radhey Shyam Pandey says:

====================
net: axienet: Dynamically enable MDIO interface

This patchset dynamically enable MDIO interface. The background for this
change is coming from Cadence GEM controller(macb) in which MDC is active
only during MDIO read or write operations while the PHY registers are
read or written. It is implemented as an IP feature.

For axiethernet as dynamic MDC enable/disable is not supported in hw
we are implementing it in sw. This change doesn't affect any existing
functionality.
====================

Link: https://lore.kernel.org/r/1604402770-78045-1-git-send-email-radhey.shyam.pandey@xilinx.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2020-11-07 11:13:55 -08:00
commit 0798827b47
3 changed files with 51 additions and 28 deletions

View File

@ -378,6 +378,7 @@ struct axidma_bd {
* @dev: Pointer to device structure
* @phy_node: Pointer to device node structure
* @mii_bus: Pointer to MII bus structure
* @mii_clk_div: MII bus clock divider value
* @regs_start: Resource start for axienet device addresses
* @regs: Base address for the axienet_local device address space
* @dma_regs: Base address for the axidma device address space
@ -427,6 +428,7 @@ struct axienet_local {
/* MDIO bus data */
struct mii_bus *mii_bus; /* MII bus reference */
u8 mii_clk_div; /* MII bus clock divider value */
/* IO registers, dma functions and IRQs */
resource_size_t regs_start;

View File

@ -1049,20 +1049,13 @@ static int axienet_open(struct net_device *ndev)
dev_dbg(&ndev->dev, "axienet_open()\n");
/* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. MDIO must be disabled before resetting
* and re-enabled afterwards.
/* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. MDIO must be disabled before resetting.
* Hold MDIO bus lock to avoid MDIO accesses during the reset.
*/
mutex_lock(&lp->mii_bus->mdio_lock);
axienet_mdio_disable(lp);
ret = axienet_device_reset(ndev);
if (ret == 0)
ret = axienet_mdio_enable(lp);
mutex_unlock(&lp->mii_bus->mdio_lock);
if (ret < 0)
return ret;
ret = phylink_of_phy_connect(lp->phylink, lp->dev->of_node, 0);
if (ret) {
@ -1156,9 +1149,7 @@ static int axienet_stop(struct net_device *ndev)
/* Do a reset to ensure DMA is really stopped */
mutex_lock(&lp->mii_bus->mdio_lock);
axienet_mdio_disable(lp);
__axienet_device_reset(lp);
axienet_mdio_enable(lp);
mutex_unlock(&lp->mii_bus->mdio_lock);
cancel_work_sync(&lp->dma_err_task);
@ -1669,16 +1660,12 @@ static void axienet_dma_err_handler(struct work_struct *work)
axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
/* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. MDIO must be disabled before resetting
* and re-enabled afterwards.
/* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. MDIO must be disabled before resetting.
* Hold MDIO bus lock to avoid MDIO accesses during the reset.
*/
mutex_lock(&lp->mii_bus->mdio_lock);
axienet_mdio_disable(lp);
__axienet_device_reset(lp);
axienet_mdio_enable(lp);
mutex_unlock(&lp->mii_bus->mdio_lock);
for (i = 0; i < lp->tx_bd_num; i++) {

View File

@ -30,6 +30,23 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
1, 20000);
}
/* Enable the MDIO MDC. Called prior to a read/write operation */
static void axienet_mdio_mdc_enable(struct axienet_local *lp)
{
axienet_iow(lp, XAE_MDIO_MC_OFFSET,
((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK));
}
/* Disable the MDIO MDC. Called after a read/write operation*/
static void axienet_mdio_mdc_disable(struct axienet_local *lp)
{
u32 mc_reg;
mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
axienet_iow(lp, XAE_MDIO_MC_OFFSET,
(mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK));
}
/**
* axienet_mdio_read - MDIO interface read function
* @bus: Pointer to mii bus structure
@ -48,9 +65,13 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
int ret;
struct axienet_local *lp = bus->priv;
axienet_mdio_mdc_enable(lp);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
if (ret < 0) {
axienet_mdio_mdc_disable(lp);
return ret;
}
axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
(((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
@ -61,14 +82,17 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
XAE_MDIO_MCR_OP_READ_MASK));
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
if (ret < 0) {
axienet_mdio_mdc_disable(lp);
return ret;
}
rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
phy_id, reg, rc);
axienet_mdio_mdc_disable(lp);
return rc;
}
@ -94,9 +118,13 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
phy_id, reg, val);
axienet_mdio_mdc_enable(lp);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
if (ret < 0) {
axienet_mdio_mdc_disable(lp);
return ret;
}
axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val);
axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
@ -108,8 +136,11 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
XAE_MDIO_MCR_OP_WRITE_MASK));
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
if (ret < 0) {
axienet_mdio_mdc_disable(lp);
return ret;
}
axienet_mdio_mdc_disable(lp);
return 0;
}
@ -124,7 +155,9 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
**/
int axienet_mdio_enable(struct axienet_local *lp)
{
u32 clk_div, host_clock;
u32 host_clock;
lp->mii_clk_div = 0;
if (lp->clk) {
host_clock = clk_get_rate(lp->clk);
@ -176,19 +209,19 @@ int axienet_mdio_enable(struct axienet_local *lp)
* "clock-frequency" from the CPU
*/
clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
lp->mii_clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
/* If there is any remainder from the division of
* fHOST / (MAX_MDIO_FREQ * 2), then we need to add
* 1 to the clock divisor or we will surely be above 2.5 MHz
*/
if (host_clock % (MAX_MDIO_FREQ * 2))
clk_div++;
lp->mii_clk_div++;
netdev_dbg(lp->ndev,
"Setting MDIO clock divisor to %u/%u Hz host clock.\n",
clk_div, host_clock);
lp->mii_clk_div, host_clock);
axienet_iow(lp, XAE_MDIO_MC_OFFSET, clk_div | XAE_MDIO_MC_MDIOEN_MASK);
axienet_iow(lp, XAE_MDIO_MC_OFFSET, lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK);
return axienet_mdio_wait_until_ready(lp);
}
@ -211,8 +244,8 @@ void axienet_mdio_disable(struct axienet_local *lp)
* 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.
* Sets up the MDIO interface by initializing the MDIO clock.
* Register the MDIO interface.
**/
int axienet_mdio_setup(struct axienet_local *lp)
{
@ -246,6 +279,7 @@ int axienet_mdio_setup(struct axienet_local *lp)
lp->mii_bus = NULL;
return ret;
}
axienet_mdio_mdc_disable(lp);
return 0;
}