diff options
Diffstat (limited to 'drivers/staging/octeon/ethernet-rgmii.c')
| -rw-r--r-- | drivers/staging/octeon/ethernet-rgmii.c | 107 |
1 files changed, 70 insertions, 37 deletions
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index 3820f1ec11d..0ec0da32821 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -26,7 +26,9 @@ **********************************************************************/ #include <linux/kernel.h> #include <linux/netdevice.h> -#include <linux/mii.h> +#include <linux/interrupt.h> +#include <linux/phy.h> +#include <linux/ratelimit.h> #include <net/dst.h> #include <asm/octeon/octeon.h> @@ -35,27 +37,33 @@ #include "octeon-ethernet.h" #include "ethernet-util.h" -#include "cvmx-helper.h" +#include <asm/octeon/cvmx-helper.h> #include <asm/octeon/cvmx-ipd-defs.h> #include <asm/octeon/cvmx-npi-defs.h> -#include "cvmx-gmxx-defs.h" +#include <asm/octeon/cvmx-gmxx-defs.h> -DEFINE_SPINLOCK(global_register_lock); +static DEFINE_SPINLOCK(global_register_lock); 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) { @@ -64,7 +72,8 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) * If the 10Mbps preamble workaround is supported and we're * at 10Mbps we may need to do some special checking. */ - if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) { + if (USE_10MBPS_PREAMBLE_WORKAROUND && + (link_info.s.speed == 10)) { /* * Read the GMXX_RXX_INT_REG[PCTERR] bit and @@ -110,12 +119,16 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) cvmx_write_csr(CVMX_GMXX_RXX_INT_REG (index, interface), gmxx_rxx_int_reg.u64); - DEBUGPRINT("%s: Using 10Mbps with software " - "preamble removal\n", - dev->name); + printk_ratelimited("%s: Using 10Mbps with software " + "preamble removal\n", + 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; } @@ -151,7 +164,11 @@ 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. */ @@ -159,23 +176,23 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) if (!netif_carrier_ok(dev)) netif_carrier_on(dev); if (priv->queue != -1) - DEBUGPRINT("%s: %u Mbps %s duplex, " - "port %2d, queue %2d\n", - dev->name, link_info.s.speed, - (link_info.s.full_duplex) ? - "Full" : "Half", - priv->port, priv->queue); + printk_ratelimited("%s: %u Mbps %s duplex, " + "port %2d, queue %2d\n", + dev->name, link_info.s.speed, + (link_info.s.full_duplex) ? + "Full" : "Half", + priv->port, priv->queue); else - DEBUGPRINT("%s: %u Mbps %s duplex, " - "port %2d, POW\n", - dev->name, link_info.s.speed, - (link_info.s.full_duplex) ? - "Full" : "Half", - priv->port); + printk_ratelimited("%s: %u Mbps %s duplex, " + "port %2d, POW\n", + dev->name, link_info.s.speed, + (link_info.s.full_duplex) ? + "Full" : "Half", + priv->port); } else { if (netif_carrier_ok(dev)) netif_carrier_off(dev); - DEBUGPRINT("%s: Link down\n", dev->name); + printk_ratelimited("%s: Link down\n", dev->name); } } } @@ -213,8 +230,13 @@ 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; @@ -252,8 +274,13 @@ 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; @@ -302,6 +329,13 @@ 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); @@ -309,7 +343,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 @@ -344,9 +378,7 @@ int cvm_oct_rgmii_init(struct net_device *dev) * Enable interrupts on inband status changes * for this port. */ - gmx_rx_int_en.u64 = - cvmx_read_csr(CVMX_GMXX_RXX_INT_EN - (index, interface)); + gmx_rx_int_en.u64 = 0; gmx_rx_int_en.s.phy_dupx = 1; gmx_rx_int_en.s.phy_link = 1; gmx_rx_int_en.s.phy_spd = 1; @@ -397,4 +429,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); } |
