diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/tg3.c')
-rw-r--r-- | drivers/net/ethernet/broadcom/tg3.c | 534 |
1 files changed, 356 insertions, 178 deletions
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 388d322..46280ba 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -44,10 +44,8 @@ #include <linux/prefetch.h> #include <linux/dma-mapping.h> #include <linux/firmware.h> -#if IS_ENABLED(CONFIG_HWMON) #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#endif #include <net/checksum.h> #include <net/ip.h> @@ -92,10 +90,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) #define DRV_MODULE_NAME "tg3" #define TG3_MAJ_NUM 3 -#define TG3_MIN_NUM 124 +#define TG3_MIN_NUM 125 #define DRV_MODULE_VERSION \ __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM) -#define DRV_MODULE_RELDATE "March 21, 2012" +#define DRV_MODULE_RELDATE "September 26, 2012" #define RESET_KIND_SHUTDOWN 0 #define RESET_KIND_INIT 1 @@ -6263,7 +6261,7 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget) u32 jmb_prod_idx = dpr->rx_jmb_prod_idx; tp->rx_refill = false; - for (i = 1; i < tp->irq_cnt; i++) + for (i = 1; i <= tp->rxq_cnt; i++) err |= tg3_rx_prodring_xfer(tp, dpr, &tp->napi[i].prodring); @@ -7592,15 +7590,11 @@ static int tg3_init_rings(struct tg3 *tp) return 0; } -/* - * Must not be invoked with interrupt sources disabled and - * the hardware shutdown down. - */ -static void tg3_free_consistent(struct tg3 *tp) +static void tg3_mem_tx_release(struct tg3 *tp) { int i; - for (i = 0; i < tp->irq_cnt; i++) { + for (i = 0; i < tp->irq_max; i++) { struct tg3_napi *tnapi = &tp->napi[i]; if (tnapi->tx_ring) { @@ -7611,17 +7605,114 @@ static void tg3_free_consistent(struct tg3 *tp) kfree(tnapi->tx_buffers); tnapi->tx_buffers = NULL; + } +} - if (tnapi->rx_rcb) { - dma_free_coherent(&tp->pdev->dev, - TG3_RX_RCB_RING_BYTES(tp), - tnapi->rx_rcb, - tnapi->rx_rcb_mapping); - tnapi->rx_rcb = NULL; - } +static int tg3_mem_tx_acquire(struct tg3 *tp) +{ + int i; + struct tg3_napi *tnapi = &tp->napi[0]; + + /* If multivector TSS is enabled, vector 0 does not handle + * tx interrupts. Don't allocate any resources for it. + */ + if (tg3_flag(tp, ENABLE_TSS)) + tnapi++; + + for (i = 0; i < tp->txq_cnt; i++, tnapi++) { + tnapi->tx_buffers = kzalloc(sizeof(struct tg3_tx_ring_info) * + TG3_TX_RING_SIZE, GFP_KERNEL); + if (!tnapi->tx_buffers) + goto err_out; + + tnapi->tx_ring = dma_alloc_coherent(&tp->pdev->dev, + TG3_TX_RING_BYTES, + &tnapi->tx_desc_mapping, + GFP_KERNEL); + if (!tnapi->tx_ring) + goto err_out; + } + + return 0; + +err_out: + tg3_mem_tx_release(tp); + return -ENOMEM; +} + +static void tg3_mem_rx_release(struct tg3 *tp) +{ + int i; + + for (i = 0; i < tp->irq_max; i++) { + struct tg3_napi *tnapi = &tp->napi[i]; tg3_rx_prodring_fini(tp, &tnapi->prodring); + if (!tnapi->rx_rcb) + continue; + + dma_free_coherent(&tp->pdev->dev, + TG3_RX_RCB_RING_BYTES(tp), + tnapi->rx_rcb, + tnapi->rx_rcb_mapping); + tnapi->rx_rcb = NULL; + } +} + +static int tg3_mem_rx_acquire(struct tg3 *tp) +{ + unsigned int i, limit; + + limit = tp->rxq_cnt; + + /* If RSS is enabled, we need a (dummy) producer ring + * set on vector zero. This is the true hw prodring. + */ + if (tg3_flag(tp, ENABLE_RSS)) + limit++; + + for (i = 0; i < limit; i++) { + struct tg3_napi *tnapi = &tp->napi[i]; + + if (tg3_rx_prodring_init(tp, &tnapi->prodring)) + goto err_out; + + /* If multivector RSS is enabled, vector 0 + * does not handle rx or tx interrupts. + * Don't allocate any resources for it. + */ + if (!i && tg3_flag(tp, ENABLE_RSS)) + continue; + + tnapi->rx_rcb = dma_alloc_coherent(&tp->pdev->dev, + TG3_RX_RCB_RING_BYTES(tp), + &tnapi->rx_rcb_mapping, + GFP_KERNEL); + if (!tnapi->rx_rcb) + goto err_out; + + memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); + } + + return 0; + +err_out: + tg3_mem_rx_release(tp); + return -ENOMEM; +} + +/* + * Must not be invoked with interrupt sources disabled and + * the hardware shutdown down. + */ +static void tg3_free_consistent(struct tg3 *tp) +{ + int i; + + for (i = 0; i < tp->irq_cnt; i++) { + struct tg3_napi *tnapi = &tp->napi[i]; + if (tnapi->hw_status) { dma_free_coherent(&tp->pdev->dev, TG3_HW_STATUS_SIZE, tnapi->hw_status, @@ -7630,6 +7721,9 @@ static void tg3_free_consistent(struct tg3 *tp) } } + tg3_mem_rx_release(tp); + tg3_mem_tx_release(tp); + if (tp->hw_stats) { dma_free_coherent(&tp->pdev->dev, sizeof(struct tg3_hw_stats), tp->hw_stats, tp->stats_mapping); @@ -7668,72 +7762,38 @@ static int tg3_alloc_consistent(struct tg3 *tp) memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); sblk = tnapi->hw_status; - if (tg3_rx_prodring_init(tp, &tnapi->prodring)) - goto err_out; + if (tg3_flag(tp, ENABLE_RSS)) { + u16 *prodptr = 0; - /* If multivector TSS is enabled, vector 0 does not handle - * tx interrupts. Don't allocate any resources for it. - */ - if ((!i && !tg3_flag(tp, ENABLE_TSS)) || - (i && tg3_flag(tp, ENABLE_TSS))) { - tnapi->tx_buffers = kzalloc( - sizeof(struct tg3_tx_ring_info) * - TG3_TX_RING_SIZE, GFP_KERNEL); - if (!tnapi->tx_buffers) - goto err_out; - - tnapi->tx_ring = dma_alloc_coherent(&tp->pdev->dev, - TG3_TX_RING_BYTES, - &tnapi->tx_desc_mapping, - GFP_KERNEL); - if (!tnapi->tx_ring) - goto err_out; - } - - /* - * When RSS is enabled, the status block format changes - * slightly. The "rx_jumbo_consumer", "reserved", - * and "rx_mini_consumer" members get mapped to the - * other three rx return ring producer indexes. - */ - switch (i) { - default: - if (tg3_flag(tp, ENABLE_RSS)) { - tnapi->rx_rcb_prod_idx = NULL; + /* + * When RSS is enabled, the status block format changes + * slightly. The "rx_jumbo_consumer", "reserved", + * and "rx_mini_consumer" members get mapped to the + * other three rx return ring producer indexes. + */ + switch (i) { + case 1: + prodptr = &sblk->idx[0].rx_producer; + break; + case 2: + prodptr = &sblk->rx_jumbo_consumer; + break; + case 3: + prodptr = &sblk->reserved; + break; + case 4: + prodptr = &sblk->rx_mini_consumer; break; } - /* Fall through */ - case 1: + tnapi->rx_rcb_prod_idx = prodptr; + } else { tnapi->rx_rcb_prod_idx = &sblk->idx[0].rx_producer; - break; - case 2: - tnapi->rx_rcb_prod_idx = &sblk->rx_jumbo_consumer; - break; - case 3: - tnapi->rx_rcb_prod_idx = &sblk->reserved; - break; - case 4: - tnapi->rx_rcb_prod_idx = &sblk->rx_mini_consumer; - break; } - - /* - * If multivector RSS is enabled, vector 0 does not handle - * rx or tx interrupts. Don't allocate any resources for it. - */ - if (!i && tg3_flag(tp, ENABLE_RSS)) - continue; - - tnapi->rx_rcb = dma_alloc_coherent(&tp->pdev->dev, - TG3_RX_RCB_RING_BYTES(tp), - &tnapi->rx_rcb_mapping, - GFP_KERNEL); - if (!tnapi->rx_rcb) - goto err_out; - - memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); } + if (tg3_mem_tx_acquire(tp) || tg3_mem_rx_acquire(tp)) + goto err_out; + return 0; err_out: @@ -8247,9 +8307,10 @@ static void tg3_set_bdinfo(struct tg3 *tp, u32 bdinfo_addr, nic_addr); } -static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) + +static void tg3_coal_tx_init(struct tg3 *tp, struct ethtool_coalesce *ec) { - int i; + int i = 0; if (!tg3_flag(tp, ENABLE_TSS)) { tw32(HOSTCC_TXCOL_TICKS, ec->tx_coalesce_usecs); @@ -8259,31 +8320,43 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) tw32(HOSTCC_TXCOL_TICKS, 0); tw32(HOSTCC_TXMAX_FRAMES, 0); tw32(HOSTCC_TXCOAL_MAXF_INT, 0); + + for (; i < tp->txq_cnt; i++) { + u32 reg; + + reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18; + tw32(reg, ec->tx_coalesce_usecs); + reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18; + tw32(reg, ec->tx_max_coalesced_frames); + reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18; + tw32(reg, ec->tx_max_coalesced_frames_irq); + } } + for (; i < tp->irq_max - 1; i++) { + tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0); + tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0); + tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); + } +} + +static void tg3_coal_rx_init(struct tg3 *tp, struct ethtool_coalesce *ec) +{ + int i = 0; + u32 limit = tp->rxq_cnt; + if (!tg3_flag(tp, ENABLE_RSS)) { tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs); tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames); tw32(HOSTCC_RXCOAL_MAXF_INT, ec->rx_max_coalesced_frames_irq); + limit--; } else { tw32(HOSTCC_RXCOL_TICKS, 0); tw32(HOSTCC_RXMAX_FRAMES, 0); tw32(HOSTCC_RXCOAL_MAXF_INT, 0); } - if (!tg3_flag(tp, 5705_PLUS)) { - u32 val = ec->stats_block_coalesce_usecs; - - tw32(HOSTCC_RXCOAL_TICK_INT, ec->rx_coalesce_usecs_irq); - tw32(HOSTCC_TXCOAL_TICK_INT, ec->tx_coalesce_usecs_irq); - - if (!netif_carrier_ok(tp->dev)) - val = 0; - - tw32(HOSTCC_STAT_COAL_TICKS, val); - } - - for (i = 0; i < tp->irq_cnt - 1; i++) { + for (; i < limit; i++) { u32 reg; reg = HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18; @@ -8292,27 +8365,30 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) tw32(reg, ec->rx_max_coalesced_frames); reg = HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18; tw32(reg, ec->rx_max_coalesced_frames_irq); - - if (tg3_flag(tp, ENABLE_TSS)) { - reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18; - tw32(reg, ec->tx_coalesce_usecs); - reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18; - tw32(reg, ec->tx_max_coalesced_frames); - reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18; - tw32(reg, ec->tx_max_coalesced_frames_irq); - } } for (; i < tp->irq_max - 1; i++) { tw32(HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18, 0); tw32(HOSTCC_RXMAX_FRAMES_VEC1 + i * 0x18, 0); tw32(HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); + } +} - if (tg3_flag(tp, ENABLE_TSS)) { - tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0); - tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0); - tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0); - } +static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) +{ + tg3_coal_tx_init(tp, ec); + tg3_coal_rx_init(tp, ec); + + if (!tg3_flag(tp, 5705_PLUS)) { + u32 val = ec->stats_block_coalesce_usecs; + + tw32(HOSTCC_RXCOAL_TICK_INT, ec->rx_coalesce_usecs_irq); + tw32(HOSTCC_TXCOAL_TICK_INT, ec->tx_coalesce_usecs_irq); + + if (!netif_carrier_ok(tp->dev)) + val = 0; + + tw32(HOSTCC_STAT_COAL_TICKS, val); } } @@ -8570,13 +8646,12 @@ static void __tg3_set_rx_mode(struct net_device *dev) } } -static void tg3_rss_init_dflt_indir_tbl(struct tg3 *tp) +static void tg3_rss_init_dflt_indir_tbl(struct tg3 *tp, u32 qcnt) { int i; for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) - tp->rss_ind_tbl[i] = - ethtool_rxfh_indir_default(i, tp->irq_cnt - 1); + tp->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i, qcnt); } static void tg3_rss_check_indir_tbl(struct tg3 *tp) @@ -8598,7 +8673,7 @@ static void tg3_rss_check_indir_tbl(struct tg3 *tp) } if (i != TG3_RSS_INDIR_TBL_SIZE) - tg3_rss_init_dflt_indir_tbl(tp); + tg3_rss_init_dflt_indir_tbl(tp, tp->rxq_cnt); } static void tg3_rss_write_indir_tbl(struct tg3 *tp) @@ -9495,7 +9570,6 @@ static int tg3_init_hw(struct tg3 *tp, int reset_phy) return tg3_reset_hw(tp, reset_phy); } -#if IS_ENABLED(CONFIG_HWMON) static void tg3_sd_scan_scratchpad(struct tg3 *tp, struct tg3_ocir *ocir) { int i; @@ -9548,22 +9622,17 @@ static const struct attribute_group tg3_group = { .attrs = tg3_attributes, }; -#endif - static void tg3_hwmon_close(struct tg3 *tp) { -#if IS_ENABLED(CONFIG_HWMON) if (tp->hwmon_dev) { hwmon_device_unregister(tp->hwmon_dev); tp->hwmon_dev = NULL; sysfs_remove_group(&tp->pdev->dev.kobj, &tg3_group); } -#endif } static void tg3_hwmon_open(struct tg3 *tp) { -#if IS_ENABLED(CONFIG_HWMON) int i, err; u32 size = 0; struct pci_dev *pdev = tp->pdev; @@ -9595,7 +9664,6 @@ static void tg3_hwmon_open(struct tg3 *tp) dev_err(&pdev->dev, "Cannot register hwmon device, aborting\n"); sysfs_remove_group(&pdev->dev.kobj, &tg3_group); } -#endif } @@ -10119,21 +10187,43 @@ static int tg3_request_firmware(struct tg3 *tp) return 0; } -static bool tg3_enable_msix(struct tg3 *tp) +static u32 tg3_irq_count(struct tg3 *tp) { - int i, rc; - struct msix_entry msix_ent[tp->irq_max]; + u32 irq_cnt = max(tp->rxq_cnt, tp->txq_cnt); - tp->irq_cnt = netif_get_num_default_rss_queues(); - if (tp->irq_cnt > 1) { + if (irq_cnt > 1) { /* We want as many rx rings enabled as there are cpus. * In multiqueue MSI-X mode, the first MSI-X vector * only deals with link interrupts, etc, so we add * one to the number of vectors we are requesting. */ - tp->irq_cnt = min_t(unsigned, tp->irq_cnt + 1, tp->irq_max); + irq_cnt = min_t(unsigned, irq_cnt + 1, tp->irq_max); } + return irq_cnt; +} + +static bool tg3_enable_msix(struct tg3 *tp) +{ + int i, rc; + struct msix_entry msix_ent[tp->irq_max]; + + tp->txq_cnt = tp->txq_req; + tp->rxq_cnt = tp->rxq_req; + if (!tp->rxq_cnt) + tp->rxq_cnt = netif_get_num_default_rss_queues(); + if (tp->rxq_cnt > tp->rxq_max) + tp->rxq_cnt = tp->rxq_max; + + /* Disable multiple TX rings by default. Simple round-robin hardware + * scheduling of the TX rings can cause starvation of rings with + * small packets when other rings have TSO or jumbo packets. + */ + if (!tp->txq_req) + tp->txq_cnt = 1; + + tp->irq_cnt = tg3_irq_count(tp); + for (i = 0; i < tp->irq_max; i++) { msix_ent[i].entry = i; msix_ent[i].vector = 0; @@ -10148,27 +10238,28 @@ static bool tg3_enable_msix(struct tg3 *tp) netdev_notice(tp->dev, "Requested %d MSI-X vectors, received %d\n", tp->irq_cnt, rc); tp->irq_cnt = rc; + tp->rxq_cnt = max(rc - 1, 1); + if (tp->txq_cnt) + tp->txq_cnt = min(tp->rxq_cnt, tp->txq_max); } for (i = 0; i < tp->irq_max; i++) tp->napi[i].irq_vec = msix_ent[i].vector; - netif_set_real_num_tx_queues(tp->dev, 1); - rc = tp->irq_cnt > 1 ? tp->irq_cnt - 1 : 1; - if (netif_set_real_num_rx_queues(tp->dev, rc)) { + if (netif_set_real_num_rx_queues(tp->dev, tp->rxq_cnt)) { pci_disable_msix(tp->pdev); return false; } - if (tp->irq_cnt > 1) { - tg3_flag_set(tp, ENABLE_RSS); + if (tp->irq_cnt == 1) + return true; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) { - tg3_flag_set(tp, ENABLE_TSS); - netif_set_real_num_tx_queues(tp->dev, tp->irq_cnt - 1); - } - } + tg3_flag_set(tp, ENABLE_RSS); + + if (tp->txq_cnt > 1) + tg3_flag_set(tp, ENABLE_TSS); + + netif_set_real_num_tx_queues(tp->dev, tp->txq_cnt); return true; } @@ -10202,6 +10293,11 @@ defcfg: if (!tg3_flag(tp, USING_MSIX)) { tp->irq_cnt = 1; tp->napi[0].irq_vec = tp->pdev->irq; + } + + if (tp->irq_cnt == 1) { + tp->txq_cnt = 1; + tp->rxq_cnt = 1; netif_set_real_num_tx_queues(tp->dev, 1); netif_set_real_num_rx_queues(tp->dev, 1); } @@ -10219,38 +10315,11 @@ static void tg3_ints_fini(struct tg3 *tp) tg3_flag_clear(tp, ENABLE_TSS); } -static int tg3_open(struct net_device *dev) +static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq) { - struct tg3 *tp = netdev_priv(dev); + struct net_device *dev = tp->dev; int i, err; - if (tp->fw_needed) { - err = tg3_request_firmware(tp); - if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { - if (err) - return err; - } else if (err) { - netdev_warn(tp->dev, "TSO capability disabled\n"); - tg3_flag_clear(tp, TSO_CAPABLE); - } else if (!tg3_flag(tp, TSO_CAPABLE)) { - netdev_notice(tp->dev, "TSO capability restored\n"); - tg3_flag_set(tp, TSO_CAPABLE); - } - } - - netif_carrier_off(tp->dev); - - err = tg3_power_up(tp); - if (err) - return err; - - tg3_full_lock(tp, 0); - - tg3_disable_ints(tp); - tg3_flag_clear(tp, INIT_COMPLETE); - - tg3_full_unlock(tp); - /* * Setup interrupts first so we know how * many NAPI resources to allocate @@ -10284,7 +10353,7 @@ static int tg3_open(struct net_device *dev) tg3_full_lock(tp, 0); - err = tg3_init_hw(tp, 1); + err = tg3_init_hw(tp, reset_phy); if (err) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_free_rings(tp); @@ -10295,7 +10364,7 @@ static int tg3_open(struct net_device *dev) if (err) goto err_out3; - if (tg3_flag(tp, USING_MSI)) { + if (test_irq && tg3_flag(tp, USING_MSI)) { err = tg3_test_msi(tp); if (err) { @@ -10351,20 +10420,18 @@ err_out2: err_out1: tg3_ints_fini(tp); - tg3_frob_aux_power(tp, false); - pci_set_power_state(tp->pdev, PCI_D3hot); + return err; } -static int tg3_close(struct net_device *dev) +static void tg3_stop(struct tg3 *tp) { int i; - struct tg3 *tp = netdev_priv(dev); tg3_napi_disable(tp); tg3_reset_task_cancel(tp); - netif_tx_stop_all_queues(dev); + netif_tx_disable(tp->dev); tg3_timer_stop(tp); @@ -10389,13 +10456,60 @@ static int tg3_close(struct net_device *dev) tg3_ints_fini(tp); - /* Clear stats across close / open calls */ - memset(&tp->net_stats_prev, 0, sizeof(tp->net_stats_prev)); - memset(&tp->estats_prev, 0, sizeof(tp->estats_prev)); - tg3_napi_fini(tp); tg3_free_consistent(tp); +} + +static int tg3_open(struct net_device *dev) +{ + struct tg3 *tp = netdev_priv(dev); + int err; + + if (tp->fw_needed) { + err = tg3_request_firmware(tp); + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { + if (err) + return err; + } else if (err) { + netdev_warn(tp->dev, "TSO capability disabled\n"); + tg3_flag_clear(tp, TSO_CAPABLE); + } else if (!tg3_flag(tp, TSO_CAPABLE)) { + netdev_notice(tp->dev, "TSO capability restored\n"); + tg3_flag_set(tp, TSO_CAPABLE); + } + } + + netif_carrier_off(tp->dev); + + err = tg3_power_up(tp); + if (err) + return err; + + tg3_full_lock(tp, 0); + + tg3_disable_ints(tp); + tg3_flag_clear(tp, INIT_COMPLETE); + + tg3_full_unlock(tp); + + err = tg3_start(tp, true, true); + if (err) { + tg3_frob_aux_power(tp, false); + pci_set_power_state(tp->pdev, PCI_D3hot); + } + return err; +} + +static int tg3_close(struct net_device *dev) +{ + struct tg3 *tp = netdev_priv(dev); + + tg3_stop(tp); + + /* Clear stats across close / open calls */ + memset(&tp->net_stats_prev, 0, sizeof(tp->net_stats_prev)); + memset(&tp->estats_prev, 0, sizeof(tp->estats_prev)); tg3_power_down(tp); @@ -11185,11 +11299,11 @@ static int tg3_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, switch (info->cmd) { case ETHTOOL_GRXRINGS: if (netif_running(tp->dev)) - info->data = tp->irq_cnt; + info->data = tp->rxq_cnt; else { info->data = num_online_cpus(); - if (info->data > TG3_IRQ_MAX_VECS_RSS) - info->data = TG3_IRQ_MAX_VECS_RSS; + if (info->data > TG3_RSS_MAX_NUM_QS) + info->data = TG3_RSS_MAX_NUM_QS; } /* The first interrupt vector only @@ -11246,6 +11360,58 @@ static int tg3_set_rxfh_indir(struct net_device *dev, const u32 *indir) return 0; } +static void tg3_get_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct tg3 *tp = netdev_priv(dev); + u32 deflt_qs = netif_get_num_default_rss_queues(); + + channel->max_rx = tp->rxq_max; + channel->max_tx = tp->txq_max; + + if (netif_running(dev)) { + channel->rx_count = tp->rxq_cnt; + channel->tx_count = tp->txq_cnt; + } else { + if (tp->rxq_req) + channel->rx_count = tp->rxq_req; + else + channel->rx_count = min(deflt_qs, tp->rxq_max); + + if (tp->txq_req) + channel->tx_count = tp->txq_req; + else + channel->tx_count = min(deflt_qs, tp->txq_max); + } +} + +static int tg3_set_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct tg3 *tp = netdev_priv(dev); + + if (!tg3_flag(tp, SUPPORT_MSIX)) + return -EOPNOTSUPP; + + if (channel->rx_count > tp->rxq_max || + channel->tx_count > tp->txq_max) + return -EINVAL; + + tp->rxq_req = channel->rx_count; + tp->txq_req = channel->tx_count; + + if (!netif_running(dev)) + return 0; + + tg3_stop(tp); + + netif_carrier_off(dev); + + tg3_start(tp, true, false); + + return 0; +} + static void tg3_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { switch (stringset) { @@ -12494,6 +12660,8 @@ static const struct ethtool_ops tg3_ethtool_ops = { .get_rxfh_indir_size = tg3_get_rxfh_indir_size, .get_rxfh_indir = tg3_get_rxfh_indir, .set_rxfh_indir = tg3_set_rxfh_indir, + .get_channels = tg3_get_channels, + .set_channels = tg3_set_channels, .get_ts_info = ethtool_op_get_ts_info, }; @@ -14510,10 +14678,20 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (tg3_flag(tp, 57765_PLUS)) { tg3_flag_set(tp, SUPPORT_MSIX); tp->irq_max = TG3_IRQ_MAX_VECS; - tg3_rss_init_dflt_indir_tbl(tp); } } + tp->txq_max = 1; + tp->rxq_max = 1; + if (tp->irq_max > 1) { + tp->rxq_max = TG3_RSS_MAX_NUM_QS; + tg3_rss_init_dflt_indir_tbl(tp, TG3_RSS_MAX_NUM_QS); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) + tp->txq_max = tp->irq_max - 1; + } + if (tg3_flag(tp, 5755_PLUS) || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tg3_flag_set(tp, SHORT_DMA_BUG); |