diff options
author | Ayaz Abdulla <aabdulla@nvidia.com> | 2006-10-30 17:32:01 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-12-02 00:12:01 -0500 |
commit | c5cf9101fefae32df654da5f0e736ffbe28aefdc (patch) | |
tree | 0591d2a2b9f3a71dd0a7bece85d74505a25af5ec /drivers/net/forcedeth.c | |
parent | 7e680c22c0579f1db7b84a7b155755a2754f9557 (diff) |
[PATCH] forcedeth: add recoverable error support
This patch adds support to recover from a previously fatal MAC error. In
the past the MAC would be hung on an internal fatal error. On new
chipsets, the MAC has the ability to enter a non-fatal state and allow
the driver to re-init it.
Signed-Off-By: Ayaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/forcedeth.c')
-rw-r--r-- | drivers/net/forcedeth.c | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 5472d12122b4..3b8087159b6d 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c | |||
@@ -111,6 +111,7 @@ | |||
111 | * 0.56: 22 Mar 2006: Additional ethtool config and moduleparam support. | 111 | * 0.56: 22 Mar 2006: Additional ethtool config and moduleparam support. |
112 | * 0.57: 14 May 2006: Mac address set in probe/remove and order corrections. | 112 | * 0.57: 14 May 2006: Mac address set in probe/remove and order corrections. |
113 | * 0.58: 30 Oct 2006: Added support for sideband management unit. | 113 | * 0.58: 30 Oct 2006: Added support for sideband management unit. |
114 | * 0.59: 30 Oct 2006: Added support for recoverable error. | ||
114 | * | 115 | * |
115 | * Known bugs: | 116 | * Known bugs: |
116 | * We suspect that on some hardware no TX done interrupts are generated. | 117 | * We suspect that on some hardware no TX done interrupts are generated. |
@@ -127,7 +128,7 @@ | |||
127 | #else | 128 | #else |
128 | #define DRIVERNAPI | 129 | #define DRIVERNAPI |
129 | #endif | 130 | #endif |
130 | #define FORCEDETH_VERSION "0.58" | 131 | #define FORCEDETH_VERSION "0.59" |
131 | #define DRV_NAME "forcedeth" | 132 | #define DRV_NAME "forcedeth" |
132 | 133 | ||
133 | #include <linux/module.h> | 134 | #include <linux/module.h> |
@@ -180,7 +181,7 @@ | |||
180 | enum { | 181 | enum { |
181 | NvRegIrqStatus = 0x000, | 182 | NvRegIrqStatus = 0x000, |
182 | #define NVREG_IRQSTAT_MIIEVENT 0x040 | 183 | #define NVREG_IRQSTAT_MIIEVENT 0x040 |
183 | #define NVREG_IRQSTAT_MASK 0x1ff | 184 | #define NVREG_IRQSTAT_MASK 0x81ff |
184 | NvRegIrqMask = 0x004, | 185 | NvRegIrqMask = 0x004, |
185 | #define NVREG_IRQ_RX_ERROR 0x0001 | 186 | #define NVREG_IRQ_RX_ERROR 0x0001 |
186 | #define NVREG_IRQ_RX 0x0002 | 187 | #define NVREG_IRQ_RX 0x0002 |
@@ -191,15 +192,16 @@ enum { | |||
191 | #define NVREG_IRQ_LINK 0x0040 | 192 | #define NVREG_IRQ_LINK 0x0040 |
192 | #define NVREG_IRQ_RX_FORCED 0x0080 | 193 | #define NVREG_IRQ_RX_FORCED 0x0080 |
193 | #define NVREG_IRQ_TX_FORCED 0x0100 | 194 | #define NVREG_IRQ_TX_FORCED 0x0100 |
195 | #define NVREG_IRQ_RECOVER_ERROR 0x8000 | ||
194 | #define NVREG_IRQMASK_THROUGHPUT 0x00df | 196 | #define NVREG_IRQMASK_THROUGHPUT 0x00df |
195 | #define NVREG_IRQMASK_CPU 0x0040 | 197 | #define NVREG_IRQMASK_CPU 0x0040 |
196 | #define NVREG_IRQ_TX_ALL (NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED) | 198 | #define NVREG_IRQ_TX_ALL (NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED) |
197 | #define NVREG_IRQ_RX_ALL (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED) | 199 | #define NVREG_IRQ_RX_ALL (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED) |
198 | #define NVREG_IRQ_OTHER (NVREG_IRQ_TIMER|NVREG_IRQ_LINK) | 200 | #define NVREG_IRQ_OTHER (NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR) |
199 | 201 | ||
200 | #define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \ | 202 | #define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \ |
201 | NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RX_FORCED| \ | 203 | NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RX_FORCED| \ |
202 | NVREG_IRQ_TX_FORCED)) | 204 | NVREG_IRQ_TX_FORCED|NVREG_IRQ_RECOVER_ERROR)) |
203 | 205 | ||
204 | NvRegUnknownSetupReg6 = 0x008, | 206 | NvRegUnknownSetupReg6 = 0x008, |
205 | #define NVREG_UNKSETUP6_VAL 3 | 207 | #define NVREG_UNKSETUP6_VAL 3 |
@@ -718,6 +720,7 @@ struct fe_priv { | |||
718 | unsigned int phy_model; | 720 | unsigned int phy_model; |
719 | u16 gigabit; | 721 | u16 gigabit; |
720 | int intr_test; | 722 | int intr_test; |
723 | int recover_error; | ||
721 | 724 | ||
722 | /* General data: RO fields */ | 725 | /* General data: RO fields */ |
723 | dma_addr_t ring_addr; | 726 | dma_addr_t ring_addr; |
@@ -2455,6 +2458,23 @@ static irqreturn_t nv_nic_irq(int foo, void *data) | |||
2455 | printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", | 2458 | printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", |
2456 | dev->name, events); | 2459 | dev->name, events); |
2457 | } | 2460 | } |
2461 | if (unlikely(events & NVREG_IRQ_RECOVER_ERROR)) { | ||
2462 | spin_lock(&np->lock); | ||
2463 | /* disable interrupts on the nic */ | ||
2464 | if (!(np->msi_flags & NV_MSI_X_ENABLED)) | ||
2465 | writel(0, base + NvRegIrqMask); | ||
2466 | else | ||
2467 | writel(np->irqmask, base + NvRegIrqMask); | ||
2468 | pci_push(base); | ||
2469 | |||
2470 | if (!np->in_shutdown) { | ||
2471 | np->nic_poll_irq = np->irqmask; | ||
2472 | np->recover_error = 1; | ||
2473 | mod_timer(&np->nic_poll, jiffies + POLL_WAIT); | ||
2474 | } | ||
2475 | spin_unlock(&np->lock); | ||
2476 | break; | ||
2477 | } | ||
2458 | #ifdef CONFIG_FORCEDETH_NAPI | 2478 | #ifdef CONFIG_FORCEDETH_NAPI |
2459 | if (events & NVREG_IRQ_RX_ALL) { | 2479 | if (events & NVREG_IRQ_RX_ALL) { |
2460 | netif_rx_schedule(dev); | 2480 | netif_rx_schedule(dev); |
@@ -2685,6 +2705,20 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data) | |||
2685 | spin_unlock_irqrestore(&np->lock, flags); | 2705 | spin_unlock_irqrestore(&np->lock, flags); |
2686 | np->link_timeout = jiffies + LINK_TIMEOUT; | 2706 | np->link_timeout = jiffies + LINK_TIMEOUT; |
2687 | } | 2707 | } |
2708 | if (events & NVREG_IRQ_RECOVER_ERROR) { | ||
2709 | spin_lock_irq(&np->lock); | ||
2710 | /* disable interrupts on the nic */ | ||
2711 | writel(NVREG_IRQ_OTHER, base + NvRegIrqMask); | ||
2712 | pci_push(base); | ||
2713 | |||
2714 | if (!np->in_shutdown) { | ||
2715 | np->nic_poll_irq |= NVREG_IRQ_OTHER; | ||
2716 | np->recover_error = 1; | ||
2717 | mod_timer(&np->nic_poll, jiffies + POLL_WAIT); | ||
2718 | } | ||
2719 | spin_unlock_irq(&np->lock); | ||
2720 | break; | ||
2721 | } | ||
2688 | if (events & (NVREG_IRQ_UNKNOWN)) { | 2722 | if (events & (NVREG_IRQ_UNKNOWN)) { |
2689 | printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", | 2723 | printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", |
2690 | dev->name, events); | 2724 | dev->name, events); |
@@ -2914,6 +2948,42 @@ static void nv_do_nic_poll(unsigned long data) | |||
2914 | } | 2948 | } |
2915 | np->nic_poll_irq = 0; | 2949 | np->nic_poll_irq = 0; |
2916 | 2950 | ||
2951 | if (np->recover_error) { | ||
2952 | np->recover_error = 0; | ||
2953 | printk(KERN_INFO "forcedeth: MAC in recoverable error state\n"); | ||
2954 | if (netif_running(dev)) { | ||
2955 | netif_tx_lock_bh(dev); | ||
2956 | spin_lock(&np->lock); | ||
2957 | /* stop engines */ | ||
2958 | nv_stop_rx(dev); | ||
2959 | nv_stop_tx(dev); | ||
2960 | nv_txrx_reset(dev); | ||
2961 | /* drain rx queue */ | ||
2962 | nv_drain_rx(dev); | ||
2963 | nv_drain_tx(dev); | ||
2964 | /* reinit driver view of the rx queue */ | ||
2965 | set_bufsize(dev); | ||
2966 | if (nv_init_ring(dev)) { | ||
2967 | if (!np->in_shutdown) | ||
2968 | mod_timer(&np->oom_kick, jiffies + OOM_REFILL); | ||
2969 | } | ||
2970 | /* reinit nic view of the rx queue */ | ||
2971 | writel(np->rx_buf_sz, base + NvRegOffloadConfig); | ||
2972 | setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING); | ||
2973 | writel( ((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT), | ||
2974 | base + NvRegRingSizes); | ||
2975 | pci_push(base); | ||
2976 | writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl); | ||
2977 | pci_push(base); | ||
2978 | |||
2979 | /* restart rx engine */ | ||
2980 | nv_start_rx(dev); | ||
2981 | nv_start_tx(dev); | ||
2982 | spin_unlock(&np->lock); | ||
2983 | netif_tx_unlock_bh(dev); | ||
2984 | } | ||
2985 | } | ||
2986 | |||
2917 | /* FIXME: Do we need synchronize_irq(dev->irq) here? */ | 2987 | /* FIXME: Do we need synchronize_irq(dev->irq) here? */ |
2918 | 2988 | ||
2919 | writel(mask, base + NvRegIrqMask); | 2989 | writel(mask, base + NvRegIrqMask); |