Commit 1bd33bf0 authored by Esben Haabendal's avatar Esben Haabendal Committed by David S. Miller

net: ll_temac: Prepare indirect register access for multicast support

With .ndo_set_rx_mode/temac_set_multicast_list() being called in atomic
context (holding addr_list_lock), and temac_set_multicast_list() needing
to access temac indirect registers, the mutex used to synchronize indirect
register is a no-no.

Replace it with a spinlock, and avoid sleeping in
temac_indirect_busywait().

To avoid excessive holding of the lock, which is now a spinlock, the
temac_device_reset() function is changed to only hold the lock for short
periods.  With timeouts, it could be holding the spinlock for more than
2 seconds.
Signed-off-by: default avatarEsben Haabendal <esben@geanix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ddc0bf34
...@@ -361,7 +361,7 @@ struct temac_local { ...@@ -361,7 +361,7 @@ struct temac_local {
/* For synchronization of indirect register access. Must be /* For synchronization of indirect register access. Must be
* shared mutex between interfaces in same TEMAC block. * shared mutex between interfaces in same TEMAC block.
*/ */
struct mutex *indirect_mutex; spinlock_t *indirect_lock;
u32 options; /* Current options word */ u32 options; /* Current options word */
int last_link; int last_link;
unsigned int temac_features; unsigned int temac_features;
...@@ -388,8 +388,9 @@ struct temac_local { ...@@ -388,8 +388,9 @@ struct temac_local {
/* xilinx_temac.c */ /* xilinx_temac.c */
int temac_indirect_busywait(struct temac_local *lp); int temac_indirect_busywait(struct temac_local *lp);
u32 temac_indirect_in32(struct temac_local *lp, int reg); u32 temac_indirect_in32(struct temac_local *lp, int reg);
u32 temac_indirect_in32_locked(struct temac_local *lp, int reg);
void temac_indirect_out32(struct temac_local *lp, int reg, u32 value); void temac_indirect_out32(struct temac_local *lp, int reg, u32 value);
void temac_indirect_out32_locked(struct temac_local *lp, int reg, u32 value);
/* xilinx_temac_mdio.c */ /* xilinx_temac_mdio.c */
int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev); int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev);
......
This diff is collapsed.
...@@ -25,14 +25,15 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) ...@@ -25,14 +25,15 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{ {
struct temac_local *lp = bus->priv; struct temac_local *lp = bus->priv;
u32 rc; u32 rc;
unsigned long flags;
/* Write the PHY address to the MIIM Access Initiator register. /* Write the PHY address to the MIIM Access Initiator register.
* When the transfer completes, the PHY register value will appear * When the transfer completes, the PHY register value will appear
* in the LSW0 register */ * in the LSW0 register */
mutex_lock(lp->indirect_mutex); spin_lock_irqsave(lp->indirect_lock, flags);
temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg);
rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET);
mutex_unlock(lp->indirect_mutex); spin_unlock_irqrestore(lp->indirect_lock, flags);
dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n",
phy_id, reg, rc); phy_id, reg, rc);
...@@ -43,6 +44,7 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) ...@@ -43,6 +44,7 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
{ {
struct temac_local *lp = bus->priv; struct temac_local *lp = bus->priv;
unsigned long flags;
dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
phy_id, reg, val); phy_id, reg, val);
...@@ -50,10 +52,10 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) ...@@ -50,10 +52,10 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
/* First write the desired value into the write data register /* First write the desired value into the write data register
* and then write the address into the access initiator register * and then write the address into the access initiator register
*/ */
mutex_lock(lp->indirect_mutex); spin_lock_irqsave(lp->indirect_lock, flags);
temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, val);
temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg);
mutex_unlock(lp->indirect_mutex); spin_unlock_irqrestore(lp->indirect_lock, flags);
return 0; return 0;
} }
...@@ -87,9 +89,7 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) ...@@ -87,9 +89,7 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev)
/* Enable the MDIO bus by asserting the enable bit and writing /* Enable the MDIO bus by asserting the enable bit and writing
* in the clock config */ * in the clock config */
mutex_lock(lp->indirect_mutex);
temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div);
mutex_unlock(lp->indirect_mutex);
bus = devm_mdiobus_alloc(&pdev->dev); bus = devm_mdiobus_alloc(&pdev->dev);
if (!bus) if (!bus)
...@@ -116,10 +116,8 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) ...@@ -116,10 +116,8 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev)
if (rc) if (rc)
return rc; return rc;
mutex_lock(lp->indirect_mutex);
dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n",
temac_indirect_in32(lp, XTE_MC_OFFSET)); temac_indirect_in32(lp, XTE_MC_OFFSET));
mutex_unlock(lp->indirect_mutex);
return 0; return 0;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/spinlock.h>
struct ll_temac_platform_data { struct ll_temac_platform_data {
bool txcsum; /* Enable/disable TX checksum */ bool txcsum; /* Enable/disable TX checksum */
...@@ -21,7 +22,7 @@ struct ll_temac_platform_data { ...@@ -21,7 +22,7 @@ struct ll_temac_platform_data {
* TEMAC IP block, the same mutex should be passed here, as * TEMAC IP block, the same mutex should be passed here, as
* they share the same DCR bus bridge. * they share the same DCR bus bridge.
*/ */
struct mutex *indirect_mutex; spinlock_t *indirect_lock;
/* DMA channel control setup */ /* DMA channel control setup */
u8 tx_irq_timeout; /* TX Interrupt Delay Time-out */ u8 tx_irq_timeout; /* TX Interrupt Delay Time-out */
u8 tx_irq_count; /* TX Interrupt Coalescing Threshold Count */ u8 tx_irq_count; /* TX Interrupt Coalescing Threshold Count */
......
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