summaryrefslogtreecommitdiffstats
path: root/drivers/staging/octeon/ethernet.c
diff options
context:
space:
mode:
authorDavid Daney2010-02-15 21:13:17 +0100
committerRalf Baechle2010-02-27 12:53:29 +0100
commitf8c2648666b5a1b5ba9bbb662ae569bafd3cc830 (patch)
tree5c4e9806d2e05c3327a731e0bdf5651929d799b1 /drivers/staging/octeon/ethernet.c
parentStaging: octeon: remove unneeded includes (diff)
downloadkernel-qcow2-linux-f8c2648666b5a1b5ba9bbb662ae569bafd3cc830.tar.gz
kernel-qcow2-linux-f8c2648666b5a1b5ba9bbb662ae569bafd3cc830.tar.xz
kernel-qcow2-linux-f8c2648666b5a1b5ba9bbb662ae569bafd3cc830.zip
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.c')
-rw-r--r--drivers/staging/octeon/ethernet.c113
1 files changed, 63 insertions, 50 deletions
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 5afece0216ca..1771c1035a3c 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -123,9 +123,16 @@ MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
static unsigned int cvm_oct_mac_addr_offset;
/**
- * Periodic timer to check auto negotiation
+ * cvm_oct_poll_queue - Workqueue for polling operations.
*/
-static struct timer_list cvm_oct_poll_timer;
+struct workqueue_struct *cvm_oct_poll_queue;
+
+/**
+ * cvm_oct_poll_queue_stopping - flag to indicate polling should stop.
+ *
+ * Set to one right before cvm_oct_poll_queue is destroyed.
+ */
+atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
/**
* Array of every ethernet device owned by this driver indexed by
@@ -133,47 +140,39 @@ static struct timer_list cvm_oct_poll_timer;
*/
struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
-/**
- * Periodic timer tick for slow management operations
- *
- * @arg: Device to check
- */
-static void cvm_do_timer(unsigned long arg)
+static void cvm_oct_rx_refill_worker(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work)
{
- static int port;
- if (port < CVMX_PIP_NUM_INPUT_PORTS) {
- if (cvm_oct_device[port]) {
- struct octeon_ethernet *priv = netdev_priv(cvm_oct_device[port]);
- if (priv->poll)
- priv->poll(cvm_oct_device[port]);
- cvm_oct_free_tx_skbs(priv);
- cvm_oct_device[port]->netdev_ops->ndo_get_stats(cvm_oct_device[port]);
- }
- port++;
- /*
- * Poll the next port in a 50th of a second. This
- * spreads the polling of ports out a little bit.
- */
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ/50);
- } else {
- port = 0;
- /*
- * FPA 0 may have been drained, try to refill it if we
- * need more than num_packet_buffers / 2, otherwise
- * normal receive processing will refill it. If it
- * were drained, no packets could be received so
- * cvm_oct_napi_poll would never be invoked to do the
- * refill.
- */
- cvm_oct_rx_refill_pool(num_packet_buffers / 2);
- /*
- * All ports have been polled. Start the next iteration through
- * the ports in one second.
- */
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
- }
+ /*
+ * FPA 0 may have been drained, try to refill it if we need
+ * more than num_packet_buffers / 2, otherwise normal receive
+ * processing will refill it. If it were drained, no packets
+ * could be received so cvm_oct_napi_poll would never be
+ * invoked to do the refill.
+ */
+ cvm_oct_rx_refill_pool(num_packet_buffers / 2);
+
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue,
+ &cvm_oct_rx_refill_work, HZ);
}
+static void cvm_oct_tx_clean_worker(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work,
+ struct octeon_ethernet,
+ tx_clean_work.work);
+
+ if (priv->poll)
+ priv->poll(cvm_oct_device[priv->port]);
+ cvm_oct_free_tx_skbs(priv);
+ cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(cvm_oct_device[priv->port]);
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue, &priv->tx_clean_work, HZ);
+ }
+
/**
* Configure common hardware for all interfaces
*/
@@ -624,6 +623,12 @@ static int __init cvm_oct_init_module(void)
else
cvm_oct_mac_addr_offset = 0;
+ cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
+ if (cvm_oct_poll_queue == NULL) {
+ pr_err("octeon-ethernet: Cannot create workqueue");
+ return -ENOMEM;
+ }
+
cvm_oct_proc_initialize();
cvm_oct_configure_common_hw();
@@ -719,7 +724,9 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
priv = netdev_priv(dev);
- memset(priv, 0, sizeof(struct octeon_ethernet));
+
+ INIT_DELAYED_WORK(&priv->tx_clean_work,
+ cvm_oct_tx_clean_worker);
priv->imode = imode;
priv->port = port;
@@ -785,17 +792,15 @@ static int __init cvm_oct_init_module(void)
fau -=
cvmx_pko_get_num_queues(priv->port) *
sizeof(uint32_t);
+ queue_delayed_work(cvm_oct_poll_queue,
+ &priv->tx_clean_work, HZ);
}
}
}
cvm_oct_rx_initialize();
- /* Enable the poll timer for checking RGMII status */
- init_timer(&cvm_oct_poll_timer);
- cvm_oct_poll_timer.data = 0;
- cvm_oct_poll_timer.function = cvm_do_timer;
- mod_timer(&cvm_oct_poll_timer, jiffies + HZ);
+ queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
return 0;
}
@@ -817,20 +822,28 @@ static void __exit cvm_oct_cleanup_module(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_device);
- del_timer(&cvm_oct_poll_timer);
+ atomic_inc_return(&cvm_oct_poll_queue_stopping);
+ cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
+
cvm_oct_rx_shutdown();
cvmx_pko_disable();
/* Free the ethernet devices */
for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
if (cvm_oct_device[port]) {
- cvm_oct_tx_shutdown(cvm_oct_device[port]);
- unregister_netdev(cvm_oct_device[port]);
- kfree(cvm_oct_device[port]);
+ struct net_device *dev = cvm_oct_device[port];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cancel_delayed_work_sync(&priv->tx_clean_work);
+
+ cvm_oct_tx_shutdown(dev);
+ unregister_netdev(dev);
+ kfree(dev);
cvm_oct_device[port] = NULL;
}
}
+ destroy_workqueue(cvm_oct_poll_queue);
+
cvmx_pko_shutdown();
cvm_oct_proc_shutdown();