diff options
author | David Daney <ddaney@caviumnetworks.com> | 2010-02-15 20:13:17 (GMT) |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2010-02-27 11:53:29 (GMT) |
commit | f8c2648666b5a1b5ba9bbb662ae569bafd3cc830 (patch) | |
tree | 5c4e9806d2e05c3327a731e0bdf5651929d799b1 /drivers/staging/octeon/ethernet-rgmii.c | |
parent | 1d08f00d576c62f1c7a96900a14648df33b3939a (diff) | |
download | linux-fsl-qoriq-f8c2648666b5a1b5ba9bbb662ae569bafd3cc830.tar.xz |
Staging: Octeon: Run phy bus accesses on a workqueue.
When directly accessing a phy, we must acquire the mdio bus lock. To
do that we cannot be in interrupt context, so we need to move these
operations to a workqueue.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
To: linux-mips@linux-mips.org
To: netdev@vger.kernel.org
To: gregkh@suse.de
Patchwork: http://patchwork.linux-mips.org/patch/965/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/staging/octeon/ethernet-rgmii.c')
-rw-r--r-- | drivers/staging/octeon/ethernet-rgmii.c | 55 |
1 files changed, 42 insertions, 13 deletions
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index f90d46e..a0d4d4b 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -26,6 +26,7 @@ **********************************************************************/ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/phy.h> #include <net/dst.h> #include <asm/octeon/octeon.h> @@ -47,14 +48,20 @@ static int number_rgmii_ports; static void cvm_oct_rgmii_poll(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); - unsigned long flags; + unsigned long flags = 0; cvmx_helper_link_info_t link_info; + int use_global_register_lock = (priv->phydev == NULL); - /* - * Take the global register lock since we are going to touch - * registers that affect more than one port. - */ - spin_lock_irqsave(&global_register_lock, flags); + BUG_ON(in_interrupt()); + if (use_global_register_lock) { + /* + * Take the global register lock since we are going to + * touch registers that affect more than one port. + */ + spin_lock_irqsave(&global_register_lock, flags); + } else { + mutex_lock(&priv->phydev->bus->mdio_lock); + } link_info = cvmx_helper_link_get(priv->port); if (link_info.u64 == priv->link_info) { @@ -114,7 +121,11 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) dev->name); } } - spin_unlock_irqrestore(&global_register_lock, flags); + + if (use_global_register_lock) + spin_unlock_irqrestore(&global_register_lock, flags); + else + mutex_unlock(&priv->phydev->bus->mdio_lock); return; } @@ -150,7 +161,12 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) link_info = cvmx_helper_link_autoconf(priv->port); priv->link_info = link_info.u64; } - spin_unlock_irqrestore(&global_register_lock, flags); + + if (use_global_register_lock) + spin_unlock_irqrestore(&global_register_lock, flags); + else { + mutex_unlock(&priv->phydev->bus->mdio_lock); + } if (priv->phydev == NULL) { /* Tell core. */ @@ -212,8 +228,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id) struct net_device *dev = cvm_oct_device[cvmx_helper_get_ipd_port (interface, index)]; - if (dev) - cvm_oct_rgmii_poll(dev); + struct octeon_ethernet *priv = netdev_priv(dev); + + if (dev && !atomic_read(&cvm_oct_poll_queue_stopping)) + queue_work(cvm_oct_poll_queue, &priv->port_work); + gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; @@ -251,8 +270,11 @@ static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id) struct net_device *dev = cvm_oct_device[cvmx_helper_get_ipd_port (interface, index)]; - if (dev) - cvm_oct_rgmii_poll(dev); + struct octeon_ethernet *priv = netdev_priv(dev); + + if (dev && !atomic_read(&cvm_oct_poll_queue_stopping)) + queue_work(cvm_oct_poll_queue, &priv->port_work); + gmx_rx_int_reg.u64 = 0; gmx_rx_int_reg.s.phy_dupx = 1; gmx_rx_int_reg.s.phy_link = 1; @@ -301,6 +323,12 @@ int cvm_oct_rgmii_stop(struct net_device *dev) return 0; } +static void cvm_oct_rgmii_immediate_poll(struct work_struct *work) +{ + struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work); + cvm_oct_rgmii_poll(cvm_oct_device[priv->port]); +} + int cvm_oct_rgmii_init(struct net_device *dev) { struct octeon_ethernet *priv = netdev_priv(dev); @@ -308,7 +336,7 @@ int cvm_oct_rgmii_init(struct net_device *dev) cvm_oct_common_init(dev); dev->netdev_ops->ndo_stop(dev); - + INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll); /* * Due to GMX errata in CN3XXX series chips, it is necessary * to take the link down immediately when the PHY changes @@ -396,4 +424,5 @@ void cvm_oct_rgmii_uninit(struct net_device *dev) number_rgmii_ports--; if (number_rgmii_ports == 0) free_irq(OCTEON_IRQ_RML, &number_rgmii_ports); + cancel_work_sync(&priv->port_work); } |