|
@@ -10,25 +10,6 @@
|
|
* NAPI and NETPOLL support
|
|
* NAPI and NETPOLL support
|
|
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
|
|
* (C) 2004 by Eric Lemoine (eric.lemoine@gmail.com)
|
|
*
|
|
*
|
|
- * TODO:
|
|
|
|
- * - Now that the driver was significantly simplified, I need to rework
|
|
|
|
- * the locking. I'm sure we don't need _2_ spinlocks, and we probably
|
|
|
|
- * can avoid taking most of them for so long period of time (and schedule
|
|
|
|
- * instead). The main issues at this point are caused by the netdev layer
|
|
|
|
- * though:
|
|
|
|
- *
|
|
|
|
- * gem_change_mtu() and gem_set_multicast() are called with a read_lock()
|
|
|
|
- * help by net/core/dev.c, thus they can't schedule. That means they can't
|
|
|
|
- * call napi_disable() neither, thus force gem_poll() to keep a spinlock
|
|
|
|
- * where it could have been dropped. change_mtu especially would love also to
|
|
|
|
- * be able to msleep instead of horrid locked delays when resetting the HW,
|
|
|
|
- * but that read_lock() makes it impossible, unless I defer it's action to
|
|
|
|
- * the reset task, which means it'll be asynchronous (won't take effect until
|
|
|
|
- * the system schedules a bit).
|
|
|
|
- *
|
|
|
|
- * Also, it would probably be possible to also remove most of the long-life
|
|
|
|
- * locking in open/resume code path (gem_reinit_chip) by beeing more careful
|
|
|
|
- * about when we can start taking interrupts or get xmit() called...
|
|
|
|
*/
|
|
*/
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
@@ -57,7 +38,6 @@
|
|
#include <linux/workqueue.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/bitops.h>
|
|
-#include <linux/mutex.h>
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/gfp.h>
|
|
|
|
|
|
@@ -95,12 +75,11 @@
|
|
SUPPORTED_Pause | SUPPORTED_Autoneg)
|
|
SUPPORTED_Pause | SUPPORTED_Autoneg)
|
|
|
|
|
|
#define DRV_NAME "sungem"
|
|
#define DRV_NAME "sungem"
|
|
-#define DRV_VERSION "0.98"
|
|
|
|
-#define DRV_RELDATE "8/24/03"
|
|
|
|
-#define DRV_AUTHOR "David S. Miller (davem@redhat.com)"
|
|
|
|
|
|
+#define DRV_VERSION "1.0"
|
|
|
|
+#define DRV_AUTHOR "David S. Miller <davem@redhat.com>"
|
|
|
|
|
|
static char version[] __devinitdata =
|
|
static char version[] __devinitdata =
|
|
- DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
|
|
|
|
|
|
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_AUTHOR "\n";
|
|
|
|
|
|
MODULE_AUTHOR(DRV_AUTHOR);
|
|
MODULE_AUTHOR(DRV_AUTHOR);
|
|
MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
|
|
MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
|
|
@@ -218,6 +197,7 @@ static inline void gem_disable_ints(struct gem *gp)
|
|
{
|
|
{
|
|
/* Disable all interrupts, including TXDONE */
|
|
/* Disable all interrupts, including TXDONE */
|
|
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
|
|
writel(GREG_STAT_NAPI | GREG_STAT_TXDONE, gp->regs + GREG_IMASK);
|
|
|
|
+ (void)readl(gp->regs + GREG_IMASK); /* write posting */
|
|
}
|
|
}
|
|
|
|
|
|
static void gem_get_cell(struct gem *gp)
|
|
static void gem_get_cell(struct gem *gp)
|
|
@@ -247,6 +227,29 @@ static void gem_put_cell(struct gem *gp)
|
|
#endif /* CONFIG_PPC_PMAC */
|
|
#endif /* CONFIG_PPC_PMAC */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline void gem_netif_stop(struct gem *gp)
|
|
|
|
+{
|
|
|
|
+ gp->dev->trans_start = jiffies; /* prevent tx timeout */
|
|
|
|
+ napi_disable(&gp->napi);
|
|
|
|
+ netif_tx_disable(gp->dev);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void gem_netif_start(struct gem *gp)
|
|
|
|
+{
|
|
|
|
+ /* NOTE: unconditional netif_wake_queue is only
|
|
|
|
+ * appropriate so long as all callers are assured to
|
|
|
|
+ * have free tx slots.
|
|
|
|
+ */
|
|
|
|
+ netif_wake_queue(gp->dev);
|
|
|
|
+ napi_enable(&gp->napi);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void gem_schedule_reset(struct gem *gp)
|
|
|
|
+{
|
|
|
|
+ gp->reset_task_pending = 1;
|
|
|
|
+ schedule_work(&gp->reset_task);
|
|
|
|
+}
|
|
|
|
+
|
|
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
|
|
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
|
|
{
|
|
{
|
|
if (netif_msg_intr(gp))
|
|
if (netif_msg_intr(gp))
|
|
@@ -604,56 +607,46 @@ static int gem_abnormal_irq(struct net_device *dev, struct gem *gp, u32 gem_stat
|
|
gp->dev->name);
|
|
gp->dev->name);
|
|
dev->stats.rx_errors++;
|
|
dev->stats.rx_errors++;
|
|
|
|
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_PCS) {
|
|
if (gem_status & GREG_STAT_PCS) {
|
|
if (gem_pcs_interrupt(dev, gp, gem_status))
|
|
if (gem_pcs_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_TXMAC) {
|
|
if (gem_status & GREG_STAT_TXMAC) {
|
|
if (gem_txmac_interrupt(dev, gp, gem_status))
|
|
if (gem_txmac_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_RXMAC) {
|
|
if (gem_status & GREG_STAT_RXMAC) {
|
|
if (gem_rxmac_interrupt(dev, gp, gem_status))
|
|
if (gem_rxmac_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_MAC) {
|
|
if (gem_status & GREG_STAT_MAC) {
|
|
if (gem_mac_interrupt(dev, gp, gem_status))
|
|
if (gem_mac_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_MIF) {
|
|
if (gem_status & GREG_STAT_MIF) {
|
|
if (gem_mif_interrupt(dev, gp, gem_status))
|
|
if (gem_mif_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
if (gem_status & GREG_STAT_PCIERR) {
|
|
if (gem_status & GREG_STAT_PCIERR) {
|
|
if (gem_pci_interrupt(dev, gp, gem_status))
|
|
if (gem_pci_interrupt(dev, gp, gem_status))
|
|
- goto do_reset;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
-
|
|
|
|
-do_reset:
|
|
|
|
- gp->reset_task_pending = 1;
|
|
|
|
- schedule_work(&gp->reset_task);
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_status)
|
|
{
|
|
{
|
|
int entry, limit;
|
|
int entry, limit;
|
|
|
|
|
|
- if (netif_msg_intr(gp))
|
|
|
|
- printk(KERN_DEBUG "%s: tx interrupt, gem_status: 0x%x\n",
|
|
|
|
- gp->dev->name, gem_status);
|
|
|
|
-
|
|
|
|
entry = gp->tx_old;
|
|
entry = gp->tx_old;
|
|
limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT);
|
|
limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT);
|
|
while (entry != limit) {
|
|
while (entry != limit) {
|
|
@@ -697,13 +690,27 @@ static __inline__ void gem_tx(struct net_device *dev, struct gem *gp, u32 gem_st
|
|
}
|
|
}
|
|
|
|
|
|
dev->stats.tx_packets++;
|
|
dev->stats.tx_packets++;
|
|
- dev_kfree_skb_irq(skb);
|
|
|
|
|
|
+ dev_kfree_skb(skb);
|
|
}
|
|
}
|
|
gp->tx_old = entry;
|
|
gp->tx_old = entry;
|
|
|
|
|
|
- if (netif_queue_stopped(dev) &&
|
|
|
|
- TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
|
|
|
|
- netif_wake_queue(dev);
|
|
|
|
|
|
+ /* Need to make the tx_old update visible to gem_start_xmit()
|
|
|
|
+ * before checking for netif_queue_stopped(). Without the
|
|
|
|
+ * memory barrier, there is a small possibility that gem_start_xmit()
|
|
|
|
+ * will miss it and cause the queue to be stopped forever.
|
|
|
|
+ */
|
|
|
|
+ smp_mb();
|
|
|
|
+
|
|
|
|
+ if (unlikely(netif_queue_stopped(dev) &&
|
|
|
|
+ TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))) {
|
|
|
|
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
|
|
|
|
+
|
|
|
|
+ __netif_tx_lock(txq, smp_processor_id());
|
|
|
|
+ if (netif_queue_stopped(dev) &&
|
|
|
|
+ TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
|
|
|
|
+ netif_wake_queue(dev);
|
|
|
|
+ __netif_tx_unlock(txq);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static __inline__ void gem_post_rxds(struct gem *gp, int limit)
|
|
static __inline__ void gem_post_rxds(struct gem *gp, int limit)
|
|
@@ -736,6 +743,21 @@ static __inline__ void gem_post_rxds(struct gem *gp, int limit)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define ALIGNED_RX_SKB_ADDR(addr) \
|
|
|
|
+ ((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
|
|
|
|
+static __inline__ struct sk_buff *gem_alloc_skb(struct net_device *dev, int size,
|
|
|
|
+ gfp_t gfp_flags)
|
|
|
|
+{
|
|
|
|
+ struct sk_buff *skb = alloc_skb(size + 64, gfp_flags);
|
|
|
|
+
|
|
|
|
+ if (likely(skb)) {
|
|
|
|
+ unsigned long offset = ALIGNED_RX_SKB_ADDR(skb->data);
|
|
|
|
+ skb_reserve(skb, offset);
|
|
|
|
+ skb->dev = dev;
|
|
|
|
+ }
|
|
|
|
+ return skb;
|
|
|
|
+}
|
|
|
|
+
|
|
static int gem_rx(struct gem *gp, int work_to_do)
|
|
static int gem_rx(struct gem *gp, int work_to_do)
|
|
{
|
|
{
|
|
struct net_device *dev = gp->dev;
|
|
struct net_device *dev = gp->dev;
|
|
@@ -799,7 +821,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
|
|
if (len > RX_COPY_THRESHOLD) {
|
|
if (len > RX_COPY_THRESHOLD) {
|
|
struct sk_buff *new_skb;
|
|
struct sk_buff *new_skb;
|
|
|
|
|
|
- new_skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
|
|
|
|
|
|
+ new_skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
|
|
if (new_skb == NULL) {
|
|
if (new_skb == NULL) {
|
|
drops++;
|
|
drops++;
|
|
goto drop_it;
|
|
goto drop_it;
|
|
@@ -808,7 +830,6 @@ static int gem_rx(struct gem *gp, int work_to_do)
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
RX_BUF_ALLOC_SIZE(gp),
|
|
PCI_DMA_FROMDEVICE);
|
|
PCI_DMA_FROMDEVICE);
|
|
gp->rx_skbs[entry] = new_skb;
|
|
gp->rx_skbs[entry] = new_skb;
|
|
- new_skb->dev = gp->dev;
|
|
|
|
skb_put(new_skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
skb_put(new_skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
rxd->buffer = cpu_to_le64(pci_map_page(gp->pdev,
|
|
rxd->buffer = cpu_to_le64(pci_map_page(gp->pdev,
|
|
virt_to_page(new_skb->data),
|
|
virt_to_page(new_skb->data),
|
|
@@ -820,7 +841,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
|
|
/* Trim the original skb for the netif. */
|
|
/* Trim the original skb for the netif. */
|
|
skb_trim(skb, len);
|
|
skb_trim(skb, len);
|
|
} else {
|
|
} else {
|
|
- struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
|
|
|
|
|
|
+ struct sk_buff *copy_skb = netdev_alloc_skb(dev, len + 2);
|
|
|
|
|
|
if (copy_skb == NULL) {
|
|
if (copy_skb == NULL) {
|
|
drops++;
|
|
drops++;
|
|
@@ -842,7 +863,7 @@ static int gem_rx(struct gem *gp, int work_to_do)
|
|
skb->ip_summed = CHECKSUM_COMPLETE;
|
|
skb->ip_summed = CHECKSUM_COMPLETE;
|
|
skb->protocol = eth_type_trans(skb, gp->dev);
|
|
skb->protocol = eth_type_trans(skb, gp->dev);
|
|
|
|
|
|
- netif_receive_skb(skb);
|
|
|
|
|
|
+ napi_gro_receive(&gp->napi, skb);
|
|
|
|
|
|
dev->stats.rx_packets++;
|
|
dev->stats.rx_packets++;
|
|
dev->stats.rx_bytes += len;
|
|
dev->stats.rx_bytes += len;
|
|
@@ -865,28 +886,32 @@ static int gem_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
{
|
|
struct gem *gp = container_of(napi, struct gem, napi);
|
|
struct gem *gp = container_of(napi, struct gem, napi);
|
|
struct net_device *dev = gp->dev;
|
|
struct net_device *dev = gp->dev;
|
|
- unsigned long flags;
|
|
|
|
int work_done;
|
|
int work_done;
|
|
|
|
|
|
- /*
|
|
|
|
- * NAPI locking nightmare: See comment at head of driver
|
|
|
|
- */
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
work_done = 0;
|
|
work_done = 0;
|
|
do {
|
|
do {
|
|
/* Handle anomalies */
|
|
/* Handle anomalies */
|
|
- if (gp->status & GREG_STAT_ABNORMAL) {
|
|
|
|
- if (gem_abnormal_irq(dev, gp, gp->status))
|
|
|
|
- break;
|
|
|
|
|
|
+ if (unlikely(gp->status & GREG_STAT_ABNORMAL)) {
|
|
|
|
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
|
|
|
|
+ int reset;
|
|
|
|
+
|
|
|
|
+ /* We run the abnormal interrupt handling code with
|
|
|
|
+ * the Tx lock. It only resets the Rx portion of the
|
|
|
|
+ * chip, but we need to guard it against DMA being
|
|
|
|
+ * restarted by the link poll timer
|
|
|
|
+ */
|
|
|
|
+ __netif_tx_lock(txq, smp_processor_id());
|
|
|
|
+ reset = gem_abnormal_irq(dev, gp, gp->status);
|
|
|
|
+ __netif_tx_unlock(txq);
|
|
|
|
+ if (reset) {
|
|
|
|
+ gem_schedule_reset(gp);
|
|
|
|
+ napi_complete(napi);
|
|
|
|
+ return work_done;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/* Run TX completion thread */
|
|
/* Run TX completion thread */
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
gem_tx(dev, gp, gp->status);
|
|
gem_tx(dev, gp, gp->status);
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
-
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
|
|
|
|
/* Run RX thread. We don't use any locking here,
|
|
/* Run RX thread. We don't use any locking here,
|
|
* code willing to do bad things - like cleaning the
|
|
* code willing to do bad things - like cleaning the
|
|
@@ -898,16 +923,12 @@ static int gem_poll(struct napi_struct *napi, int budget)
|
|
if (work_done >= budget)
|
|
if (work_done >= budget)
|
|
return work_done;
|
|
return work_done;
|
|
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
gp->status = readl(gp->regs + GREG_STAT);
|
|
gp->status = readl(gp->regs + GREG_STAT);
|
|
} while (gp->status & GREG_STAT_NAPI);
|
|
} while (gp->status & GREG_STAT_NAPI);
|
|
|
|
|
|
- __napi_complete(napi);
|
|
|
|
|
|
+ napi_complete(napi);
|
|
gem_enable_ints(gp);
|
|
gem_enable_ints(gp);
|
|
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
return work_done;
|
|
return work_done;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -915,32 +936,23 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
|
|
{
|
|
{
|
|
struct net_device *dev = dev_id;
|
|
struct net_device *dev = dev_id;
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- /* Swallow interrupts when shutting the chip down, though
|
|
|
|
- * that shouldn't happen, we should have done free_irq() at
|
|
|
|
- * this point...
|
|
|
|
- */
|
|
|
|
- if (!gp->running)
|
|
|
|
- return IRQ_HANDLED;
|
|
|
|
-
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
|
|
|
|
if (napi_schedule_prep(&gp->napi)) {
|
|
if (napi_schedule_prep(&gp->napi)) {
|
|
u32 gem_status = readl(gp->regs + GREG_STAT);
|
|
u32 gem_status = readl(gp->regs + GREG_STAT);
|
|
|
|
|
|
- if (gem_status == 0) {
|
|
|
|
|
|
+ if (unlikely(gem_status == 0)) {
|
|
napi_enable(&gp->napi);
|
|
napi_enable(&gp->napi);
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
return IRQ_NONE;
|
|
return IRQ_NONE;
|
|
}
|
|
}
|
|
|
|
+ if (netif_msg_intr(gp))
|
|
|
|
+ printk(KERN_DEBUG "%s: gem_interrupt() gem_status: 0x%x\n",
|
|
|
|
+ gp->dev->name, gem_status);
|
|
|
|
+
|
|
gp->status = gem_status;
|
|
gp->status = gem_status;
|
|
gem_disable_ints(gp);
|
|
gem_disable_ints(gp);
|
|
__napi_schedule(&gp->napi);
|
|
__napi_schedule(&gp->napi);
|
|
}
|
|
}
|
|
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
/* If polling was disabled at the time we received that
|
|
/* If polling was disabled at the time we received that
|
|
* interrupt, we may return IRQ_HANDLED here while we
|
|
* interrupt, we may return IRQ_HANDLED here while we
|
|
* should return IRQ_NONE. No big deal...
|
|
* should return IRQ_NONE. No big deal...
|
|
@@ -951,10 +963,11 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
static void gem_poll_controller(struct net_device *dev)
|
|
static void gem_poll_controller(struct net_device *dev)
|
|
{
|
|
{
|
|
- /* gem_interrupt is safe to reentrance so no need
|
|
|
|
- * to disable_irq here.
|
|
|
|
- */
|
|
|
|
- gem_interrupt(dev->irq, dev);
|
|
|
|
|
|
+ struct gem *gp = netdev_priv(dev);
|
|
|
|
+
|
|
|
|
+ disable_irq(gp->pdev->irq);
|
|
|
|
+ gem_interrupt(gp->pdev->irq, dev);
|
|
|
|
+ enable_irq(gp->pdev->irq);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -963,10 +976,7 @@ static void gem_tx_timeout(struct net_device *dev)
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
|
|
|
|
netdev_err(dev, "transmit timed out, resetting\n");
|
|
netdev_err(dev, "transmit timed out, resetting\n");
|
|
- if (!gp->running) {
|
|
|
|
- netdev_err(dev, "hrm.. hw not running !\n");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
netdev_err(dev, "TX_STATE[%08x:%08x:%08x]\n",
|
|
netdev_err(dev, "TX_STATE[%08x:%08x:%08x]\n",
|
|
readl(gp->regs + TXDMA_CFG),
|
|
readl(gp->regs + TXDMA_CFG),
|
|
readl(gp->regs + MAC_TXSTAT),
|
|
readl(gp->regs + MAC_TXSTAT),
|
|
@@ -976,14 +986,7 @@ static void gem_tx_timeout(struct net_device *dev)
|
|
readl(gp->regs + MAC_RXSTAT),
|
|
readl(gp->regs + MAC_RXSTAT),
|
|
readl(gp->regs + MAC_RXCFG));
|
|
readl(gp->regs + MAC_RXCFG));
|
|
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
-
|
|
|
|
- gp->reset_task_pending = 1;
|
|
|
|
- schedule_work(&gp->reset_task);
|
|
|
|
-
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ gem_schedule_reset(gp);
|
|
}
|
|
}
|
|
|
|
|
|
static __inline__ int gem_intme(int entry)
|
|
static __inline__ int gem_intme(int entry)
|
|
@@ -1001,7 +1004,6 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
int entry;
|
|
int entry;
|
|
u64 ctrl;
|
|
u64 ctrl;
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
ctrl = 0;
|
|
ctrl = 0;
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
@@ -1013,21 +1015,12 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
|
|
(csum_stuff_off << 21));
|
|
(csum_stuff_off << 21));
|
|
}
|
|
}
|
|
|
|
|
|
- if (!spin_trylock_irqsave(&gp->tx_lock, flags)) {
|
|
|
|
- /* Tell upper layer to requeue */
|
|
|
|
- return NETDEV_TX_LOCKED;
|
|
|
|
- }
|
|
|
|
- /* We raced with gem_do_stop() */
|
|
|
|
- if (!gp->running) {
|
|
|
|
- spin_unlock_irqrestore(&gp->tx_lock, flags);
|
|
|
|
- return NETDEV_TX_BUSY;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* This is a hard error, log it. */
|
|
|
|
- if (TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1)) {
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
- spin_unlock_irqrestore(&gp->tx_lock, flags);
|
|
|
|
- netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
|
|
|
|
|
|
+ if (unlikely(TX_BUFFS_AVAIL(gp) <= (skb_shinfo(skb)->nr_frags + 1))) {
|
|
|
|
+ /* This is a hard error, log it. */
|
|
|
|
+ if (!netif_queue_stopped(dev)) {
|
|
|
|
+ netif_stop_queue(dev);
|
|
|
|
+ netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
|
|
|
|
+ }
|
|
return NETDEV_TX_BUSY;
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1104,17 +1097,23 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
|
|
}
|
|
}
|
|
|
|
|
|
gp->tx_new = entry;
|
|
gp->tx_new = entry;
|
|
- if (TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))
|
|
|
|
|
|
+ if (unlikely(TX_BUFFS_AVAIL(gp) <= (MAX_SKB_FRAGS + 1))) {
|
|
netif_stop_queue(dev);
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
+ /* netif_stop_queue() must be done before checking
|
|
|
|
+ * checking tx index in TX_BUFFS_AVAIL() below, because
|
|
|
|
+ * in gem_tx(), we update tx_old before checking for
|
|
|
|
+ * netif_queue_stopped().
|
|
|
|
+ */
|
|
|
|
+ smp_mb();
|
|
|
|
+ if (TX_BUFFS_AVAIL(gp) > (MAX_SKB_FRAGS + 1))
|
|
|
|
+ netif_wake_queue(dev);
|
|
|
|
+ }
|
|
if (netif_msg_tx_queued(gp))
|
|
if (netif_msg_tx_queued(gp))
|
|
printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
|
|
printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
|
|
dev->name, entry, skb->len);
|
|
dev->name, entry, skb->len);
|
|
mb();
|
|
mb();
|
|
writel(gp->tx_new, gp->regs + TXDMA_KICK);
|
|
writel(gp->tx_new, gp->regs + TXDMA_KICK);
|
|
- spin_unlock_irqrestore(&gp->tx_lock, flags);
|
|
|
|
-
|
|
|
|
- dev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */
|
|
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
return NETDEV_TX_OK;
|
|
}
|
|
}
|
|
@@ -1184,7 +1183,6 @@ static void gem_pcs_reinit_adv(struct gem *gp)
|
|
|
|
|
|
#define STOP_TRIES 32
|
|
#define STOP_TRIES 32
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_reset(struct gem *gp)
|
|
static void gem_reset(struct gem *gp)
|
|
{
|
|
{
|
|
int limit;
|
|
int limit;
|
|
@@ -1213,7 +1211,6 @@ static void gem_reset(struct gem *gp)
|
|
gem_pcs_reinit_adv(gp);
|
|
gem_pcs_reinit_adv(gp);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_start_dma(struct gem *gp)
|
|
static void gem_start_dma(struct gem *gp)
|
|
{
|
|
{
|
|
u32 val;
|
|
u32 val;
|
|
@@ -1236,8 +1233,7 @@ static void gem_start_dma(struct gem *gp)
|
|
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
|
|
writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. DMA won't be
|
|
|
|
- * actually stopped before about 4ms tho ...
|
|
|
|
|
|
+/* DMA won't be actually stopped before about 4ms tho ...
|
|
*/
|
|
*/
|
|
static void gem_stop_dma(struct gem *gp)
|
|
static void gem_stop_dma(struct gem *gp)
|
|
{
|
|
{
|
|
@@ -1259,7 +1255,6 @@ static void gem_stop_dma(struct gem *gp)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
// XXX dbl check what that function should do when called on PCS PHY
|
|
// XXX dbl check what that function should do when called on PCS PHY
|
|
static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
|
|
static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
|
|
{
|
|
{
|
|
@@ -1319,7 +1314,7 @@ start_aneg:
|
|
/* If we are asleep, we don't try to actually setup the PHY, we
|
|
/* If we are asleep, we don't try to actually setup the PHY, we
|
|
* just store the settings
|
|
* just store the settings
|
|
*/
|
|
*/
|
|
- if (gp->asleep) {
|
|
|
|
|
|
+ if (!netif_device_present(gp->dev)) {
|
|
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
|
|
gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
|
|
gp->phy_mii.speed = speed;
|
|
gp->phy_mii.speed = speed;
|
|
gp->phy_mii.duplex = duplex;
|
|
gp->phy_mii.duplex = duplex;
|
|
@@ -1345,13 +1340,12 @@ non_mii:
|
|
|
|
|
|
/* A link-up condition has occurred, initialize and enable the
|
|
/* A link-up condition has occurred, initialize and enable the
|
|
* rest of the chip.
|
|
* rest of the chip.
|
|
- *
|
|
|
|
- * Must be invoked under gp->lock and gp->tx_lock.
|
|
|
|
*/
|
|
*/
|
|
static int gem_set_link_modes(struct gem *gp)
|
|
static int gem_set_link_modes(struct gem *gp)
|
|
{
|
|
{
|
|
- u32 val;
|
|
|
|
|
|
+ struct netdev_queue *txq = netdev_get_tx_queue(gp->dev, 0);
|
|
int full_duplex, speed, pause;
|
|
int full_duplex, speed, pause;
|
|
|
|
+ u32 val;
|
|
|
|
|
|
full_duplex = 0;
|
|
full_duplex = 0;
|
|
speed = SPEED_10;
|
|
speed = SPEED_10;
|
|
@@ -1375,8 +1369,11 @@ static int gem_set_link_modes(struct gem *gp)
|
|
netif_info(gp, link, gp->dev, "Link is up at %d Mbps, %s-duplex\n",
|
|
netif_info(gp, link, gp->dev, "Link is up at %d Mbps, %s-duplex\n",
|
|
speed, (full_duplex ? "full" : "half"));
|
|
speed, (full_duplex ? "full" : "half"));
|
|
|
|
|
|
- if (!gp->running)
|
|
|
|
- return 0;
|
|
|
|
|
|
+
|
|
|
|
+ /* We take the tx queue lock to avoid collisions between
|
|
|
|
+ * this code, the tx path and the NAPI-driven error path
|
|
|
|
+ */
|
|
|
|
+ __netif_tx_lock(txq, smp_processor_id());
|
|
|
|
|
|
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
|
|
val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
|
|
if (full_duplex) {
|
|
if (full_duplex) {
|
|
@@ -1425,18 +1422,6 @@ static int gem_set_link_modes(struct gem *gp)
|
|
pause = 1;
|
|
pause = 1;
|
|
}
|
|
}
|
|
|
|
|
|
- if (netif_msg_link(gp)) {
|
|
|
|
- if (pause) {
|
|
|
|
- netdev_info(gp->dev,
|
|
|
|
- "Pause is enabled (rxfifo: %d off: %d on: %d)\n",
|
|
|
|
- gp->rx_fifo_sz,
|
|
|
|
- gp->rx_pause_off,
|
|
|
|
- gp->rx_pause_on);
|
|
|
|
- } else {
|
|
|
|
- netdev_info(gp->dev, "Pause is disabled\n");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (!full_duplex)
|
|
if (!full_duplex)
|
|
writel(512, gp->regs + MAC_STIME);
|
|
writel(512, gp->regs + MAC_STIME);
|
|
else
|
|
else
|
|
@@ -1450,10 +1435,23 @@ static int gem_set_link_modes(struct gem *gp)
|
|
|
|
|
|
gem_start_dma(gp);
|
|
gem_start_dma(gp);
|
|
|
|
|
|
|
|
+ __netif_tx_unlock(txq);
|
|
|
|
+
|
|
|
|
+ if (netif_msg_link(gp)) {
|
|
|
|
+ if (pause) {
|
|
|
|
+ netdev_info(gp->dev,
|
|
|
|
+ "Pause is enabled (rxfifo: %d off: %d on: %d)\n",
|
|
|
|
+ gp->rx_fifo_sz,
|
|
|
|
+ gp->rx_pause_off,
|
|
|
|
+ gp->rx_pause_on);
|
|
|
|
+ } else {
|
|
|
|
+ netdev_info(gp->dev, "Pause is disabled\n");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static int gem_mdio_link_not_up(struct gem *gp)
|
|
static int gem_mdio_link_not_up(struct gem *gp)
|
|
{
|
|
{
|
|
switch (gp->lstate) {
|
|
switch (gp->lstate) {
|
|
@@ -1501,20 +1499,12 @@ static int gem_mdio_link_not_up(struct gem *gp)
|
|
static void gem_link_timer(unsigned long data)
|
|
static void gem_link_timer(unsigned long data)
|
|
{
|
|
{
|
|
struct gem *gp = (struct gem *) data;
|
|
struct gem *gp = (struct gem *) data;
|
|
|
|
+ struct net_device *dev = gp->dev;
|
|
int restart_aneg = 0;
|
|
int restart_aneg = 0;
|
|
|
|
|
|
- if (gp->asleep)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
-
|
|
|
|
- /* If the reset task is still pending, we just
|
|
|
|
- * reschedule the link timer
|
|
|
|
- */
|
|
|
|
|
|
+ /* There's no point doing anything if we're going to be reset */
|
|
if (gp->reset_task_pending)
|
|
if (gp->reset_task_pending)
|
|
- goto restart;
|
|
|
|
|
|
+ return;
|
|
|
|
|
|
if (gp->phy_type == phy_serialink ||
|
|
if (gp->phy_type == phy_serialink ||
|
|
gp->phy_type == phy_serdes) {
|
|
gp->phy_type == phy_serdes) {
|
|
@@ -1528,7 +1518,7 @@ static void gem_link_timer(unsigned long data)
|
|
goto restart;
|
|
goto restart;
|
|
|
|
|
|
gp->lstate = link_up;
|
|
gp->lstate = link_up;
|
|
- netif_carrier_on(gp->dev);
|
|
|
|
|
|
+ netif_carrier_on(dev);
|
|
(void)gem_set_link_modes(gp);
|
|
(void)gem_set_link_modes(gp);
|
|
}
|
|
}
|
|
goto restart;
|
|
goto restart;
|
|
@@ -1544,12 +1534,12 @@ static void gem_link_timer(unsigned long data)
|
|
gp->last_forced_speed = gp->phy_mii.speed;
|
|
gp->last_forced_speed = gp->phy_mii.speed;
|
|
gp->timer_ticks = 5;
|
|
gp->timer_ticks = 5;
|
|
if (netif_msg_link(gp))
|
|
if (netif_msg_link(gp))
|
|
- netdev_info(gp->dev,
|
|
|
|
|
|
+ netdev_info(dev,
|
|
"Got link after fallback, retrying autoneg once...\n");
|
|
"Got link after fallback, retrying autoneg once...\n");
|
|
gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising);
|
|
gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising);
|
|
} else if (gp->lstate != link_up) {
|
|
} else if (gp->lstate != link_up) {
|
|
gp->lstate = link_up;
|
|
gp->lstate = link_up;
|
|
- netif_carrier_on(gp->dev);
|
|
|
|
|
|
+ netif_carrier_on(dev);
|
|
if (gem_set_link_modes(gp))
|
|
if (gem_set_link_modes(gp))
|
|
restart_aneg = 1;
|
|
restart_aneg = 1;
|
|
}
|
|
}
|
|
@@ -1559,11 +1549,11 @@ static void gem_link_timer(unsigned long data)
|
|
*/
|
|
*/
|
|
if (gp->lstate == link_up) {
|
|
if (gp->lstate == link_up) {
|
|
gp->lstate = link_down;
|
|
gp->lstate = link_down;
|
|
- netif_info(gp, link, gp->dev, "Link down\n");
|
|
|
|
- netif_carrier_off(gp->dev);
|
|
|
|
- gp->reset_task_pending = 1;
|
|
|
|
- schedule_work(&gp->reset_task);
|
|
|
|
- restart_aneg = 1;
|
|
|
|
|
|
+ netif_info(gp, link, dev, "Link down\n");
|
|
|
|
+ netif_carrier_off(dev);
|
|
|
|
+ gem_schedule_reset(gp);
|
|
|
|
+ /* The reset task will restart the timer */
|
|
|
|
+ return;
|
|
} else if (++gp->timer_ticks > 10) {
|
|
} else if (++gp->timer_ticks > 10) {
|
|
if (found_mii_phy(gp))
|
|
if (found_mii_phy(gp))
|
|
restart_aneg = gem_mdio_link_not_up(gp);
|
|
restart_aneg = gem_mdio_link_not_up(gp);
|
|
@@ -1573,17 +1563,12 @@ static void gem_link_timer(unsigned long data)
|
|
}
|
|
}
|
|
if (restart_aneg) {
|
|
if (restart_aneg) {
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
- goto out_unlock;
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
restart:
|
|
restart:
|
|
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
|
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
|
-out_unlock:
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_clean_rings(struct gem *gp)
|
|
static void gem_clean_rings(struct gem *gp)
|
|
{
|
|
{
|
|
struct gem_init_block *gb = gp->init_block;
|
|
struct gem_init_block *gb = gp->init_block;
|
|
@@ -1634,7 +1619,6 @@ static void gem_clean_rings(struct gem *gp)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_init_rings(struct gem *gp)
|
|
static void gem_init_rings(struct gem *gp)
|
|
{
|
|
{
|
|
struct gem_init_block *gb = gp->init_block;
|
|
struct gem_init_block *gb = gp->init_block;
|
|
@@ -1653,7 +1637,7 @@ static void gem_init_rings(struct gem *gp)
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct gem_rxd *rxd = &gb->rxd[i];
|
|
struct gem_rxd *rxd = &gb->rxd[i];
|
|
|
|
|
|
- skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
|
|
|
|
|
|
+ skb = gem_alloc_skb(dev, RX_BUF_ALLOC_SIZE(gp), GFP_KERNEL);
|
|
if (!skb) {
|
|
if (!skb) {
|
|
rxd->buffer = 0;
|
|
rxd->buffer = 0;
|
|
rxd->status_word = 0;
|
|
rxd->status_word = 0;
|
|
@@ -1661,7 +1645,6 @@ static void gem_init_rings(struct gem *gp)
|
|
}
|
|
}
|
|
|
|
|
|
gp->rx_skbs[i] = skb;
|
|
gp->rx_skbs[i] = skb;
|
|
- skb->dev = dev;
|
|
|
|
skb_put(skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
skb_put(skb, (gp->rx_buf_sz + RX_OFFSET));
|
|
dma_addr = pci_map_page(gp->pdev,
|
|
dma_addr = pci_map_page(gp->pdev,
|
|
virt_to_page(skb->data),
|
|
virt_to_page(skb->data),
|
|
@@ -1737,7 +1720,7 @@ static void gem_init_phy(struct gem *gp)
|
|
|
|
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
if (gp->phy_type == phy_mii_mdio0 ||
|
|
gp->phy_type == phy_mii_mdio1) {
|
|
gp->phy_type == phy_mii_mdio1) {
|
|
- // XXX check for errors
|
|
|
|
|
|
+ /* Reset and detect MII PHY */
|
|
mii_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
|
|
mii_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
|
|
|
|
|
|
/* Init PHY */
|
|
/* Init PHY */
|
|
@@ -1753,13 +1736,15 @@ static void gem_init_phy(struct gem *gp)
|
|
gp->lstate = link_down;
|
|
gp->lstate = link_down;
|
|
netif_carrier_off(gp->dev);
|
|
netif_carrier_off(gp->dev);
|
|
|
|
|
|
- /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
|
|
+ /* Print things out */
|
|
|
|
+ if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
+ gp->phy_type == phy_mii_mdio1)
|
|
|
|
+ netdev_info(gp->dev, "Found %s PHY\n",
|
|
|
|
+ gp->phy_mii.def ? gp->phy_mii.def->name : "no");
|
|
|
|
+
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
gem_begin_auto_negotiation(gp, NULL);
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_init_dma(struct gem *gp)
|
|
static void gem_init_dma(struct gem *gp)
|
|
{
|
|
{
|
|
u64 desc_dma = (u64) gp->gblock_dvma;
|
|
u64 desc_dma = (u64) gp->gblock_dvma;
|
|
@@ -1797,7 +1782,6 @@ static void gem_init_dma(struct gem *gp)
|
|
gp->regs + RXDMA_BLANK);
|
|
gp->regs + RXDMA_BLANK);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static u32 gem_setup_multicast(struct gem *gp)
|
|
static u32 gem_setup_multicast(struct gem *gp)
|
|
{
|
|
{
|
|
u32 rxcfg = 0;
|
|
u32 rxcfg = 0;
|
|
@@ -1835,7 +1819,6 @@ static u32 gem_setup_multicast(struct gem *gp)
|
|
return rxcfg;
|
|
return rxcfg;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_init_mac(struct gem *gp)
|
|
static void gem_init_mac(struct gem *gp)
|
|
{
|
|
{
|
|
unsigned char *e = &gp->dev->dev_addr[0];
|
|
unsigned char *e = &gp->dev->dev_addr[0];
|
|
@@ -1918,7 +1901,6 @@ static void gem_init_mac(struct gem *gp)
|
|
writel(0, gp->regs + WOL_WAKECSR);
|
|
writel(0, gp->regs + WOL_WAKECSR);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_init_pause_thresholds(struct gem *gp)
|
|
static void gem_init_pause_thresholds(struct gem *gp)
|
|
{
|
|
{
|
|
u32 cfg;
|
|
u32 cfg;
|
|
@@ -2079,7 +2061,6 @@ static int gem_check_invariants(struct gem *gp)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Must be invoked under gp->lock and gp->tx_lock. */
|
|
|
|
static void gem_reinit_chip(struct gem *gp)
|
|
static void gem_reinit_chip(struct gem *gp)
|
|
{
|
|
{
|
|
/* Reset the chip */
|
|
/* Reset the chip */
|
|
@@ -2100,11 +2081,9 @@ static void gem_reinit_chip(struct gem *gp)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-/* Must be invoked with no lock held. */
|
|
|
|
static void gem_stop_phy(struct gem *gp, int wol)
|
|
static void gem_stop_phy(struct gem *gp, int wol)
|
|
{
|
|
{
|
|
u32 mifcfg;
|
|
u32 mifcfg;
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
/* Let the chip settle down a bit, it seems that helps
|
|
/* Let the chip settle down a bit, it seems that helps
|
|
* for sleep mode on some models
|
|
* for sleep mode on some models
|
|
@@ -2150,15 +2129,9 @@ static void gem_stop_phy(struct gem *gp, int wol)
|
|
writel(0, gp->regs + RXDMA_CFG);
|
|
writel(0, gp->regs + RXDMA_CFG);
|
|
|
|
|
|
if (!wol) {
|
|
if (!wol) {
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
gem_reset(gp);
|
|
gem_reset(gp);
|
|
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
|
|
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
|
|
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
|
|
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
- /* No need to take the lock here */
|
|
|
|
|
|
|
|
if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
|
|
if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
|
|
gp->phy_mii.def->ops->suspend(&gp->phy_mii);
|
|
gp->phy_mii.def->ops->suspend(&gp->phy_mii);
|
|
@@ -2175,54 +2148,55 @@ static void gem_stop_phy(struct gem *gp, int wol)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
static int gem_do_start(struct net_device *dev)
|
|
static int gem_do_start(struct net_device *dev)
|
|
{
|
|
{
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
|
|
+ int rc;
|
|
|
|
|
|
/* Enable the cell */
|
|
/* Enable the cell */
|
|
gem_get_cell(gp);
|
|
gem_get_cell(gp);
|
|
|
|
|
|
- /* Init & setup chip hardware */
|
|
|
|
- gem_reinit_chip(gp);
|
|
|
|
-
|
|
|
|
- gp->running = 1;
|
|
|
|
-
|
|
|
|
- napi_enable(&gp->napi);
|
|
|
|
|
|
+ /* Make sure PCI access and bus master are enabled */
|
|
|
|
+ rc = pci_enable_device(gp->pdev);
|
|
|
|
+ if (rc) {
|
|
|
|
+ netdev_err(dev, "Failed to enable chip on PCI bus !\n");
|
|
|
|
|
|
- if (gp->lstate == link_up) {
|
|
|
|
- netif_carrier_on(gp->dev);
|
|
|
|
- gem_set_link_modes(gp);
|
|
|
|
|
|
+ /* Put cell and forget it for now, it will be considered as
|
|
|
|
+ * still asleep, a new sleep cycle may bring it back
|
|
|
|
+ */
|
|
|
|
+ gem_put_cell(gp);
|
|
|
|
+ return -ENXIO;
|
|
}
|
|
}
|
|
|
|
+ pci_set_master(gp->pdev);
|
|
|
|
|
|
- netif_wake_queue(gp->dev);
|
|
|
|
-
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
|
|
+ /* Init & setup chip hardware */
|
|
|
|
+ gem_reinit_chip(gp);
|
|
|
|
|
|
- if (request_irq(gp->pdev->irq, gem_interrupt,
|
|
|
|
- IRQF_SHARED, dev->name, (void *)dev)) {
|
|
|
|
|
|
+ /* An interrupt might come in handy */
|
|
|
|
+ rc = request_irq(gp->pdev->irq, gem_interrupt,
|
|
|
|
+ IRQF_SHARED, dev->name, (void *)dev);
|
|
|
|
+ if (rc) {
|
|
netdev_err(dev, "failed to request irq !\n");
|
|
netdev_err(dev, "failed to request irq !\n");
|
|
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
-
|
|
|
|
- napi_disable(&gp->napi);
|
|
|
|
-
|
|
|
|
- gp->running = 0;
|
|
|
|
gem_reset(gp);
|
|
gem_reset(gp);
|
|
gem_clean_rings(gp);
|
|
gem_clean_rings(gp);
|
|
gem_put_cell(gp);
|
|
gem_put_cell(gp);
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
|
|
+ /* Mark us as attached again if we come from resume(), this has
|
|
|
|
+ * no effect if we weren't detatched and needs to be done now.
|
|
|
|
+ */
|
|
|
|
+ netif_device_attach(dev);
|
|
|
|
|
|
- return -EAGAIN;
|
|
|
|
- }
|
|
|
|
|
|
+ /* Restart NAPI & queues */
|
|
|
|
+ gem_netif_start(gp);
|
|
|
|
+
|
|
|
|
+ /* Detect & init PHY, start autoneg etc... this will
|
|
|
|
+ * eventually result in starting DMA operations when
|
|
|
|
+ * the link is up
|
|
|
|
+ */
|
|
|
|
+ gem_init_phy(gp);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2230,22 +2204,30 @@ static int gem_do_start(struct net_device *dev)
|
|
static void gem_do_stop(struct net_device *dev, int wol)
|
|
static void gem_do_stop(struct net_device *dev, int wol)
|
|
{
|
|
{
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
|
|
+ /* Stop NAPI and stop tx queue */
|
|
|
|
+ gem_netif_stop(gp);
|
|
|
|
|
|
- gp->running = 0;
|
|
|
|
-
|
|
|
|
- /* Stop netif queue */
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
-
|
|
|
|
- /* Make sure ints are disabled */
|
|
|
|
|
|
+ /* Make sure ints are disabled. We don't care about
|
|
|
|
+ * synchronizing as NAPI is disabled, thus a stray
|
|
|
|
+ * interrupt will do nothing bad (our irq handler
|
|
|
|
+ * just schedules NAPI)
|
|
|
|
+ */
|
|
gem_disable_ints(gp);
|
|
gem_disable_ints(gp);
|
|
|
|
|
|
- /* We can drop the lock now */
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
|
|
+ /* Stop the link timer */
|
|
|
|
+ del_timer_sync(&gp->link_timer);
|
|
|
|
+
|
|
|
|
+ /* We cannot cancel the reset task while holding the
|
|
|
|
+ * rtnl lock, we'd get an A->B / B->A deadlock stituation
|
|
|
|
+ * if we did. This is not an issue however as the reset
|
|
|
|
+ * task is synchronized vs. us (rtnl_lock) and will do
|
|
|
|
+ * nothing if the device is down or suspended. We do
|
|
|
|
+ * still clear reset_task_pending to avoid a spurrious
|
|
|
|
+ * reset later on in case we do resume before it gets
|
|
|
|
+ * scheduled.
|
|
|
|
+ */
|
|
|
|
+ gp->reset_task_pending = 0;
|
|
|
|
|
|
/* If we are going to sleep with WOL */
|
|
/* If we are going to sleep with WOL */
|
|
gem_stop_dma(gp);
|
|
gem_stop_dma(gp);
|
|
@@ -2260,79 +2242,79 @@ static void gem_do_stop(struct net_device *dev, int wol)
|
|
/* No irq needed anymore */
|
|
/* No irq needed anymore */
|
|
free_irq(gp->pdev->irq, (void *) dev);
|
|
free_irq(gp->pdev->irq, (void *) dev);
|
|
|
|
|
|
|
|
+ /* Shut the PHY down eventually and setup WOL */
|
|
|
|
+ gem_stop_phy(gp, wol);
|
|
|
|
+
|
|
|
|
+ /* Make sure bus master is disabled */
|
|
|
|
+ pci_disable_device(gp->pdev);
|
|
|
|
+
|
|
/* Cell not needed neither if no WOL */
|
|
/* Cell not needed neither if no WOL */
|
|
- if (!wol) {
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
|
|
+ if (!wol)
|
|
gem_put_cell(gp);
|
|
gem_put_cell(gp);
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void gem_reset_task(struct work_struct *work)
|
|
static void gem_reset_task(struct work_struct *work)
|
|
{
|
|
{
|
|
struct gem *gp = container_of(work, struct gem, reset_task);
|
|
struct gem *gp = container_of(work, struct gem, reset_task);
|
|
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
|
|
+ /* Lock out the network stack (essentially shield ourselves
|
|
|
|
+ * against a racing open, close, control call, or suspend
|
|
|
|
+ */
|
|
|
|
+ rtnl_lock();
|
|
|
|
|
|
- if (gp->opened)
|
|
|
|
- napi_disable(&gp->napi);
|
|
|
|
|
|
+ /* Skip the reset task if suspended or closed, or if it's
|
|
|
|
+ * been cancelled by gem_do_stop (see comment there)
|
|
|
|
+ */
|
|
|
|
+ if (!netif_device_present(gp->dev) ||
|
|
|
|
+ !netif_running(gp->dev) ||
|
|
|
|
+ !gp->reset_task_pending) {
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
|
|
+ /* Stop the link timer */
|
|
|
|
+ del_timer_sync(&gp->link_timer);
|
|
|
|
|
|
- if (gp->running) {
|
|
|
|
- netif_stop_queue(gp->dev);
|
|
|
|
|
|
+ /* Stop NAPI and tx */
|
|
|
|
+ gem_netif_stop(gp);
|
|
|
|
|
|
- /* Reset the chip & rings */
|
|
|
|
- gem_reinit_chip(gp);
|
|
|
|
- if (gp->lstate == link_up)
|
|
|
|
- gem_set_link_modes(gp);
|
|
|
|
- netif_wake_queue(gp->dev);
|
|
|
|
- }
|
|
|
|
|
|
+ /* Reset the chip & rings */
|
|
|
|
+ gem_reinit_chip(gp);
|
|
|
|
+ if (gp->lstate == link_up)
|
|
|
|
+ gem_set_link_modes(gp);
|
|
|
|
|
|
- gp->reset_task_pending = 0;
|
|
|
|
|
|
+ /* Restart NAPI and Tx */
|
|
|
|
+ gem_netif_start(gp);
|
|
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ /* We are back ! */
|
|
|
|
+ gp->reset_task_pending = 0;
|
|
|
|
|
|
- if (gp->opened)
|
|
|
|
- napi_enable(&gp->napi);
|
|
|
|
|
|
+ /* If the link is not up, restart autoneg, else restart the
|
|
|
|
+ * polling timer
|
|
|
|
+ */
|
|
|
|
+ if (gp->lstate != link_up)
|
|
|
|
+ gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
+ else
|
|
|
|
+ mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
|
|
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
|
|
+ rtnl_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
static int gem_open(struct net_device *dev)
|
|
static int gem_open(struct net_device *dev)
|
|
{
|
|
{
|
|
- struct gem *gp = netdev_priv(dev);
|
|
|
|
- int rc = 0;
|
|
|
|
-
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- /* We need the cell enabled */
|
|
|
|
- if (!gp->asleep)
|
|
|
|
- rc = gem_do_start(dev);
|
|
|
|
- gp->opened = (rc == 0);
|
|
|
|
-
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- return rc;
|
|
|
|
|
|
+ /* We allow open while suspended, we just do nothing,
|
|
|
|
+ * the chip will be initialized in resume()
|
|
|
|
+ */
|
|
|
|
+ if (netif_device_present(dev))
|
|
|
|
+ return gem_do_start(dev);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int gem_close(struct net_device *dev)
|
|
static int gem_close(struct net_device *dev)
|
|
{
|
|
{
|
|
- struct gem *gp = netdev_priv(dev);
|
|
|
|
-
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- napi_disable(&gp->napi);
|
|
|
|
-
|
|
|
|
- gp->opened = 0;
|
|
|
|
- if (!gp->asleep)
|
|
|
|
|
|
+ if (netif_device_present(dev))
|
|
gem_do_stop(dev, 0);
|
|
gem_do_stop(dev, 0);
|
|
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2341,59 +2323,35 @@ static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
{
|
|
{
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- netdev_info(dev, "suspending, WakeOnLan %s\n",
|
|
|
|
- (gp->wake_on_lan && gp->opened) ? "enabled" : "disabled");
|
|
|
|
-
|
|
|
|
- /* Keep the cell enabled during the entire operation */
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
- /* If the driver is opened, we stop the MAC */
|
|
|
|
- if (gp->opened) {
|
|
|
|
- napi_disable(&gp->napi);
|
|
|
|
|
|
+ /* Lock the network stack first to avoid racing with open/close,
|
|
|
|
+ * reset task and setting calls
|
|
|
|
+ */
|
|
|
|
+ rtnl_lock();
|
|
|
|
|
|
- /* Stop traffic, mark us closed */
|
|
|
|
|
|
+ /* Not running, mark ourselves non-present, no need for
|
|
|
|
+ * a lock here
|
|
|
|
+ */
|
|
|
|
+ if (!netif_running(dev)) {
|
|
netif_device_detach(dev);
|
|
netif_device_detach(dev);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ netdev_info(dev, "suspending, WakeOnLan %s\n",
|
|
|
|
+ (gp->wake_on_lan && netif_running(dev)) ?
|
|
|
|
+ "enabled" : "disabled");
|
|
|
|
|
|
- /* Switch off MAC, remember WOL setting */
|
|
|
|
- gp->asleep_wol = gp->wake_on_lan;
|
|
|
|
- gem_do_stop(dev, gp->asleep_wol);
|
|
|
|
- } else
|
|
|
|
- gp->asleep_wol = 0;
|
|
|
|
-
|
|
|
|
- /* Mark us asleep */
|
|
|
|
- gp->asleep = 1;
|
|
|
|
- wmb();
|
|
|
|
-
|
|
|
|
- /* Stop the link timer */
|
|
|
|
- del_timer_sync(&gp->link_timer);
|
|
|
|
-
|
|
|
|
- /* Now we release the mutex to not block the reset task who
|
|
|
|
- * can take it too. We are marked asleep, so there will be no
|
|
|
|
- * conflict here
|
|
|
|
|
|
+ /* Tell the network stack we're gone. gem_do_stop() below will
|
|
|
|
+ * synchronize with TX, stop NAPI etc...
|
|
*/
|
|
*/
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- /* Wait for the pending reset task to complete */
|
|
|
|
- flush_work_sync(&gp->reset_task);
|
|
|
|
|
|
+ netif_device_detach(dev);
|
|
|
|
|
|
- /* Shut the PHY down eventually and setup WOL */
|
|
|
|
- gem_stop_phy(gp, gp->asleep_wol);
|
|
|
|
-
|
|
|
|
- /* Make sure bus master is disabled */
|
|
|
|
- pci_disable_device(gp->pdev);
|
|
|
|
|
|
+ /* Switch off chip, remember WOL setting */
|
|
|
|
+ gp->asleep_wol = gp->wake_on_lan;
|
|
|
|
+ gem_do_stop(dev, gp->asleep_wol);
|
|
|
|
|
|
- /* Release the cell, no need to take a lock at this point since
|
|
|
|
- * nothing else can happen now
|
|
|
|
- */
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
|
|
+ /* Unlock the network stack */
|
|
|
|
+ rtnl_unlock();
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2402,53 +2360,23 @@ static int gem_resume(struct pci_dev *pdev)
|
|
{
|
|
{
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
struct net_device *dev = pci_get_drvdata(pdev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
- netdev_info(dev, "resuming\n");
|
|
|
|
|
|
+ /* See locking comment in gem_suspend */
|
|
|
|
+ rtnl_lock();
|
|
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- /* Keep the cell enabled during the entire operation, no need to
|
|
|
|
- * take a lock here tho since nothing else can happen while we are
|
|
|
|
- * marked asleep
|
|
|
|
|
|
+ /* Not running, mark ourselves present, no need for
|
|
|
|
+ * a lock here
|
|
*/
|
|
*/
|
|
- gem_get_cell(gp);
|
|
|
|
-
|
|
|
|
- /* Make sure PCI access and bus master are enabled */
|
|
|
|
- if (pci_enable_device(gp->pdev)) {
|
|
|
|
- netdev_err(dev, "Can't re-enable chip !\n");
|
|
|
|
- /* Put cell and forget it for now, it will be considered as
|
|
|
|
- * still asleep, a new sleep cycle may bring it back
|
|
|
|
- */
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
|
|
+ if (!netif_running(dev)) {
|
|
|
|
+ netif_device_attach(dev);
|
|
|
|
+ rtnl_unlock();
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
- pci_set_master(gp->pdev);
|
|
|
|
-
|
|
|
|
- /* Reset everything */
|
|
|
|
- gem_reset(gp);
|
|
|
|
|
|
|
|
- /* Mark us woken up */
|
|
|
|
- gp->asleep = 0;
|
|
|
|
- wmb();
|
|
|
|
-
|
|
|
|
- /* Bring the PHY back. Again, lock is useless at this point as
|
|
|
|
- * nothing can be happening until we restart the whole thing
|
|
|
|
|
|
+ /* Restart chip. If that fails there isn't much we can do, we
|
|
|
|
+ * leave things stopped.
|
|
*/
|
|
*/
|
|
- gem_init_phy(gp);
|
|
|
|
-
|
|
|
|
- /* If we were opened, bring everything back */
|
|
|
|
- if (gp->opened) {
|
|
|
|
- /* Restart MAC */
|
|
|
|
- gem_do_start(dev);
|
|
|
|
-
|
|
|
|
- /* Re-attach net device */
|
|
|
|
- netif_device_attach(dev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
|
|
+ gem_do_start(dev);
|
|
|
|
|
|
/* If we had WOL enabled, the cell clock was never turned off during
|
|
/* If we had WOL enabled, the cell clock was never turned off during
|
|
* sleep, so we end up beeing unbalanced. Fix that here
|
|
* sleep, so we end up beeing unbalanced. Fix that here
|
|
@@ -2456,15 +2384,8 @@ static int gem_resume(struct pci_dev *pdev)
|
|
if (gp->asleep_wol)
|
|
if (gp->asleep_wol)
|
|
gem_put_cell(gp);
|
|
gem_put_cell(gp);
|
|
|
|
|
|
- /* This function doesn't need to hold the cell, it will be held if the
|
|
|
|
- * driver is open by gem_do_start().
|
|
|
|
- */
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
-
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
|
|
+ /* Unlock the network stack */
|
|
|
|
+ rtnl_unlock();
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2474,33 +2395,35 @@ static struct net_device_stats *gem_get_stats(struct net_device *dev)
|
|
{
|
|
{
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
-
|
|
|
|
/* I have seen this being called while the PM was in progress,
|
|
/* I have seen this being called while the PM was in progress,
|
|
- * so we shield against this
|
|
|
|
|
|
+ * so we shield against this. Let's also not poke at registers
|
|
|
|
+ * while the reset task is going on.
|
|
|
|
+ *
|
|
|
|
+ * TODO: Move stats collection elsewhere (link timer ?) and
|
|
|
|
+ * make this a nop to avoid all those synchro issues
|
|
*/
|
|
*/
|
|
- if (gp->running) {
|
|
|
|
- dev->stats.rx_crc_errors += readl(gp->regs + MAC_FCSERR);
|
|
|
|
- writel(0, gp->regs + MAC_FCSERR);
|
|
|
|
|
|
+ if (!netif_device_present(dev) || !netif_running(dev))
|
|
|
|
+ goto bail;
|
|
|
|
|
|
- dev->stats.rx_frame_errors += readl(gp->regs + MAC_AERR);
|
|
|
|
- writel(0, gp->regs + MAC_AERR);
|
|
|
|
|
|
+ /* Better safe than sorry... */
|
|
|
|
+ if (WARN_ON(!gp->cell_enabled))
|
|
|
|
+ goto bail;
|
|
|
|
|
|
- dev->stats.rx_length_errors += readl(gp->regs + MAC_LERR);
|
|
|
|
- writel(0, gp->regs + MAC_LERR);
|
|
|
|
|
|
+ dev->stats.rx_crc_errors += readl(gp->regs + MAC_FCSERR);
|
|
|
|
+ writel(0, gp->regs + MAC_FCSERR);
|
|
|
|
|
|
- dev->stats.tx_aborted_errors += readl(gp->regs + MAC_ECOLL);
|
|
|
|
- dev->stats.collisions +=
|
|
|
|
- (readl(gp->regs + MAC_ECOLL) +
|
|
|
|
- readl(gp->regs + MAC_LCOLL));
|
|
|
|
- writel(0, gp->regs + MAC_ECOLL);
|
|
|
|
- writel(0, gp->regs + MAC_LCOLL);
|
|
|
|
- }
|
|
|
|
|
|
+ dev->stats.rx_frame_errors += readl(gp->regs + MAC_AERR);
|
|
|
|
+ writel(0, gp->regs + MAC_AERR);
|
|
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ dev->stats.rx_length_errors += readl(gp->regs + MAC_LERR);
|
|
|
|
+ writel(0, gp->regs + MAC_LERR);
|
|
|
|
|
|
|
|
+ dev->stats.tx_aborted_errors += readl(gp->regs + MAC_ECOLL);
|
|
|
|
+ dev->stats.collisions +=
|
|
|
|
+ (readl(gp->regs + MAC_ECOLL) + readl(gp->regs + MAC_LCOLL));
|
|
|
|
+ writel(0, gp->regs + MAC_ECOLL);
|
|
|
|
+ writel(0, gp->regs + MAC_LCOLL);
|
|
|
|
+ bail:
|
|
return &dev->stats;
|
|
return &dev->stats;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2513,22 +2436,19 @@ static int gem_set_mac_address(struct net_device *dev, void *addr)
|
|
if (!is_valid_ether_addr(macaddr->sa_data))
|
|
if (!is_valid_ether_addr(macaddr->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
- if (!netif_running(dev) || !netif_device_present(dev)) {
|
|
|
|
- /* We'll just catch it later when the
|
|
|
|
- * device is up'd or resumed.
|
|
|
|
- */
|
|
|
|
- memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
|
|
|
|
|
|
+ memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
|
|
|
|
+
|
|
|
|
+ /* We'll just catch it later when the device is up'd or resumed */
|
|
|
|
+ if (!netif_running(dev) || !netif_device_present(dev))
|
|
return 0;
|
|
return 0;
|
|
- }
|
|
|
|
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
- memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len);
|
|
|
|
- if (gp->running) {
|
|
|
|
- writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
|
|
|
|
- writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
|
|
|
|
- writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
|
|
|
|
- }
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
|
|
+ /* Better safe than sorry... */
|
|
|
|
+ if (WARN_ON(!gp->cell_enabled))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0);
|
|
|
|
+ writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1);
|
|
|
|
+ writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2539,14 +2459,12 @@ static void gem_set_multicast(struct net_device *dev)
|
|
u32 rxcfg, rxcfg_new;
|
|
u32 rxcfg, rxcfg_new;
|
|
int limit = 10000;
|
|
int limit = 10000;
|
|
|
|
|
|
|
|
+ if (!netif_running(dev) || !netif_device_present(dev))
|
|
|
|
+ return;
|
|
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
-
|
|
|
|
- if (!gp->running)
|
|
|
|
- goto bail;
|
|
|
|
-
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
|
|
+ /* Better safe than sorry... */
|
|
|
|
+ if (gp->reset_task_pending || WARN_ON(!gp->cell_enabled))
|
|
|
|
+ return;
|
|
|
|
|
|
rxcfg = readl(gp->regs + MAC_RXCFG);
|
|
rxcfg = readl(gp->regs + MAC_RXCFG);
|
|
rxcfg_new = gem_setup_multicast(gp);
|
|
rxcfg_new = gem_setup_multicast(gp);
|
|
@@ -2566,12 +2484,6 @@ static void gem_set_multicast(struct net_device *dev)
|
|
rxcfg |= rxcfg_new;
|
|
rxcfg |= rxcfg_new;
|
|
|
|
|
|
writel(rxcfg, gp->regs + MAC_RXCFG);
|
|
writel(rxcfg, gp->regs + MAC_RXCFG);
|
|
-
|
|
|
|
- netif_wake_queue(dev);
|
|
|
|
-
|
|
|
|
- bail:
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Jumbo-grams don't seem to work :-( */
|
|
/* Jumbo-grams don't seem to work :-( */
|
|
@@ -2589,26 +2501,21 @@ static int gem_change_mtu(struct net_device *dev, int new_mtu)
|
|
if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
|
|
if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!netif_running(dev) || !netif_device_present(dev)) {
|
|
|
|
- /* We'll just catch it later when the
|
|
|
|
- * device is up'd or resumed.
|
|
|
|
- */
|
|
|
|
- dev->mtu = new_mtu;
|
|
|
|
|
|
+ dev->mtu = new_mtu;
|
|
|
|
+
|
|
|
|
+ /* We'll just catch it later when the device is up'd or resumed */
|
|
|
|
+ if (!netif_running(dev) || !netif_device_present(dev))
|
|
return 0;
|
|
return 0;
|
|
- }
|
|
|
|
|
|
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- spin_lock(&gp->tx_lock);
|
|
|
|
- dev->mtu = new_mtu;
|
|
|
|
- if (gp->running) {
|
|
|
|
- gem_reinit_chip(gp);
|
|
|
|
- if (gp->lstate == link_up)
|
|
|
|
- gem_set_link_modes(gp);
|
|
|
|
- }
|
|
|
|
- spin_unlock(&gp->tx_lock);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
|
|
+ /* Better safe than sorry... */
|
|
|
|
+ if (WARN_ON(!gp->cell_enabled))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ gem_netif_stop(gp);
|
|
|
|
+ gem_reinit_chip(gp);
|
|
|
|
+ if (gp->lstate == link_up)
|
|
|
|
+ gem_set_link_modes(gp);
|
|
|
|
+ gem_netif_start(gp);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2640,7 +2547,6 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
cmd->phy_address = 0; /* XXX fixed PHYAD */
|
|
cmd->phy_address = 0; /* XXX fixed PHYAD */
|
|
|
|
|
|
/* Return current PHY settings */
|
|
/* Return current PHY settings */
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
cmd->autoneg = gp->want_autoneg;
|
|
cmd->autoneg = gp->want_autoneg;
|
|
ethtool_cmd_speed_set(cmd, gp->phy_mii.speed);
|
|
ethtool_cmd_speed_set(cmd, gp->phy_mii.speed);
|
|
cmd->duplex = gp->phy_mii.duplex;
|
|
cmd->duplex = gp->phy_mii.duplex;
|
|
@@ -2652,7 +2558,6 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
*/
|
|
*/
|
|
if (cmd->advertising == 0)
|
|
if (cmd->advertising == 0)
|
|
cmd->advertising = cmd->supported;
|
|
cmd->advertising = cmd->supported;
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
} else { // XXX PCS ?
|
|
} else { // XXX PCS ?
|
|
cmd->supported =
|
|
cmd->supported =
|
|
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
|
|
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
|
|
@@ -2706,11 +2611,10 @@ static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
/* Apply settings and restart link process. */
|
|
/* Apply settings and restart link process. */
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
- gem_begin_auto_negotiation(gp, cmd);
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ if (netif_device_present(gp->dev)) {
|
|
|
|
+ del_timer_sync(&gp->link_timer);
|
|
|
|
+ gem_begin_auto_negotiation(gp, cmd);
|
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2722,12 +2626,11 @@ static int gem_nway_reset(struct net_device *dev)
|
|
if (!gp->want_autoneg)
|
|
if (!gp->want_autoneg)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- /* Restart link process. */
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
- gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ /* Restart link process */
|
|
|
|
+ if (netif_device_present(gp->dev)) {
|
|
|
|
+ del_timer_sync(&gp->link_timer);
|
|
|
|
+ gem_begin_auto_negotiation(gp, NULL);
|
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -2791,16 +2694,11 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct gem *gp = netdev_priv(dev);
|
|
struct mii_ioctl_data *data = if_mii(ifr);
|
|
struct mii_ioctl_data *data = if_mii(ifr);
|
|
int rc = -EOPNOTSUPP;
|
|
int rc = -EOPNOTSUPP;
|
|
- unsigned long flags;
|
|
|
|
|
|
|
|
- /* Hold the PM mutex while doing ioctl's or we may collide
|
|
|
|
- * with power management.
|
|
|
|
|
|
+ /* For SIOCGMIIREG and SIOCSMIIREG the core checks for us that
|
|
|
|
+ * netif_device_present() is true and holds rtnl_lock for us
|
|
|
|
+ * so we have nothing to worry about
|
|
*/
|
|
*/
|
|
- mutex_lock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
switch (cmd) {
|
|
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
|
|
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
|
|
@@ -2808,32 +2706,17 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
/* Fallthrough... */
|
|
/* Fallthrough... */
|
|
|
|
|
|
case SIOCGMIIREG: /* Read MII PHY register. */
|
|
case SIOCGMIIREG: /* Read MII PHY register. */
|
|
- if (!gp->running)
|
|
|
|
- rc = -EAGAIN;
|
|
|
|
- else {
|
|
|
|
- data->val_out = __phy_read(gp, data->phy_id & 0x1f,
|
|
|
|
- data->reg_num & 0x1f);
|
|
|
|
- rc = 0;
|
|
|
|
- }
|
|
|
|
|
|
+ data->val_out = __phy_read(gp, data->phy_id & 0x1f,
|
|
|
|
+ data->reg_num & 0x1f);
|
|
|
|
+ rc = 0;
|
|
break;
|
|
break;
|
|
|
|
|
|
case SIOCSMIIREG: /* Write MII PHY register. */
|
|
case SIOCSMIIREG: /* Write MII PHY register. */
|
|
- if (!gp->running)
|
|
|
|
- rc = -EAGAIN;
|
|
|
|
- else {
|
|
|
|
- __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
|
|
|
|
- data->val_in);
|
|
|
|
- rc = 0;
|
|
|
|
- }
|
|
|
|
|
|
+ __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f,
|
|
|
|
+ data->val_in);
|
|
|
|
+ rc = 0;
|
|
break;
|
|
break;
|
|
};
|
|
};
|
|
-
|
|
|
|
- spin_lock_irqsave(&gp->lock, flags);
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- spin_unlock_irqrestore(&gp->lock, flags);
|
|
|
|
-
|
|
|
|
- mutex_unlock(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2921,23 +2804,9 @@ static void gem_remove_one(struct pci_dev *pdev)
|
|
|
|
|
|
unregister_netdev(dev);
|
|
unregister_netdev(dev);
|
|
|
|
|
|
- /* Stop the link timer */
|
|
|
|
- del_timer_sync(&gp->link_timer);
|
|
|
|
-
|
|
|
|
- /* We shouldn't need any locking here */
|
|
|
|
- gem_get_cell(gp);
|
|
|
|
-
|
|
|
|
- /* Cancel reset task */
|
|
|
|
|
|
+ /* Ensure reset task is truely gone */
|
|
cancel_work_sync(&gp->reset_task);
|
|
cancel_work_sync(&gp->reset_task);
|
|
|
|
|
|
- /* Shut the PHY down */
|
|
|
|
- gem_stop_phy(gp, 0);
|
|
|
|
-
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
-
|
|
|
|
- /* Make sure bus master is disabled */
|
|
|
|
- pci_disable_device(gp->pdev);
|
|
|
|
-
|
|
|
|
/* Free resources */
|
|
/* Free resources */
|
|
pci_free_consistent(pdev,
|
|
pci_free_consistent(pdev,
|
|
sizeof(struct gem_init_block),
|
|
sizeof(struct gem_init_block),
|
|
@@ -3043,10 +2912,6 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
|
|
|
|
|
|
gp->msg_enable = DEFAULT_MSG;
|
|
gp->msg_enable = DEFAULT_MSG;
|
|
|
|
|
|
- spin_lock_init(&gp->lock);
|
|
|
|
- spin_lock_init(&gp->tx_lock);
|
|
|
|
- mutex_init(&gp->pm_mutex);
|
|
|
|
-
|
|
|
|
init_timer(&gp->link_timer);
|
|
init_timer(&gp->link_timer);
|
|
gp->link_timer.function = gem_link_timer;
|
|
gp->link_timer.function = gem_link_timer;
|
|
gp->link_timer.data = (unsigned long) gp;
|
|
gp->link_timer.data = (unsigned long) gp;
|
|
@@ -3122,14 +2987,11 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
|
|
/* Set that now, in case PM kicks in now */
|
|
/* Set that now, in case PM kicks in now */
|
|
pci_set_drvdata(pdev, dev);
|
|
pci_set_drvdata(pdev, dev);
|
|
|
|
|
|
- /* Detect & init PHY, start autoneg, we release the cell now
|
|
|
|
- * too, it will be managed by whoever needs it
|
|
|
|
- */
|
|
|
|
- gem_init_phy(gp);
|
|
|
|
-
|
|
|
|
- spin_lock_irq(&gp->lock);
|
|
|
|
- gem_put_cell(gp);
|
|
|
|
- spin_unlock_irq(&gp->lock);
|
|
|
|
|
|
+ /* We can do scatter/gather and HW checksum */
|
|
|
|
+ dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
|
|
|
|
+ dev->features |= dev->hw_features | NETIF_F_RXCSUM;
|
|
|
|
+ if (pci_using_dac)
|
|
|
|
+ dev->features |= NETIF_F_HIGHDMA;
|
|
|
|
|
|
/* Register with kernel */
|
|
/* Register with kernel */
|
|
if (register_netdev(dev)) {
|
|
if (register_netdev(dev)) {
|
|
@@ -3138,20 +3000,15 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
|
|
goto err_out_free_consistent;
|
|
goto err_out_free_consistent;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Undo the get_cell with appropriate locking (we could use
|
|
|
|
+ * ndo_init/uninit but that would be even more clumsy imho)
|
|
|
|
+ */
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ gem_put_cell(gp);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+
|
|
netdev_info(dev, "Sun GEM (PCI) 10/100/1000BaseT Ethernet %pM\n",
|
|
netdev_info(dev, "Sun GEM (PCI) 10/100/1000BaseT Ethernet %pM\n",
|
|
dev->dev_addr);
|
|
dev->dev_addr);
|
|
-
|
|
|
|
- if (gp->phy_type == phy_mii_mdio0 ||
|
|
|
|
- gp->phy_type == phy_mii_mdio1)
|
|
|
|
- netdev_info(dev, "Found %s PHY\n",
|
|
|
|
- gp->phy_mii.def ? gp->phy_mii.def->name : "no");
|
|
|
|
-
|
|
|
|
- /* GEM can do it all... */
|
|
|
|
- dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
|
|
|
|
- dev->features |= dev->hw_features | NETIF_F_RXCSUM | NETIF_F_LLTX;
|
|
|
|
- if (pci_using_dac)
|
|
|
|
- dev->features |= NETIF_F_HIGHDMA;
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
err_out_free_consistent:
|
|
err_out_free_consistent:
|