diff options
-rw-r--r-- | drivers/net/forcedeth.c | 171 |
1 files changed, 104 insertions, 67 deletions
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 6364adbd73a1..731d0d7b2f37 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c | |||
@@ -98,6 +98,7 @@ | |||
98 | * 0.43: 10 Aug 2005: Add support for tx checksum. | 98 | * 0.43: 10 Aug 2005: Add support for tx checksum. |
99 | * 0.44: 20 Aug 2005: Add support for scatter gather and segmentation. | 99 | * 0.44: 20 Aug 2005: Add support for scatter gather and segmentation. |
100 | * 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check | 100 | * 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check |
101 | * 0.46: 20 Oct 2005: Add irq optimization modes. | ||
101 | * | 102 | * |
102 | * Known bugs: | 103 | * Known bugs: |
103 | * We suspect that on some hardware no TX done interrupts are generated. | 104 | * We suspect that on some hardware no TX done interrupts are generated. |
@@ -109,7 +110,7 @@ | |||
109 | * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few | 110 | * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few |
110 | * superfluous timer interrupts from the nic. | 111 | * superfluous timer interrupts from the nic. |
111 | */ | 112 | */ |
112 | #define FORCEDETH_VERSION "0.45" | 113 | #define FORCEDETH_VERSION "0.46" |
113 | #define DRV_NAME "forcedeth" | 114 | #define DRV_NAME "forcedeth" |
114 | 115 | ||
115 | #include <linux/module.h> | 116 | #include <linux/module.h> |
@@ -164,7 +165,8 @@ enum { | |||
164 | #define NVREG_IRQ_LINK 0x0040 | 165 | #define NVREG_IRQ_LINK 0x0040 |
165 | #define NVREG_IRQ_TX_ERROR 0x0080 | 166 | #define NVREG_IRQ_TX_ERROR 0x0080 |
166 | #define NVREG_IRQ_TX1 0x0100 | 167 | #define NVREG_IRQ_TX1 0x0100 |
167 | #define NVREG_IRQMASK_WANTED 0x00df | 168 | #define NVREG_IRQMASK_THROUGHPUT 0x00df |
169 | #define NVREG_IRQMASK_CPU 0x0040 | ||
168 | 170 | ||
169 | #define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \ | 171 | #define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \ |
170 | NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX_ERROR| \ | 172 | NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX_ERROR| \ |
@@ -178,7 +180,8 @@ enum { | |||
178 | * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms | 180 | * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms |
179 | */ | 181 | */ |
180 | NvRegPollingInterval = 0x00c, | 182 | NvRegPollingInterval = 0x00c, |
181 | #define NVREG_POLL_DEFAULT 970 | 183 | #define NVREG_POLL_DEFAULT_THROUGHPUT 970 |
184 | #define NVREG_POLL_DEFAULT_CPU 13 | ||
182 | NvRegMisc1 = 0x080, | 185 | NvRegMisc1 = 0x080, |
183 | #define NVREG_MISC1_HD 0x02 | 186 | #define NVREG_MISC1_HD 0x02 |
184 | #define NVREG_MISC1_FORCE 0x3b0f3c | 187 | #define NVREG_MISC1_FORCE 0x3b0f3c |
@@ -539,6 +542,25 @@ struct fe_priv { | |||
539 | */ | 542 | */ |
540 | static int max_interrupt_work = 5; | 543 | static int max_interrupt_work = 5; |
541 | 544 | ||
545 | /* | ||
546 | * Optimization can be either throuput mode or cpu mode | ||
547 | * | ||
548 | * Throughput Mode: Every tx and rx packet will generate an interrupt. | ||
549 | * CPU Mode: Interrupts are controlled by a timer. | ||
550 | */ | ||
551 | #define NV_OPTIMIZATION_MODE_THROUGHPUT 0 | ||
552 | #define NV_OPTIMIZATION_MODE_CPU 1 | ||
553 | static int optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT; | ||
554 | |||
555 | /* | ||
556 | * Poll interval for timer irq | ||
557 | * | ||
558 | * This interval determines how frequent an interrupt is generated. | ||
559 | * The is value is determined by [(time_in_micro_secs * 100) / (2^10)] | ||
560 | * Min = 0, and Max = 65535 | ||
561 | */ | ||
562 | static int poll_interval = -1; | ||
563 | |||
542 | static inline struct fe_priv *get_nvpriv(struct net_device *dev) | 564 | static inline struct fe_priv *get_nvpriv(struct net_device *dev) |
543 | { | 565 | { |
544 | return netdev_priv(dev); | 566 | return netdev_priv(dev); |
@@ -1329,67 +1351,71 @@ static void nv_rx_process(struct net_device *dev) | |||
1329 | if (!(Flags & NV_RX_DESCRIPTORVALID)) | 1351 | if (!(Flags & NV_RX_DESCRIPTORVALID)) |
1330 | goto next_pkt; | 1352 | goto next_pkt; |
1331 | 1353 | ||
1332 | if (Flags & NV_RX_MISSEDFRAME) { | 1354 | if (Flags & NV_RX_ERROR) { |
1333 | np->stats.rx_missed_errors++; | 1355 | if (Flags & NV_RX_MISSEDFRAME) { |
1334 | np->stats.rx_errors++; | 1356 | np->stats.rx_missed_errors++; |
1335 | goto next_pkt; | ||
1336 | } | ||
1337 | if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) { | ||
1338 | np->stats.rx_errors++; | ||
1339 | goto next_pkt; | ||
1340 | } | ||
1341 | if (Flags & NV_RX_CRCERR) { | ||
1342 | np->stats.rx_crc_errors++; | ||
1343 | np->stats.rx_errors++; | ||
1344 | goto next_pkt; | ||
1345 | } | ||
1346 | if (Flags & NV_RX_OVERFLOW) { | ||
1347 | np->stats.rx_over_errors++; | ||
1348 | np->stats.rx_errors++; | ||
1349 | goto next_pkt; | ||
1350 | } | ||
1351 | if (Flags & NV_RX_ERROR4) { | ||
1352 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); | ||
1353 | if (len < 0) { | ||
1354 | np->stats.rx_errors++; | 1357 | np->stats.rx_errors++; |
1355 | goto next_pkt; | 1358 | goto next_pkt; |
1356 | } | 1359 | } |
1357 | } | 1360 | if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) { |
1358 | /* framing errors are soft errors. */ | 1361 | np->stats.rx_errors++; |
1359 | if (Flags & NV_RX_FRAMINGERR) { | 1362 | goto next_pkt; |
1360 | if (Flags & NV_RX_SUBSTRACT1) { | 1363 | } |
1361 | len--; | 1364 | if (Flags & NV_RX_CRCERR) { |
1365 | np->stats.rx_crc_errors++; | ||
1366 | np->stats.rx_errors++; | ||
1367 | goto next_pkt; | ||
1368 | } | ||
1369 | if (Flags & NV_RX_OVERFLOW) { | ||
1370 | np->stats.rx_over_errors++; | ||
1371 | np->stats.rx_errors++; | ||
1372 | goto next_pkt; | ||
1373 | } | ||
1374 | if (Flags & NV_RX_ERROR4) { | ||
1375 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); | ||
1376 | if (len < 0) { | ||
1377 | np->stats.rx_errors++; | ||
1378 | goto next_pkt; | ||
1379 | } | ||
1380 | } | ||
1381 | /* framing errors are soft errors. */ | ||
1382 | if (Flags & NV_RX_FRAMINGERR) { | ||
1383 | if (Flags & NV_RX_SUBSTRACT1) { | ||
1384 | len--; | ||
1385 | } | ||
1362 | } | 1386 | } |
1363 | } | 1387 | } |
1364 | } else { | 1388 | } else { |
1365 | if (!(Flags & NV_RX2_DESCRIPTORVALID)) | 1389 | if (!(Flags & NV_RX2_DESCRIPTORVALID)) |
1366 | goto next_pkt; | 1390 | goto next_pkt; |
1367 | 1391 | ||
1368 | if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { | 1392 | if (Flags & NV_RX2_ERROR) { |
1369 | np->stats.rx_errors++; | 1393 | if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { |
1370 | goto next_pkt; | ||
1371 | } | ||
1372 | if (Flags & NV_RX2_CRCERR) { | ||
1373 | np->stats.rx_crc_errors++; | ||
1374 | np->stats.rx_errors++; | ||
1375 | goto next_pkt; | ||
1376 | } | ||
1377 | if (Flags & NV_RX2_OVERFLOW) { | ||
1378 | np->stats.rx_over_errors++; | ||
1379 | np->stats.rx_errors++; | ||
1380 | goto next_pkt; | ||
1381 | } | ||
1382 | if (Flags & NV_RX2_ERROR4) { | ||
1383 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); | ||
1384 | if (len < 0) { | ||
1385 | np->stats.rx_errors++; | 1394 | np->stats.rx_errors++; |
1386 | goto next_pkt; | 1395 | goto next_pkt; |
1387 | } | 1396 | } |
1388 | } | 1397 | if (Flags & NV_RX2_CRCERR) { |
1389 | /* framing errors are soft errors */ | 1398 | np->stats.rx_crc_errors++; |
1390 | if (Flags & NV_RX2_FRAMINGERR) { | 1399 | np->stats.rx_errors++; |
1391 | if (Flags & NV_RX2_SUBSTRACT1) { | 1400 | goto next_pkt; |
1392 | len--; | 1401 | } |
1402 | if (Flags & NV_RX2_OVERFLOW) { | ||
1403 | np->stats.rx_over_errors++; | ||
1404 | np->stats.rx_errors++; | ||
1405 | goto next_pkt; | ||
1406 | } | ||
1407 | if (Flags & NV_RX2_ERROR4) { | ||
1408 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); | ||
1409 | if (len < 0) { | ||
1410 | np->stats.rx_errors++; | ||
1411 | goto next_pkt; | ||
1412 | } | ||
1413 | } | ||
1414 | /* framing errors are soft errors */ | ||
1415 | if (Flags & NV_RX2_FRAMINGERR) { | ||
1416 | if (Flags & NV_RX2_SUBSTRACT1) { | ||
1417 | len--; | ||
1418 | } | ||
1393 | } | 1419 | } |
1394 | } | 1420 | } |
1395 | Flags &= NV_RX2_CHECKSUMMASK; | 1421 | Flags &= NV_RX2_CHECKSUMMASK; |
@@ -1809,22 +1835,18 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs) | |||
1809 | if (!(events & np->irqmask)) | 1835 | if (!(events & np->irqmask)) |
1810 | break; | 1836 | break; |
1811 | 1837 | ||
1812 | if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_ERROR|NVREG_IRQ_TX_ERR)) { | 1838 | spin_lock(&np->lock); |
1839 | nv_tx_done(dev); | ||
1840 | spin_unlock(&np->lock); | ||
1841 | |||
1842 | nv_rx_process(dev); | ||
1843 | if (nv_alloc_rx(dev)) { | ||
1813 | spin_lock(&np->lock); | 1844 | spin_lock(&np->lock); |
1814 | nv_tx_done(dev); | 1845 | if (!np->in_shutdown) |
1846 | mod_timer(&np->oom_kick, jiffies + OOM_REFILL); | ||
1815 | spin_unlock(&np->lock); | 1847 | spin_unlock(&np->lock); |
1816 | } | 1848 | } |
1817 | 1849 | ||
1818 | if (events & (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF)) { | ||
1819 | nv_rx_process(dev); | ||
1820 | if (nv_alloc_rx(dev)) { | ||
1821 | spin_lock(&np->lock); | ||
1822 | if (!np->in_shutdown) | ||
1823 | mod_timer(&np->oom_kick, jiffies + OOM_REFILL); | ||
1824 | spin_unlock(&np->lock); | ||
1825 | } | ||
1826 | } | ||
1827 | |||
1828 | if (events & NVREG_IRQ_LINK) { | 1850 | if (events & NVREG_IRQ_LINK) { |
1829 | spin_lock(&np->lock); | 1851 | spin_lock(&np->lock); |
1830 | nv_link_irq(dev); | 1852 | nv_link_irq(dev); |
@@ -2226,7 +2248,14 @@ static int nv_open(struct net_device *dev) | |||
2226 | writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed); | 2248 | writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed); |
2227 | writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1); | 2249 | writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1); |
2228 | writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2); | 2250 | writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2); |
2229 | writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval); | 2251 | if (poll_interval == -1) { |
2252 | if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT) | ||
2253 | writel(NVREG_POLL_DEFAULT_THROUGHPUT, base + NvRegPollingInterval); | ||
2254 | else | ||
2255 | writel(NVREG_POLL_DEFAULT_CPU, base + NvRegPollingInterval); | ||
2256 | } | ||
2257 | else | ||
2258 | writel(poll_interval & 0xFFFF, base + NvRegPollingInterval); | ||
2230 | writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6); | 2259 | writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6); |
2231 | writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID|NVREG_ADAPTCTL_RUNNING, | 2260 | writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID|NVREG_ADAPTCTL_RUNNING, |
2232 | base + NvRegAdapterControl); | 2261 | base + NvRegAdapterControl); |
@@ -2511,7 +2540,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i | |||
2511 | } else { | 2540 | } else { |
2512 | np->tx_flags = NV_TX2_VALID; | 2541 | np->tx_flags = NV_TX2_VALID; |
2513 | } | 2542 | } |
2514 | np->irqmask = NVREG_IRQMASK_WANTED; | 2543 | if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT) |
2544 | np->irqmask = NVREG_IRQMASK_THROUGHPUT; | ||
2545 | else | ||
2546 | np->irqmask = NVREG_IRQMASK_CPU; | ||
2547 | |||
2515 | if (id->driver_data & DEV_NEED_TIMERIRQ) | 2548 | if (id->driver_data & DEV_NEED_TIMERIRQ) |
2516 | np->irqmask |= NVREG_IRQ_TIMER; | 2549 | np->irqmask |= NVREG_IRQ_TIMER; |
2517 | if (id->driver_data & DEV_NEED_LINKTIMER) { | 2550 | if (id->driver_data & DEV_NEED_LINKTIMER) { |
@@ -2699,6 +2732,10 @@ static void __exit exit_nic(void) | |||
2699 | 2732 | ||
2700 | module_param(max_interrupt_work, int, 0); | 2733 | module_param(max_interrupt_work, int, 0); |
2701 | MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt"); | 2734 | MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt"); |
2735 | module_param(optimization_mode, int, 0); | ||
2736 | MODULE_PARM_DESC(optimization_mode, "In throughput mode (0), every tx & rx packet will generate an interrupt. In CPU mode (1), interrupts are controlled by a timer."); | ||
2737 | module_param(poll_interval, int, 0); | ||
2738 | MODULE_PARM_DESC(poll_interval, "Interval determines how frequent timer interrupt is generated by [(time_in_micro_secs * 100) / (2^10)]. Min is 0 and Max is 65535."); | ||
2702 | 2739 | ||
2703 | MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>"); | 2740 | MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>"); |
2704 | MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); | 2741 | MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); |