aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJack F Vogel <jfv@FreeBSD.org>2010-04-05 20:39:44 +0000
committerJack F Vogel <jfv@FreeBSD.org>2010-04-05 20:39:44 +0000
commitd05b20c60cbb95d7ca1748de74c18e404a2b0475 (patch)
treeb862ac3419b9d6a0b0590f8fccca836451936970 /sys
parentd4e2e2a53f013f49b53afe5785c9eba0cab99712 (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/e1000/LICENSE2
-rw-r--r--sys/dev/e1000/e1000_80003es2lan.c268
-rw-r--r--sys/dev/e1000/e1000_80003es2lan.h12
-rw-r--r--sys/dev/e1000/e1000_82540.c4
-rw-r--r--sys/dev/e1000/e1000_82541.c39
-rw-r--r--sys/dev/e1000/e1000_82542.c4
-rw-r--r--sys/dev/e1000/e1000_82543.c86
-rw-r--r--sys/dev/e1000/e1000_82571.c224
-rw-r--r--sys/dev/e1000/e1000_82575.c790
-rw-r--r--sys/dev/e1000/e1000_82575.h84
-rw-r--r--sys/dev/e1000/e1000_api.c38
-rw-r--r--sys/dev/e1000/e1000_api.h4
-rw-r--r--sys/dev/e1000/e1000_defines.h113
-rw-r--r--sys/dev/e1000/e1000_hw.h32
-rw-r--r--sys/dev/e1000/e1000_ich8lan.c1035
-rw-r--r--sys/dev/e1000/e1000_ich8lan.h38
-rw-r--r--sys/dev/e1000/e1000_mac.c127
-rw-r--r--sys/dev/e1000/e1000_mac.h4
-rw-r--r--sys/dev/e1000/e1000_manage.c70
-rw-r--r--sys/dev/e1000/e1000_osdep.h43
-rw-r--r--sys/dev/e1000/e1000_phy.c804
-rw-r--r--sys/dev/e1000/e1000_phy.h24
-rw-r--r--sys/dev/e1000/e1000_regs.h57
-rw-r--r--sys/dev/e1000/if_em.c3584
-rw-r--r--sys/dev/e1000/if_em.h240
-rw-r--r--sys/dev/e1000/if_igb.c2370
-rw-r--r--sys/dev/e1000/if_igb.h127
-rw-r--r--sys/dev/e1000/if_lem.c4706
-rw-r--r--sys/dev/e1000/if_lem.h481
30 files changed, 10707 insertions, 4705 deletions
diff --git a/sys/conf/files b/sys/conf/files
index d4de3668a10c..86e9d4721529 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -899,6 +899,8 @@ dev/eisa/eisa_if.m standard
dev/eisa/eisaconf.c optional eisa
dev/e1000/if_em.c optional em inet \
compile-with "${NORMAL_C} -I$S/dev/e1000"
+dev/e1000/if_lem.c optional em inet \
+ compile-with "${NORMAL_C} -I$S/dev/e1000"
dev/e1000/if_igb.c optional igb inet \
compile-with "${NORMAL_C} -I$S/dev/e1000"
dev/e1000/e1000_80003es2lan.c optional em | igb \
diff --git a/sys/dev/e1000/LICENSE b/sys/dev/e1000/LICENSE
index d3f8bf5f367e..f70a7cbd4a1d 100644
--- a/sys/dev/e1000/LICENSE
+++ b/sys/dev/e1000/LICENSE
@@ -1,6 +1,6 @@
$FreeBSD$
- Copyright (c) 2001-2008, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/e1000/e1000_80003es2lan.c b/sys/dev/e1000/e1000_80003es2lan.c
index 5c060869cf96..af32ee0f1fcd 100644
--- a/sys/dev/e1000/e1000_80003es2lan.c
+++ b/sys/dev/e1000/e1000_80003es2lan.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -171,7 +171,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
break;
}
- nvm->type = e1000_nvm_eeprom_spi;
+ nvm->type = e1000_nvm_eeprom_spi;
size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
E1000_EECD_SIZE_EX_SHIFT);
@@ -206,17 +206,22 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
- s32 ret_val = E1000_SUCCESS;
DEBUGFUNC("e1000_init_mac_params_80003es2lan");
- /* Set media type */
+ /* Set media type and media-dependent function pointers */
switch (hw->device_id) {
case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
hw->phy.media_type = e1000_media_type_internal_serdes;
+ mac->ops.check_for_link = e1000_check_for_serdes_link_generic;
+ mac->ops.setup_physical_interface =
+ e1000_setup_fiber_serdes_link_generic;
break;
default:
hw->phy.media_type = e1000_media_type_copper;
+ mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+ mac->ops.setup_physical_interface =
+ e1000_setup_copper_link_80003es2lan;
break;
}
@@ -226,10 +231,14 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
mac->rar_entry_count = E1000_RAR_ENTRIES;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = TRUE;
- /* Set if manageability features are enabled. */
+ /* FWSM register */
+ mac->has_fwsm = TRUE;
+ /* ARC supported; valid only if manageability features are enabled. */
mac->arc_subsystem_valid =
(E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK)
? TRUE : FALSE;
+ /* Adaptive IFS not supported */
+ mac->adaptive_ifs = FALSE;
/* Function pointers */
@@ -241,27 +250,6 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
mac->ops.init_hw = e1000_init_hw_80003es2lan;
/* link setup */
mac->ops.setup_link = e1000_setup_link_generic;
- /* physical interface link setup */
- mac->ops.setup_physical_interface =
- (hw->phy.media_type == e1000_media_type_copper)
- ? e1000_setup_copper_link_80003es2lan
- : e1000_setup_fiber_serdes_link_generic;
- /* check for link */
- switch (hw->phy.media_type) {
- case e1000_media_type_copper:
- mac->ops.check_for_link = e1000_check_for_copper_link_generic;
- break;
- case e1000_media_type_fiber:
- mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
- break;
- case e1000_media_type_internal_serdes:
- mac->ops.check_for_link = e1000_check_for_serdes_link_generic;
- break;
- default:
- ret_val = -E1000_ERR_CONFIG;
- goto out;
- break;
- }
/* check management mode */
mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
/* multicast address update */
@@ -270,8 +258,6 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* read mac address */
mac->ops.read_mac_addr = e1000_read_mac_addr_80003es2lan;
/* ID LED init */
@@ -290,8 +276,10 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
/* link info */
mac->ops.get_link_up_info = e1000_get_link_up_info_80003es2lan;
-out:
- return ret_val;
+ /* set lan id for port to determine which phy lock to use */
+ hw->mac.ops.set_lan_id(hw);
+
+ return E1000_SUCCESS;
}
/**
@@ -307,7 +295,6 @@ void e1000_init_function_pointers_80003es2lan(struct e1000_hw *hw)
hw->mac.ops.init_params = e1000_init_mac_params_80003es2lan;
hw->nvm.ops.init_params = e1000_init_nvm_params_80003es2lan;
hw->phy.ops.init_params = e1000_init_phy_params_80003es2lan;
- e1000_get_bus_info_pcie_generic(hw);
}
/**
@@ -342,7 +329,6 @@ static void e1000_release_phy_80003es2lan(struct e1000_hw *hw)
e1000_release_swfw_sync_80003es2lan(hw, mask);
}
-
/**
* e1000_acquire_mac_csr_80003es2lan - Acquire rights to access Kumeran register
* @hw: pointer to the HW structure
@@ -532,28 +518,36 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
goto out;
}
- /*
- * The "ready" bit in the MDIC register may be incorrectly set
- * before the device has completed the "Page Select" MDI
- * transaction. So we wait 200us after each MDI command...
- */
- usec_delay(200);
+ if (hw->dev_spec._80003es2lan.mdic_wa_enable == TRUE) {
+ /*
+ * The "ready" bit in the MDIC register may be incorrectly set
+ * before the device has completed the "Page Select" MDI
+ * transaction. So we wait 200us after each MDI command...
+ */
+ usec_delay(200);
- /* ...and verify the command was successful. */
- ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+ /* ...and verify the command was successful. */
+ ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
- if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
- ret_val = -E1000_ERR_PHY;
- e1000_release_phy_80003es2lan(hw);
- goto out;
- }
+ if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+ ret_val = -E1000_ERR_PHY;
+ e1000_release_phy_80003es2lan(hw);
+ goto out;
+ }
- usec_delay(200);
+ usec_delay(200);
- ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
- data);
+ ret_val = e1000_read_phy_reg_mdic(hw,
+ MAX_PHY_REG_ADDRESS & offset,
+ data);
+
+ usec_delay(200);
+ } else {
+ ret_val = e1000_read_phy_reg_mdic(hw,
+ MAX_PHY_REG_ADDRESS & offset,
+ data);
+ }
- usec_delay(200);
e1000_release_phy_80003es2lan(hw);
out:
@@ -599,29 +593,36 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
goto out;
}
+ if (hw->dev_spec._80003es2lan.mdic_wa_enable == TRUE) {
+ /*
+ * The "ready" bit in the MDIC register may be incorrectly set
+ * before the device has completed the "Page Select" MDI
+ * transaction. So we wait 200us after each MDI command...
+ */
+ usec_delay(200);
- /*
- * The "ready" bit in the MDIC register may be incorrectly set
- * before the device has completed the "Page Select" MDI
- * transaction. So we wait 200us after each MDI command...
- */
- usec_delay(200);
+ /* ...and verify the command was successful. */
+ ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
- /* ...and verify the command was successful. */
- ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+ if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+ ret_val = -E1000_ERR_PHY;
+ e1000_release_phy_80003es2lan(hw);
+ goto out;
+ }
- if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
- ret_val = -E1000_ERR_PHY;
- e1000_release_phy_80003es2lan(hw);
- goto out;
- }
+ usec_delay(200);
- usec_delay(200);
+ ret_val = e1000_write_phy_reg_mdic(hw,
+ MAX_PHY_REG_ADDRESS & offset,
+ data);
- ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
- data);
+ usec_delay(200);
+ } else {
+ ret_val = e1000_write_phy_reg_mdic(hw,
+ MAX_PHY_REG_ADDRESS & offset,
+ data);
+ }
- usec_delay(200);
e1000_release_phy_80003es2lan(hw);
out:
@@ -802,13 +803,13 @@ static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw)
index = phy_data & GG82563_DSPD_CABLE_LENGTH;
- if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE + 5) {
- ret_val = E1000_ERR_PHY;
+ if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) {
+ ret_val = -E1000_ERR_PHY;
goto out;
}
phy->min_cable_length = e1000_gg82563_cable_length_table[index];
- phy->max_cable_length = e1000_gg82563_cable_length_table[index+5];
+ phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5];
phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
@@ -916,10 +917,9 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
/* Initialize identification LED */
ret_val = mac->ops.id_led_init(hw);
- if (ret_val) {
+ if (ret_val)
DEBUGOUT("Error initializing identification LED\n");
/* This is not fatal and we should not stop init due to this */
- }
/* Disabling VLAN filtering */
DEBUGOUT("Initializing the IEEE VLAN\n");
@@ -969,6 +969,19 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
reg_data &= ~0x00100000;
E1000_WRITE_REG_ARRAY(hw, E1000_FFLT, 0x0001, reg_data);
+ /* default to TRUE to enable the MDIC W/A */
+ hw->dev_spec._80003es2lan.mdic_wa_enable = TRUE;
+
+ ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
+ E1000_KMRNCTRLSTA_OFFSET >>
+ E1000_KMRNCTRLSTA_OFFSET_SHIFT,
+ &i);
+ if (!ret_val) {
+ if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) ==
+ E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO)
+ hw->dev_spec._80003es2lan.mdic_wa_enable = FALSE;
+ }
+
/*
* Clear all of the statistics registers (clear on read). It is
* important that we do this after we have tried to establish link
@@ -1035,72 +1048,73 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
DEBUGFUNC("e1000_copper_link_setup_gg82563_80003es2lan");
- if (!phy->reset_disable) {
- ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
- &data);
- if (ret_val)
- goto out;
+ if (phy->reset_disable)
+ goto skip_reset;
- data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
- /* Use 25MHz for both link down and 1000Base-T for Tx clock. */
- data |= GG82563_MSCR_TX_CLK_1000MBPS_25;
+ ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+ &data);
+ if (ret_val)
+ goto out;
- ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
- data);
- if (ret_val)
- goto out;
+ data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
+ /* Use 25MHz for both link down and 1000Base-T for Tx clock. */
+ data |= GG82563_MSCR_TX_CLK_1000MBPS_25;
- /*
- * Options:
- * MDI/MDI-X = 0 (default)
- * 0 - Auto for all speeds
- * 1 - MDI mode
- * 2 - MDI-X mode
- * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
- */
- ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL, &data);
- if (ret_val)
- goto out;
+ ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+ data);
+ if (ret_val)
+ goto out;
- data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
+ /*
+ * Options:
+ * MDI/MDI-X = 0 (default)
+ * 0 - Auto for all speeds
+ * 1 - MDI mode
+ * 2 - MDI-X mode
+ * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+ */
+ ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL, &data);
+ if (ret_val)
+ goto out;
- switch (phy->mdix) {
- case 1:
- data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
- break;
- case 2:
- data |= GG82563_PSCR_CROSSOVER_MODE_MDIX;
- break;
- case 0:
- default:
- data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
- break;
- }
+ data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
- /*
- * Options:
- * disable_polarity_correction = 0 (default)
- * Automatic Correction for Reversed Cable Polarity
- * 0 - Disabled
- * 1 - Enabled
- */
- data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
- if (phy->disable_polarity_correction)
- data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+ switch (phy->mdix) {
+ case 1:
+ data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
+ break;
+ case 2:
+ data |= GG82563_PSCR_CROSSOVER_MODE_MDIX;
+ break;
+ case 0:
+ default:
+ data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
+ break;
+ }
- ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, data);
- if (ret_val)
- goto out;
+ /*
+ * Options:
+ * disable_polarity_correction = 0 (default)
+ * Automatic Correction for Reversed Cable Polarity
+ * 0 - Disabled
+ * 1 - Enabled
+ */
+ data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+ if (phy->disable_polarity_correction)
+ data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
- /* SW Reset the PHY so all changes take effect */
- ret_val = hw->phy.ops.commit(hw);
- if (ret_val) {
- DEBUGOUT("Error Resetting the PHY\n");
- goto out;
- }
+ ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, data);
+ if (ret_val)
+ goto out;
+ /* SW Reset the PHY so all changes take effect */
+ ret_val = hw->phy.ops.commit(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ goto out;
}
+skip_reset:
/* Bypass Rx and Tx FIFO's */
ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL,
@@ -1303,7 +1317,6 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex)
tipg |= DEFAULT_TIPG_IPGT_10_100_80003ES2LAN;
E1000_WRITE_REG(hw, E1000_TIPG, tipg);
-
do {
ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
&reg_data);
@@ -1357,7 +1370,6 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw)
tipg |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN;
E1000_WRITE_REG(hw, E1000_TIPG, tipg);
-
do {
ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
&reg_data);
diff --git a/sys/dev/e1000/e1000_80003es2lan.h b/sys/dev/e1000/e1000_80003es2lan.h
index 7bf8d9d54362..3ab1ec9d04e1 100644
--- a/sys/dev/e1000/e1000_80003es2lan.h
+++ b/sys/dev/e1000/e1000_80003es2lan.h
@@ -1,6 +1,6 @@
-/*******************************************************************************
+/******************************************************************************
- Copyright (c) 2001-2008, Intel Corporation
+ Copyright (c) 2001-2009, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -29,9 +29,8 @@
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-*******************************************************************************/
-/* $FreeBSD$ */
-
+******************************************************************************/
+/*$FreeBSD$*/
#ifndef _E1000_80003ES2LAN_H_
#define _E1000_80003ES2LAN_H_
@@ -49,6 +48,9 @@
#define E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT 0x0000
#define E1000_KMRNCTRLSTA_OPMODE_E_IDLE 0x2000
+#define E1000_KMRNCTRLSTA_OPMODE_MASK 0x000C
+#define E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO 0x0004
+
#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */
#define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN 0x00010000
diff --git a/sys/dev/e1000/e1000_82540.c b/sys/dev/e1000/e1000_82540.c
index 14dcbb3e35d3..80a5877725cf 100644
--- a/sys/dev/e1000/e1000_82540.c
+++ b/sys/dev/e1000/e1000_82540.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -228,8 +228,6 @@ static s32 e1000_init_mac_params_82540(struct e1000_hw *hw)
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* read mac address */
mac->ops.read_mac_addr = e1000_read_mac_addr_82540;
/* ID LED init */
diff --git a/sys/dev/e1000/e1000_82541.c b/sys/dev/e1000/e1000_82541.c
index 68d1b05a07b5..fd8d8ebbe746 100644
--- a/sys/dev/e1000/e1000_82541.c
+++ b/sys/dev/e1000/e1000_82541.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -59,6 +59,7 @@ static s32 e1000_set_d3_lplu_state_82541(struct e1000_hw *hw,
static s32 e1000_setup_led_82541(struct e1000_hw *hw);
static s32 e1000_cleanup_led_82541(struct e1000_hw *hw);
static void e1000_clear_hw_cntrs_82541(struct e1000_hw *hw);
+static s32 e1000_read_mac_addr_82541(struct e1000_hw *hw);
static s32 e1000_config_dsp_after_link_change_82541(struct e1000_hw *hw,
bool link_up);
static s32 e1000_phy_init_script_82541(struct e1000_hw *hw);
@@ -259,8 +260,8 @@ static s32 e1000_init_mac_params_82541(struct e1000_hw *hw)
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
+ /* read mac address */
+ mac->ops.read_mac_addr = e1000_read_mac_addr_82541;
/* ID LED init */
mac->ops.id_led_init = e1000_id_led_init_generic;
/* setup LED */
@@ -1292,3 +1293,35 @@ static void e1000_clear_hw_cntrs_82541(struct e1000_hw *hw)
E1000_READ_REG(hw, E1000_MGTPDC);
E1000_READ_REG(hw, E1000_MGTPTC);
}
+
+/**
+ * e1000_read_mac_addr_82541 - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ **/
+static s32 e1000_read_mac_addr_82541(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 offset, nvm_data, i;
+
+ DEBUGFUNC("e1000_read_mac_addr");
+
+ for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+ offset = i >> 1;
+ ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+ if (ret_val) {
+ DEBUGOUT("NVM Read Error\n");
+ goto out;
+ }
+ hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+ hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+ }
+
+ for (i = 0; i < ETH_ADDR_LEN; i++)
+ hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+out:
+ return ret_val;
+}
+
diff --git a/sys/dev/e1000/e1000_82542.c b/sys/dev/e1000/e1000_82542.c
index 46ef66a0a512..282814bb8934 100644
--- a/sys/dev/e1000/e1000_82542.c
+++ b/sys/dev/e1000/e1000_82542.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -134,8 +134,6 @@ static s32 e1000_init_mac_params_82542(struct e1000_hw *hw)
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* read mac address */
mac->ops.read_mac_addr = e1000_read_mac_addr_82542;
/* set RAR */
diff --git a/sys/dev/e1000/e1000_82543.c b/sys/dev/e1000/e1000_82543.c
index 97c7f3b2044f..4bb0cbdd5c33 100644
--- a/sys/dev/e1000/e1000_82543.c
+++ b/sys/dev/e1000/e1000_82543.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2008, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -63,7 +63,6 @@ static s32 e1000_led_on_82543(struct e1000_hw *hw);
static s32 e1000_led_off_82543(struct e1000_hw *hw);
static void e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset,
u32 value);
-static void e1000_mta_set_82543(struct e1000_hw *hw, u32 hash_value);
static void e1000_clear_hw_cntrs_82543(struct e1000_hw *hw);
static s32 e1000_config_mac_to_phy_82543(struct e1000_hw *hw);
static bool e1000_init_phy_disabled_82543(struct e1000_hw *hw);
@@ -75,6 +74,8 @@ static void e1000_shift_out_mdi_bits_82543(struct e1000_hw *hw, u32 data,
u16 count);
static bool e1000_tbi_compatibility_enabled_82543(struct e1000_hw *hw);
static void e1000_set_tbi_sbp_82543(struct e1000_hw *hw, bool state);
+static s32 e1000_read_mac_addr_82543(struct e1000_hw *hw);
+
/**
* e1000_init_phy_params_82543 - Init PHY func ptrs.
@@ -244,8 +245,8 @@ static s32 e1000_init_mac_params_82543(struct e1000_hw *hw)
mac->ops.write_vfta = e1000_write_vfta_82543;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_82543;
+ /* read mac address */
+ mac->ops.read_mac_addr = e1000_read_mac_addr_82543;
/* turn on/off LED */
mac->ops.led_on = e1000_led_on_82543;
mac->ops.led_off = e1000_led_off_82543;
@@ -1477,45 +1478,6 @@ static void e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset, u32 value)
}
/**
- * e1000_mta_set_82543 - Set multicast filter table address
- * @hw: pointer to the HW structure
- * @hash_value: determines the MTA register and bit to set
- *
- * The multicast table address is a register array of 32-bit registers.
- * The hash_value is used to determine what register the bit is in, the
- * current value is read, the new bit is OR'd in and the new value is
- * written back into the register.
- **/
-static void e1000_mta_set_82543(struct e1000_hw *hw, u32 hash_value)
-{
- u32 hash_bit, hash_reg, mta, temp;
-
- DEBUGFUNC("e1000_mta_set_82543");
-
- hash_reg = (hash_value >> 5);
-
- /*
- * If we are on an 82544 and we are trying to write an odd offset
- * in the MTA, save off the previous entry before writing and
- * restore the old value after writing.
- */
- if ((hw->mac.type == e1000_82544) && (hash_reg & 1)) {
- hash_reg &= (hw->mac.mta_reg_count - 1);
- hash_bit = hash_value & 0x1F;
- mta = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg);
- mta |= (1 << hash_bit);
- temp = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg - 1);
-
- E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg, mta);
- E1000_WRITE_FLUSH(hw);
- E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg - 1, temp);
- E1000_WRITE_FLUSH(hw);
- } else {
- e1000_mta_set_generic(hw, hash_value);
- }
-}
-
-/**
* e1000_led_on_82543 - Turn on SW controllable LED
* @hw: pointer to the HW structure
*
@@ -1600,3 +1562,41 @@ static void e1000_clear_hw_cntrs_82543(struct e1000_hw *hw)
E1000_READ_REG(hw, E1000_TSCTC);
E1000_READ_REG(hw, E1000_TSCTFC);
}
+
+/**
+ * e1000_read_mac_addr_82543 - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ * Since devices with two ports use the same EEPROM, we increment the
+ * last bit in the MAC address for the second port.
+ *
+ **/
+s32 e1000_read_mac_addr_82543(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 offset, nvm_data, i;
+
+ DEBUGFUNC("e1000_read_mac_addr");
+
+ for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+ offset = i >> 1;
+ ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+ if (ret_val) {
+ DEBUGOUT("NVM Read Error\n");
+ goto out;
+ }
+ hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+ hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+ }
+
+ /* Flip last bit of mac address if we're on second port */
+ if (hw->bus.func == E1000_FUNC_1)
+ hw->mac.perm_addr[5] ^= 1;
+
+ for (i = 0; i < ETH_ADDR_LEN; i++)
+ hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+out:
+ return ret_val;
+}
diff --git a/sys/dev/e1000/e1000_82571.c b/sys/dev/e1000/e1000_82571.c
index 18fe745b14a5..afeb1a05e5c3 100644
--- a/sys/dev/e1000/e1000_82571.c
+++ b/sys/dev/e1000/e1000_82571.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -46,7 +46,6 @@
* 82573E Gigabit Ethernet Controller (Copper)
* 82573L Gigabit Ethernet Controller
* 82574L Gigabit Network Connection
- * 82574L Gigabit Network Connection
* 82583V Gigabit Network Connection
*/
@@ -106,7 +105,6 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
phy->reset_delay_us = 100;
phy->ops.acquire = e1000_get_hw_semaphore_82571;
- phy->ops.check_polarity = e1000_check_polarity_igp;
phy->ops.check_reset_block = e1000_check_reset_block_generic;
phy->ops.release = e1000_put_hw_semaphore_82571;
phy->ops.reset = e1000_phy_hw_reset_generic;
@@ -121,6 +119,7 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
phy->type = e1000_phy_igp_2;
phy->ops.get_cfg_done = e1000_get_cfg_done_82571;
phy->ops.get_info = e1000_get_phy_info_igp;
+ phy->ops.check_polarity = e1000_check_polarity_igp;
phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp;
phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
phy->ops.read_reg = e1000_read_phy_reg_igp;
@@ -132,6 +131,7 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
/* Verify PHY ID */
if (phy->id != IGP01E1000_I_PHY_ID) {
ret_val = -E1000_ERR_PHY;
+ DEBUGOUT1("PHY ID unknown: type = 0x%08x\n", phy->id);
goto out;
}
break;
@@ -139,6 +139,7 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
phy->type = e1000_phy_m88;
phy->ops.get_cfg_done = e1000_get_cfg_done_generic;
phy->ops.get_info = e1000_get_phy_info_m88;
+ phy->ops.check_polarity = e1000_check_polarity_m88;
phy->ops.commit = e1000_phy_sw_reset_generic;
phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
phy->ops.get_cable_length = e1000_get_cable_length_m88;
@@ -155,11 +156,12 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
goto out;
}
break;
- case e1000_82583:
case e1000_82574:
+ case e1000_82583:
phy->type = e1000_phy_bm;
phy->ops.get_cfg_done = e1000_get_cfg_done_generic;
phy->ops.get_info = e1000_get_phy_info_m88;
+ phy->ops.check_polarity = e1000_check_polarity_m88;
phy->ops.commit = e1000_phy_sw_reset_generic;
phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
phy->ops.get_cable_length = e1000_get_cable_length_m88;
@@ -266,28 +268,42 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
static s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
- s32 ret_val = E1000_SUCCESS;
u32 swsm = 0;
u32 swsm2 = 0;
bool force_clear_smbi = FALSE;
DEBUGFUNC("e1000_init_mac_params_82571");
- /* Set media type */
+ /* Set media type and media-dependent function pointers */
switch (hw->device_id) {
case E1000_DEV_ID_82571EB_FIBER:
case E1000_DEV_ID_82572EI_FIBER:
case E1000_DEV_ID_82571EB_QUAD_FIBER:
hw->phy.media_type = e1000_media_type_fiber;
+ mac->ops.setup_physical_interface =
+ e1000_setup_fiber_serdes_link_82571;
+ mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
+ mac->ops.get_link_up_info =
+ e1000_get_speed_and_duplex_fiber_serdes_generic;
break;
case E1000_DEV_ID_82571EB_SERDES:
case E1000_DEV_ID_82571EB_SERDES_DUAL:
case E1000_DEV_ID_82571EB_SERDES_QUAD:
case E1000_DEV_ID_82572EI_SERDES:
hw->phy.media_type = e1000_media_type_internal_serdes;
+ mac->ops.setup_physical_interface =
+ e1000_setup_fiber_serdes_link_82571;
+ mac->ops.check_for_link = e1000_check_for_serdes_link_82571;
+ mac->ops.get_link_up_info =
+ e1000_get_speed_and_duplex_fiber_serdes_generic;
break;
default:
hw->phy.media_type = e1000_media_type_copper;
+ mac->ops.setup_physical_interface =
+ e1000_setup_copper_link_82571;
+ mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+ mac->ops.get_link_up_info =
+ e1000_get_speed_and_duplex_copper_generic;
break;
}
@@ -297,70 +313,25 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
mac->rar_entry_count = E1000_RAR_ENTRIES;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = TRUE;
- /* Set if manageability features are enabled. */
- mac->arc_subsystem_valid =
- (E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK)
- ? TRUE : FALSE;
+ /* Adaptive IFS supported */
+ mac->adaptive_ifs = TRUE;
/* Function pointers */
/* bus type/speed/width */
mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic;
- /* function id */
- switch (hw->mac.type) {
- case e1000_82573:
- case e1000_82574:
- case e1000_82583:
- mac->ops.set_lan_id = e1000_set_lan_id_single_port;
- break;
- default:
- break;
- }
/* reset */
mac->ops.reset_hw = e1000_reset_hw_82571;
/* hw initialization */
mac->ops.init_hw = e1000_init_hw_82571;
/* link setup */
mac->ops.setup_link = e1000_setup_link_82571;
- /* physical interface link setup */
- mac->ops.setup_physical_interface =
- (hw->phy.media_type == e1000_media_type_copper)
- ? e1000_setup_copper_link_82571
- : e1000_setup_fiber_serdes_link_82571;
- /* check for link */
- switch (hw->phy.media_type) {
- case e1000_media_type_copper:
- mac->ops.check_for_link = e1000_check_for_copper_link_generic;
- break;
- case e1000_media_type_fiber:
- mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
- break;
- case e1000_media_type_internal_serdes:
- mac->ops.check_for_link = e1000_check_for_serdes_link_82571;
- break;
- default:
- ret_val = -E1000_ERR_CONFIG;
- goto out;
- break;
- }
- /* check management mode */
- switch (hw->mac.type) {
- case e1000_82574:
- case e1000_82583:
- mac->ops.check_mng_mode = e1000_check_mng_mode_82574;
- break;
- default:
- mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
- break;
- }
/* multicast address update */
mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
/* writing VFTA */
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_82571;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* read mac address */
mac->ops.read_mac_addr = e1000_read_mac_addr_82571;
/* ID LED init */
@@ -371,24 +342,42 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
mac->ops.setup_led = e1000_setup_led_generic;
/* cleanup LED */
mac->ops.cleanup_led = e1000_cleanup_led_generic;
- /* turn on/off LED */
+ /* turn off LED */
+ mac->ops.led_off = e1000_led_off_generic;
+ /* clear hardware counters */
+ mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82571;
+
+ /* MAC-specific function pointers */
switch (hw->mac.type) {
+ case e1000_82573:
+ mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+ mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
+ mac->ops.led_on = e1000_led_on_generic;
+
+ /* FWSM register */
+ mac->has_fwsm = TRUE;
+ /*
+ * ARC supported; valid only if manageability features are
+ * enabled.
+ */
+ mac->arc_subsystem_valid =
+ (E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK)
+ ? TRUE : FALSE;
+ break;
case e1000_82574:
case e1000_82583:
+ mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+ mac->ops.check_mng_mode = e1000_check_mng_mode_82574;
mac->ops.led_on = e1000_led_on_82574;
break;
default:
+ mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
mac->ops.led_on = e1000_led_on_generic;
+
+ /* FWSM register */
+ mac->has_fwsm = TRUE;
break;
}
- mac->ops.led_off = e1000_led_off_generic;
- /* clear hardware counters */
- mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82571;
- /* link info */
- mac->ops.get_link_up_info =
- (hw->phy.media_type == e1000_media_type_copper)
- ? e1000_get_speed_and_duplex_copper_generic
- : e1000_get_speed_and_duplex_fiber_serdes_generic;
/*
* Ensure that the inter-port SWSM.SMBI lock bit is clear before
@@ -434,8 +423,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
*/
hw->dev_spec._82571.smb_counter = 0;
-out:
- return ret_val;
+ return E1000_SUCCESS;
}
/**
@@ -501,7 +489,6 @@ static s32 e1000_get_phy_id_82571(struct e1000_hw *hw)
ret_val = -E1000_ERR_PHY;
break;
}
-
out:
return ret_val;
}
@@ -512,7 +499,7 @@ out:
*
* Acquire the HW semaphore to access the PHY or NVM
**/
-s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
+static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
{
u32 swsm;
s32 ret_val = E1000_SUCCESS;
@@ -577,7 +564,7 @@ out:
*
* Release hardware semaphore used to access the PHY or NVM
**/
-void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
+static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
{
u32 swsm;
@@ -610,9 +597,9 @@ static s32 e1000_acquire_nvm_82571(struct e1000_hw *hw)
goto out;
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
break;
default:
ret_val = e1000_acquire_nvm_generic(hw);
@@ -831,7 +818,8 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw)
DEBUGFUNC("e1000_get_cfg_done_82571");
while (timeout) {
- if (E1000_READ_REG(hw, E1000_EEMNGCTL) & E1000_NVM_CFG_DONE_PORT_0)
+ if (E1000_READ_REG(hw, E1000_EEMNGCTL) &
+ E1000_NVM_CFG_DONE_PORT_0)
break;
msec_delay(1);
timeout--;
@@ -966,9 +954,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
* Ownership defaults to firmware after a reset.
*/
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
@@ -1014,9 +1002,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
*/
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
msec_delay(25);
break;
default:
@@ -1061,10 +1049,9 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
/* Initialize identification LED */
ret_val = mac->ops.id_led_init(hw);
- if (ret_val) {
+ if (ret_val)
DEBUGOUT("Error initializing identification LED\n");
/* This is not fatal and we should not stop init due to this */
- }
/* Disabling VLAN filtering */
DEBUGOUT("Initializing the IEEE VLAN\n");
@@ -1097,10 +1084,11 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
/* ...for both queues. */
switch (mac->type) {
- case e1000_82574:
- case e1000_82583:
case e1000_82573:
e1000_enable_tx_pkt_filtering_generic(hw);
+ /* fall through */
+ case e1000_82574:
+ case e1000_82583:
reg_data = E1000_READ_REG(hw, E1000_GCR);
reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX;
E1000_WRITE_REG(hw, E1000_GCR, reg_data);
@@ -1108,8 +1096,8 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
default:
reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1));
reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
- E1000_TXDCTL_FULL_TX_DESC_WB |
- E1000_TXDCTL_COUNT_DESC;
+ E1000_TXDCTL_FULL_TX_DESC_WB |
+ E1000_TXDCTL_COUNT_DESC;
E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data);
break;
}
@@ -1178,11 +1166,10 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
}
/* Device Control */
-
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
reg = E1000_READ_REG(hw, E1000_CTRL);
reg &= ~(1 << 29);
E1000_WRITE_REG(hw, E1000_CTRL, reg);
@@ -1193,9 +1180,9 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
/* Extended Device Control */
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
reg &= ~(1 << 23);
reg |= (1 << 22);
@@ -1205,7 +1192,6 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
break;
}
-
if (hw->mac.type == e1000_82571) {
reg = E1000_READ_REG(hw, E1000_PBA_ECC);
reg |= E1000_PBA_ECC_CORR_EN;
@@ -1216,7 +1202,6 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
* Workaround for hardware errata.
* Ensure that DMA Dynamic Clock gating is disabled on 82571 and 82572
*/
-
if ((hw->mac.type == e1000_82571) ||
(hw->mac.type == e1000_82572)) {
reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
@@ -1225,13 +1210,13 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
}
/* PCI-Ex Control Registers */
-
switch (hw->mac.type) {
case e1000_82574:
case e1000_82583:
reg = E1000_READ_REG(hw, E1000_GCR);
reg |= (1 << 22);
E1000_WRITE_REG(hw, E1000_GCR, reg);
+
/*
* Workaround for hardware errata.
* apply workaround for hardware errata documented in errata
@@ -1267,39 +1252,36 @@ static void e1000_clear_vfta_82571(struct e1000_hw *hw)
DEBUGFUNC("e1000_clear_vfta_82571");
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
if (hw->mng_cookie.vlan_id != 0) {
/*
- *The VFTA is a 4096b bit-field, each identifying
- *a single VLAN ID. The following operations
- *determine which 32b entry (i.e. offset) into the
- *array we want to set the VLAN ID (i.e. bit) of
- *the manageability unit.
- */
+ * The VFTA is a 4096b bit-field, each identifying
+ * a single VLAN ID. The following operations
+ * determine which 32b entry (i.e. offset) into the
+ * array we want to set the VLAN ID (i.e. bit) of
+ * the manageability unit.
+ */
vfta_offset = (hw->mng_cookie.vlan_id >>
E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK;
vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id &
E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
}
-
- for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
- /*
- *If the offset we want to clear is the same offset of
- *the manageability VLAN ID, then clear all bits except
- *that of the manageability unit
- */
- vfta_value = (offset == vfta_offset) ?
- vfta_bit_in_reg : 0;
- E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset,
- vfta_value);
- E1000_WRITE_FLUSH(hw);
- }
break;
default:
break;
}
+ for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
+ /*
+ * If the offset we want to clear is the same offset of the
+ * manageability VLAN ID, then clear all bits except that of
+ * the manageability unit.
+ */
+ vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0;
+ E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, vfta_value);
+ E1000_WRITE_FLUSH(hw);
+ }
}
/**
@@ -1369,9 +1351,9 @@ static s32 e1000_setup_link_82571(struct e1000_hw *hw)
* set it to full.
*/
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
if (hw->fc.requested_mode == e1000_fc_default)
hw->fc.requested_mode = e1000_fc_full;
break;
@@ -1392,7 +1374,7 @@ static s32 e1000_setup_link_82571(struct e1000_hw *hw)
static s32 e1000_setup_copper_link_82571(struct e1000_hw *hw)
{
u32 ctrl;
- s32 ret_val;
+ s32 ret_val;
DEBUGFUNC("e1000_setup_copper_link_82571");
@@ -1460,7 +1442,7 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw)
* Reports the link state as up or down.
*
* If autonegotiation is supported by the link partner, the link state is
- * determined by the result of autongotiation. This is the most likely case.
+ * determined by the result of autonegotiation. This is the most likely case.
* If autonegotiation is not supported by the link partner, and the link
* has a valid signal, force the link up.
*
@@ -1472,7 +1454,7 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw)
* 4) forced_up (the link has been forced up, it did not autonegotiate)
*
**/
-s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
+static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
u32 rxcw;
@@ -1524,9 +1506,10 @@ s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
case e1000_serdes_link_autoneg_progress:
if (rxcw & E1000_RXCW_C) {
- /* We received /C/ ordered sets, meaning the
+ /*
+ * We received /C/ ordered sets, meaning the
* link partner has autonegotiated, and we can
- * trust the Link Up (LU) status bit
+ * trust the Link Up (LU) status bit.
*/
if (status & E1000_STATUS_LU) {
mac->serdes_link_state =
@@ -1534,13 +1517,14 @@ s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
DEBUGOUT("AN_PROG -> AN_UP\n");
mac->serdes_has_link = TRUE;
} else {
- /* Autoneg completed, but failed */
+ /* Autoneg completed, but failed. */
mac->serdes_link_state =
e1000_serdes_link_down;
DEBUGOUT("AN_PROG -> DOWN\n");
}
} else {
- /* The link partner did not autoneg.
+ /*
+ * The link partner did not autoneg.
* Force link up and full duplex, and change
* state to forced.
*/
@@ -1565,9 +1549,11 @@ s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
case e1000_serdes_link_down:
default:
- /* The link was down but the receiver has now gained
+ /*
+ * The link was down but the receiver has now gained
* valid sync, so lets see if we can bring the link
- * up. */
+ * up.
+ */
E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
E1000_WRITE_REG(hw, E1000_CTRL,
(ctrl & ~E1000_CTRL_SLU));
@@ -1583,9 +1569,9 @@ s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
DEBUGOUT("ANYSTATE -> DOWN\n");
} else {
/*
- * We have sync, and can tolerate one
- * invalid (IV) codeword before declaring
- * link down, so reread to look again
+ * We have sync, and can tolerate one invalid (IV)
+ * codeword before declaring link down, so reread
+ * to look again.
*/
usec_delay(10);
rxcw = E1000_READ_REG(hw, E1000_RXCW);
@@ -1621,15 +1607,15 @@ static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data)
}
switch (hw->mac.type) {
+ case e1000_82573:
case e1000_82574:
case e1000_82583:
- case e1000_82573:
- if(*data == ID_LED_RESERVED_F746)
+ if (*data == ID_LED_RESERVED_F746)
*data = ID_LED_DEFAULT_82573;
break;
default:
if (*data == ID_LED_RESERVED_0000 ||
- *data == ID_LED_RESERVED_FFFF)
+ *data == ID_LED_RESERVED_FFFF)
*data = ID_LED_DEFAULT;
break;
}
diff --git a/sys/dev/e1000/e1000_82575.c b/sys/dev/e1000/e1000_82575.c
index 2f8e8ed31edb..65b3ce47a41a 100644
--- a/sys/dev/e1000/e1000_82575.c
+++ b/sys/dev/e1000/e1000_82575.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -59,16 +59,20 @@ static s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw);
static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
u16 *data);
static s32 e1000_reset_hw_82575(struct e1000_hw *hw);
+static s32 e1000_reset_hw_82580(struct e1000_hw *hw);
+static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw,
+ u32 offset, u16 *data);
+static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw,
+ u32 offset, u16 data);
static s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw,
bool active);
static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw);
-static s32 e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw);
+static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw);
static s32 e1000_valid_led_default_82575(struct e1000_hw *hw, u16 *data);
static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw,
u32 offset, u16 data);
static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw);
static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask);
-static s32 e1000_configure_pcs_link_82575(struct e1000_hw *hw);
static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw,
u16 *speed, u16 *duplex);
static s32 e1000_get_phy_id_82575(struct e1000_hw *hw);
@@ -76,10 +80,18 @@ static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask);
static bool e1000_sgmii_active_82575(struct e1000_hw *hw);
static s32 e1000_reset_init_script_82575(struct e1000_hw *hw);
static s32 e1000_read_mac_addr_82575(struct e1000_hw *hw);
+static void e1000_config_collision_dist_82575(struct e1000_hw *hw);
static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw);
-void e1000_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw);
+static void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw);
+static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw);
static s32 e1000_set_pcie_completion_timeout(struct e1000_hw *hw);
+static const u16 e1000_82580_rxpbs_table[] =
+ { 36, 72, 144, 1, 2, 4, 8, 16,
+ 35, 70, 140 };
+#define E1000_82580_RXPBS_TABLE_SIZE \
+ (sizeof(e1000_82580_rxpbs_table)/sizeof(u16))
+
/**
* e1000_init_phy_params_82575 - Init PHY func ptrs.
* @hw: pointer to the HW structure
@@ -94,11 +106,11 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw)
if (hw->phy.media_type != e1000_media_type_copper) {
phy->type = e1000_phy_none;
goto out;
- } else {
- phy->ops.power_up = e1000_power_up_phy_copper;
- phy->ops.power_down = e1000_power_down_phy_copper_82575;
}
+ phy->ops.power_up = e1000_power_up_phy_copper;
+ phy->ops.power_down = e1000_power_down_phy_copper_82575;
+
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
phy->reset_delay_us = 100;
@@ -112,6 +124,10 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw)
phy->ops.reset = e1000_phy_hw_reset_sgmii_82575;
phy->ops.read_reg = e1000_read_phy_reg_sgmii_82575;
phy->ops.write_reg = e1000_write_phy_reg_sgmii_82575;
+ } else if (hw->mac.type >= e1000_82580) {
+ phy->ops.reset = e1000_phy_hw_reset_generic;
+ phy->ops.read_reg = e1000_read_phy_reg_82580;
+ phy->ops.write_reg = e1000_write_phy_reg_82580;
} else {
phy->ops.reset = e1000_phy_hw_reset_generic;
phy->ops.read_reg = e1000_read_phy_reg_igp;
@@ -140,6 +156,13 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw)
phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82575;
phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_generic;
break;
+ case I82580_I_PHY_ID:
+ phy->type = e1000_phy_82580;
+ phy->ops.check_polarity = e1000_check_polarity_82577;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_82577;
+ phy->ops.get_cable_length = e1000_get_cable_length_82577;
+ phy->ops.get_info = e1000_get_phy_info_82577;
+ break;
default:
ret_val = -E1000_ERR_PHY;
goto out;
@@ -192,7 +215,7 @@ static s32 e1000_init_nvm_params_82575(struct e1000_hw *hw)
/* EEPROM access above 16k is unsupported */
if (size > 14)
size = 14;
- nvm->word_size = 1 << size;
+ nvm->word_size = 1 << size;
/* Function Pointers */
nvm->ops.acquire = e1000_acquire_nvm_82575;
@@ -230,27 +253,45 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
dev_spec->sgmii_active = FALSE;
ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
- if ((ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) ==
- E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES) {
- hw->phy.media_type = e1000_media_type_internal_serdes;
- ctrl_ext |= E1000_CTRL_I2C_ENA;
- } else if (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_SGMII) {
+ switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
+ case E1000_CTRL_EXT_LINK_MODE_SGMII:
dev_spec->sgmii_active = TRUE;
ctrl_ext |= E1000_CTRL_I2C_ENA;
- } else {
+ break;
+ case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+ case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
+ hw->phy.media_type = e1000_media_type_internal_serdes;
+ ctrl_ext |= E1000_CTRL_I2C_ENA;
+ break;
+ default:
ctrl_ext &= ~E1000_CTRL_I2C_ENA;
+ break;
}
+
E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+ /*
+ * if using i2c make certain the MDICNFG register is cleared to prevent
+ * communications from being misrouted to the mdic registers
+ */
+ if ((ctrl_ext & E1000_CTRL_I2C_ENA) && (hw->mac.type == e1000_82580))
+ E1000_WRITE_REG(hw, E1000_MDICNFG, 0);
+
/* Set mta register count */
mac->mta_reg_count = 128;
+ /* Set uta register count */
+ mac->uta_reg_count = (hw->mac.type == e1000_82575) ? 0 : 128;
/* Set rar entry count */
mac->rar_entry_count = E1000_RAR_ENTRIES_82575;
if (mac->type == e1000_82576)
mac->rar_entry_count = E1000_RAR_ENTRIES_82576;
+ if (mac->type == e1000_82580)
+ mac->rar_entry_count = E1000_RAR_ENTRIES_82580;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = TRUE;
- /* Set if manageability features are enabled. */
+ /* FWSM register */
+ mac->has_fwsm = TRUE;
+ /* ARC supported; valid only if manageability features are enabled. */
mac->arc_subsystem_valid =
(E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK)
? TRUE : FALSE;
@@ -260,6 +301,9 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
/* bus type/speed/width */
mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic;
/* reset */
+ if (mac->type >= e1000_82580)
+ mac->ops.reset_hw = e1000_reset_hw_82580;
+ else
mac->ops.reset_hw = e1000_reset_hw_82575;
/* hw initialization */
mac->ops.init_hw = e1000_init_hw_82575;
@@ -269,23 +313,25 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
mac->ops.setup_physical_interface =
(hw->phy.media_type == e1000_media_type_copper)
? e1000_setup_copper_link_82575
- : e1000_setup_fiber_serdes_link_82575;
+ : e1000_setup_serdes_link_82575;
/* physical interface shutdown */
- mac->ops.shutdown_serdes = e1000_shutdown_fiber_serdes_link_82575;
+ mac->ops.shutdown_serdes = e1000_shutdown_serdes_link_82575;
+ /* physical interface power up */
+ mac->ops.power_up_serdes = e1000_power_up_serdes_link_82575;
/* check for link */
mac->ops.check_for_link = e1000_check_for_link_82575;
/* receive address register setting */
mac->ops.rar_set = e1000_rar_set_generic;
/* read mac address */
mac->ops.read_mac_addr = e1000_read_mac_addr_82575;
+ /* configure collision distance */
+ mac->ops.config_collision_dist = e1000_config_collision_dist_82575;
/* multicast address update */
mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
/* writing VFTA */
mac->ops.write_vfta = e1000_write_vfta_generic;
/* clearing VFTA */
mac->ops.clear_vfta = e1000_clear_vfta_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* ID LED init */
mac->ops.id_led_init = e1000_id_led_init_generic;
/* blink LED */
@@ -302,6 +348,9 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
/* link info */
mac->ops.get_link_up_info = e1000_get_link_up_info_82575;
+ /* set lan id for port to determine which phy lock to use */
+ hw->mac.ops.set_lan_id(hw);
+
return E1000_SUCCESS;
}
@@ -334,6 +383,10 @@ static s32 e1000_acquire_phy_82575(struct e1000_hw *hw)
if (hw->bus.func == E1000_FUNC_1)
mask = E1000_SWFW_PHY1_SM;
+ else if (hw->bus.func == E1000_FUNC_2)
+ mask = E1000_SWFW_PHY2_SM;
+ else if (hw->bus.func == E1000_FUNC_3)
+ mask = E1000_SWFW_PHY3_SM;
return e1000_acquire_swfw_sync_82575(hw, mask);
}
@@ -352,6 +405,10 @@ static void e1000_release_phy_82575(struct e1000_hw *hw)
if (hw->bus.func == E1000_FUNC_1)
mask = E1000_SWFW_PHY1_SM;
+ else if (hw->bus.func == E1000_FUNC_2)
+ mask = E1000_SWFW_PHY2_SM;
+ else if (hw->bus.func == E1000_FUNC_3)
+ mask = E1000_SWFW_PHY3_SM;
e1000_release_swfw_sync_82575(hw, mask);
}
@@ -368,47 +425,25 @@ static void e1000_release_phy_82575(struct e1000_hw *hw)
static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
u16 *data)
{
- struct e1000_phy_info *phy = &hw->phy;
- u32 i, i2ccmd = 0;
+ s32 ret_val = -E1000_ERR_PARAM;
DEBUGFUNC("e1000_read_phy_reg_sgmii_82575");
if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) {
DEBUGOUT1("PHY Address %u is out of range\n", offset);
- return -E1000_ERR_PARAM;
+ goto out;
}
- /*
- * Set up Op-code, Phy Address, and register address in the I2CCMD
- * register. The MAC will take care of interfacing with the
- * PHY to retrieve the desired data.
- */
- i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
- (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
- (E1000_I2CCMD_OPCODE_READ));
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
- E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd);
+ ret_val = e1000_read_phy_reg_i2c(hw, offset, data);
- /* Poll the ready bit to see if the I2C read completed */
- for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
- usec_delay(50);
- i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD);
- if (i2ccmd & E1000_I2CCMD_READY)
- break;
- }
- if (!(i2ccmd & E1000_I2CCMD_READY)) {
- DEBUGOUT("I2CCMD Read did not complete\n");
- return -E1000_ERR_PHY;
- }
- if (i2ccmd & E1000_I2CCMD_ERROR) {
- DEBUGOUT("I2CCMD Error bit set\n");
- return -E1000_ERR_PHY;
- }
-
- /* Need to byte-swap the 16-bit value. */
- *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+ hw->phy.ops.release(hw);
- return E1000_SUCCESS;
+out:
+ return ret_val;
}
/**
@@ -423,49 +458,25 @@ static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
u16 data)
{
- struct e1000_phy_info *phy = &hw->phy;
- u32 i, i2ccmd = 0;
- u16 phy_data_swapped;
+ s32 ret_val = -E1000_ERR_PARAM;
DEBUGFUNC("e1000_write_phy_reg_sgmii_82575");
if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) {
DEBUGOUT1("PHY Address %d is out of range\n", offset);
- return -E1000_ERR_PARAM;
+ goto out;
}
- /* Swap the data bytes for the I2C interface */
- phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
-
- /*
- * Set up Op-code, Phy Address, and register address in the I2CCMD
- * register. The MAC will take care of interfacing with the
- * PHY to retrieve the desired data.
- */
- i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
- (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
- E1000_I2CCMD_OPCODE_WRITE |
- phy_data_swapped);
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
- E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd);
+ ret_val = e1000_write_phy_reg_i2c(hw, offset, data);
- /* Poll the ready bit to see if the I2C read completed */
- for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
- usec_delay(50);
- i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD);
- if (i2ccmd & E1000_I2CCMD_READY)
- break;
- }
- if (!(i2ccmd & E1000_I2CCMD_READY)) {
- DEBUGOUT("I2CCMD Write did not complete\n");
- return -E1000_ERR_PHY;
- }
- if (i2ccmd & E1000_I2CCMD_ERROR) {
- DEBUGOUT("I2CCMD Error bit set\n");
- return -E1000_ERR_PHY;
- }
+ hw->phy.ops.release(hw);
- return E1000_SUCCESS;
+out:
+ return ret_val;
}
/**
@@ -480,6 +491,7 @@ static s32 e1000_get_phy_id_82575(struct e1000_hw *hw)
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val = E1000_SUCCESS;
u16 phy_id;
+ u32 ctrl_ext;
DEBUGFUNC("e1000_get_phy_id_82575");
@@ -490,12 +502,19 @@ static s32 e1000_get_phy_id_82575(struct e1000_hw *hw)
* work. The result of this function should mean phy->phy_addr
* and phy->id are set correctly.
*/
- if (!(e1000_sgmii_active_82575(hw))) {
+ if (!e1000_sgmii_active_82575(hw)) {
phy->addr = 1;
ret_val = e1000_get_phy_id(hw);
goto out;
}
+ /* Power on sgmii phy if it is disabled */
+ ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT,
+ ctrl_ext & ~E1000_CTRL_EXT_SDP3_DATA);
+ E1000_WRITE_FLUSH(hw);
+ msec_delay(300);
+
/*
* The address field in the I2CCMD register is 3 bits and 0 is invalid.
* Therefore, we need to test 1-7
@@ -522,10 +541,12 @@ static s32 e1000_get_phy_id_82575(struct e1000_hw *hw)
if (phy->addr == 8) {
phy->addr = 0;
ret_val = -E1000_ERR_PHY;
- goto out;
+ } else {
+ ret_val = e1000_get_phy_id(hw);
}
- ret_val = e1000_get_phy_id(hw);
+ /* restore previous sfp cage power state */
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
out:
return ret_val;
@@ -792,21 +813,23 @@ static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw)
if (hw->bus.func == E1000_FUNC_1)
mask = E1000_NVM_CFG_DONE_PORT_1;
+ else if (hw->bus.func == E1000_FUNC_2)
+ mask = E1000_NVM_CFG_DONE_PORT_2;
+ else if (hw->bus.func == E1000_FUNC_3)
+ mask = E1000_NVM_CFG_DONE_PORT_3;
while (timeout) {
if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask)
break;
msec_delay(1);
timeout--;
}
- if (!timeout) {
+ if (!timeout)
DEBUGOUT("MNG configuration cycle has not completed.\n");
- }
/* If EEPROM is not marked present, init the PHY manually */
if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) &&
- (hw->phy.type == e1000_phy_igp_3)) {
+ (hw->phy.type == e1000_phy_igp_3))
e1000_phy_init_script_igp3(hw);
- }
return ret_val;
}
@@ -828,14 +851,12 @@ static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed,
DEBUGFUNC("e1000_get_link_up_info_82575");
- if (hw->phy.media_type != e1000_media_type_copper ||
- e1000_sgmii_active_82575(hw)) {
+ if (hw->phy.media_type != e1000_media_type_copper)
ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, speed,
duplex);
- } else {
+ else
ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed,
duplex);
- }
return ret_val;
}
@@ -854,9 +875,7 @@ static s32 e1000_check_for_link_82575(struct e1000_hw *hw)
DEBUGFUNC("e1000_check_for_link_82575");
- /* SGMII link check is done through the PCS register. */
- if ((hw->phy.media_type != e1000_media_type_copper) ||
- (e1000_sgmii_active_82575(hw))) {
+ if (hw->phy.media_type != e1000_media_type_copper) {
ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, &speed,
&duplex);
/*
@@ -873,6 +892,35 @@ static s32 e1000_check_for_link_82575(struct e1000_hw *hw)
}
/**
+ * e1000_power_up_serdes_link_82575 - Power up the serdes link after shutdown
+ * @hw: pointer to the HW structure
+ **/
+static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw)
+{
+ u32 reg;
+
+ DEBUGFUNC("e1000_power_up_serdes_link_82575");
+
+ if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
+ !e1000_sgmii_active_82575(hw))
+ return;
+
+ /* Enable PCS to turn on link */
+ reg = E1000_READ_REG(hw, E1000_PCS_CFG0);
+ reg |= E1000_PCS_CFG_PCS_EN;
+ E1000_WRITE_REG(hw, E1000_PCS_CFG0, reg);
+
+ /* Power up the laser */
+ reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+ reg &= ~E1000_CTRL_EXT_SDP3_DATA;
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+
+ /* flush the write to verify completion */
+ E1000_WRITE_FLUSH(hw);
+ msec_delay(1);
+}
+
+/**
* e1000_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex
* @hw: pointer to the HW structure
* @speed: stores the current speed
@@ -930,31 +978,23 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw,
}
/**
- * e1000_shutdown_fiber_serdes_link_82575 - Remove link during power down
+ * e1000_shutdown_serdes_link_82575 - Remove link during power down
* @hw: pointer to the HW structure
*
- * In the case of fiber serdes shut down optics and PCS on driver unload
+ * In the case of serdes shut down sfp and PCS on driver unload
* when management pass thru is not enabled.
**/
-void e1000_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw)
+void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw)
{
u32 reg;
- u16 eeprom_data = 0;
- if (hw->phy.media_type != e1000_media_type_internal_serdes)
- return;
+ DEBUGFUNC("e1000_shutdown_serdes_link_82575");
- if (hw->bus.func == E1000_FUNC_0)
- hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
- else if (hw->bus.func == E1000_FUNC_1)
- hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+ if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
+ !e1000_sgmii_active_82575(hw))
+ return;
- /*
- * If APM is not enabled in the EEPROM and management interface is
- * not enabled, then power down.
- */
- if (!(eeprom_data & E1000_NVM_APME_82575) &&
- !e1000_enable_mng_pass_thru(hw)) {
+ if (!e1000_enable_mng_pass_thru(hw)) {
/* Disable PCS to turn off link */
reg = E1000_READ_REG(hw, E1000_PCS_CFG0);
reg &= ~E1000_PCS_CFG_PCS_EN;
@@ -962,10 +1002,10 @@ void e1000_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw)
/* shutdown the laser */
reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
- reg |= E1000_CTRL_EXT_SDP7_DATA;
+ reg |= E1000_CTRL_EXT_SDP3_DATA;
E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
- /* flush the write to verfiy completion */
+ /* flush the write to verify completion */
E1000_WRITE_FLUSH(hw);
msec_delay(1);
}
@@ -974,45 +1014,6 @@ void e1000_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw)
}
/**
- * e1000_vmdq_set_loopback_pf - enable or disable vmdq loopback
- * @hw: pointer to the HW structure
- * @enable: state to enter, either enabled or disabled
- *
- * enables/disables L2 switch loopback functionality
- **/
-void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable)
-{
- u32 reg;
-
- reg = E1000_READ_REG(hw, E1000_DTXSWC);
- if (enable)
- reg |= E1000_DTXSWC_VMDQ_LOOPBACK_EN;
- else
- reg &= ~(E1000_DTXSWC_VMDQ_LOOPBACK_EN);
- E1000_WRITE_REG(hw, E1000_DTXSWC, reg);
-}
-
-/**
- * e1000_vmdq_set_replication_pf - enable or disable vmdq replication
- * @hw: pointer to the HW structure
- * @enable: state to enter, either enabled or disabled
- *
- * enables/disables replication of packets across multiple pools
- **/
-void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable)
-{
- u32 reg;
-
- reg = E1000_READ_REG(hw, E1000_VT_CTL);
- if (enable)
- reg |= E1000_VT_CTL_VM_REPL_EN;
- else
- reg &= ~(E1000_VT_CTL_VM_REPL_EN);
-
- E1000_WRITE_REG(hw, E1000_VT_CTL, reg);
-}
-
-/**
* e1000_reset_hw_82575 - Reset hardware
* @hw: pointer to the HW structure
*
@@ -1111,6 +1112,11 @@ static s32 e1000_init_hw_82575(struct e1000_hw *hw)
for (i = 0; i < mac->mta_reg_count; i++)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+ /* Zero out the Unicast HASH table */
+ DEBUGOUT("Zeroing the UTA\n");
+ for (i = 0; i < mac->uta_reg_count; i++)
+ E1000_WRITE_REG_ARRAY(hw, E1000_UTA, i, 0);
+
/* Setup link and flow control */
ret_val = mac->ops.setup_link(hw);
@@ -1137,7 +1143,6 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw)
{
u32 ctrl;
s32 ret_val;
- bool link;
DEBUGFUNC("e1000_setup_copper_link_82575");
@@ -1146,6 +1151,20 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw)
ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+ ret_val = e1000_setup_serdes_link_82575(hw);
+ if (ret_val)
+ goto out;
+
+ if (e1000_sgmii_active_82575(hw) && !hw->phy.reset_disable) {
+ /* allow time for SFP cage time to power up phy */
+ msec_delay(300);
+
+ ret_val = hw->phy.ops.reset(hw);
+ if (ret_val) {
+ DEBUGOUT("Error resetting the PHY.\n");
+ goto out;
+ }
+ }
switch (hw->phy.type) {
case e1000_phy_m88:
ret_val = e1000_copper_link_setup_m88(hw);
@@ -1153,6 +1172,9 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw)
case e1000_phy_igp_3:
ret_val = e1000_copper_link_setup_igp(hw);
break;
+ case e1000_phy_82580:
+ ret_val = e1000_copper_link_setup_82577(hw);
+ break;
default:
ret_val = -E1000_ERR_PHY;
break;
@@ -1161,66 +1183,30 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw)
if (ret_val)
goto out;
- if (hw->mac.autoneg) {
- /*
- * Setup autoneg and flow control advertisement
- * and perform autonegotiation.
- */
- ret_val = e1000_copper_link_autoneg(hw);
- if (ret_val)
- goto out;
- } else {
- /*
- * PHY will be set to 10H, 10F, 100H or 100F
- * depending on user settings.
- */
- DEBUGOUT("Forcing Speed and Duplex\n");
- ret_val = hw->phy.ops.force_speed_duplex(hw);
- if (ret_val) {
- DEBUGOUT("Error Forcing Speed and Duplex\n");
- goto out;
- }
- }
-
- ret_val = e1000_configure_pcs_link_82575(hw);
- if (ret_val)
- goto out;
-
- /*
- * Check link status. Wait up to 100 microseconds for link to become
- * valid.
- */
- ret_val = e1000_phy_has_link_generic(hw,
- COPPER_LINK_UP_LIMIT,
- 10,
- &link);
- if (ret_val)
- goto out;
-
- if (link) {
- DEBUGOUT("Valid link established!!!\n");
- /* Config the MAC and PHY after link is up */
- e1000_config_collision_dist_generic(hw);
- ret_val = e1000_config_fc_after_link_up_generic(hw);
- } else {
- DEBUGOUT("Unable to establish link!!!\n");
- }
-
+ ret_val = e1000_setup_copper_link_generic(hw);
out:
return ret_val;
}
/**
- * e1000_setup_fiber_serdes_link_82575 - Setup link for fiber/serdes
+ * e1000_setup_serdes_link_82575 - Setup link for serdes
* @hw: pointer to the HW structure
*
- * Configures speed and duplex for fiber and serdes links.
+ * Configure the physical coding sub-layer (PCS) link. The PCS link is
+ * used on copper connections where the serialized gigabit media independent
+ * interface (sgmii), or serdes fiber is being used. Configures the link
+ * for auto-negotiation or forces speed/duplex.
**/
-static s32 e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw)
+static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw)
{
- u32 reg;
+ u32 ctrl_ext, ctrl_reg, reg;
+ bool pcs_autoneg;
+
+ DEBUGFUNC("e1000_setup_serdes_link_82575");
- DEBUGFUNC("e1000_setup_fiber_serdes_link_82575");
+ if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
+ !e1000_sgmii_active_82575(hw))
+ return E1000_SUCCESS;
/*
* On the 82575, SerDes loopback mode persists until it is
@@ -1230,25 +1216,48 @@ static s32 e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw)
*/
E1000_WRITE_REG(hw, E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK);
- /* Force link up, set 1gb */
- reg = E1000_READ_REG(hw, E1000_CTRL);
- reg |= E1000_CTRL_SLU | E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD;
- if (hw->mac.type == e1000_82575 || hw->mac.type == e1000_82576) {
- /* set both sw defined pins */
- reg |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1;
- }
- E1000_WRITE_REG(hw, E1000_CTRL, reg);
- /* Power on phy for 82576 fiber adapters */
- if (hw->mac.type == e1000_82576) {
- reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
- reg &= ~E1000_CTRL_EXT_SDP7_DATA;
- E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+ /* power on the sfp cage if present */
+ ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+ ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+
+ ctrl_reg = E1000_READ_REG(hw, E1000_CTRL);
+ ctrl_reg |= E1000_CTRL_SLU;
+
+ /* set both sw defined pins on 82575/82576*/
+ if (hw->mac.type == e1000_82575 || hw->mac.type == e1000_82576)
+ ctrl_reg |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1;
+
+ reg = E1000_READ_REG(hw, E1000_PCS_LCTL);
+
+ /* default pcs_autoneg to the same setting as mac autoneg */
+ pcs_autoneg = hw->mac.autoneg;
+
+ switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
+ case E1000_CTRL_EXT_LINK_MODE_SGMII:
+ /* sgmii mode lets the phy handle forcing speed/duplex */
+ pcs_autoneg = TRUE;
+ /* autoneg time out should be disabled for SGMII mode */
+ reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT);
+ break;
+ case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+ /* disable PCS autoneg and support parallel detect only */
+ pcs_autoneg = FALSE;
+ default:
+ /*
+ * non-SGMII modes only supports a speed of 1000/Full for the
+ * link so it is best to just force the MAC and let the pcs
+ * link either autoneg or be forced to 1000/Full
+ */
+ ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD |
+ E1000_CTRL_FD | E1000_CTRL_FRCDPX;
+
+ /* set speed of 1000/Full if speed/duplex is forced */
+ reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL;
+ break;
}
- /* Set switch control to serdes energy detect */
- reg = E1000_READ_REG(hw, E1000_CONNSW);
- reg |= E1000_CONNSW_ENRGSRC;
- E1000_WRITE_REG(hw, E1000_CONNSW, reg);
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl_reg);
/*
* New SerDes mode allows for forcing speed or autonegotiating speed
@@ -1256,35 +1265,31 @@ static s32 e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw)
* mode that will be compatible with older link partners and switches.
* However, both are supported by the hardware and some drivers/tools.
*/
- reg = E1000_READ_REG(hw, E1000_PCS_LCTL);
-
reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP |
- E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
+ E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
- if (hw->mac.autoneg) {
+ /*
+ * We force flow control to prevent the CTRL register values from being
+ * overwritten by the autonegotiated flow control values
+ */
+ reg |= E1000_PCS_LCTL_FORCE_FCTRL;
+
+ if (pcs_autoneg) {
/* Set PCS register for autoneg */
- reg |= E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */
- E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */
- E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
- E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
- DEBUGOUT1("Configuring Autoneg; PCS_LCTL = 0x%08X\n", reg);
+ reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
+ E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
+ DEBUGOUT1("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg);
} else {
- /* Set PCS register for forced speed */
- reg |= E1000_PCS_LCTL_FLV_LINK_UP | /* Force link up */
- E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */
- E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */
- E1000_PCS_LCTL_FSD | /* Force Speed */
- E1000_PCS_LCTL_FORCE_LINK; /* Force Link */
- DEBUGOUT1("Configuring Forced Link; PCS_LCTL = 0x%08X\n", reg);
- }
-
- if (hw->mac.type == e1000_82576) {
- reg |= E1000_PCS_LCTL_FORCE_FCTRL;
- e1000_force_mac_fc_generic(hw);
+ /* Set PCS register for forced link */
+ reg |= E1000_PCS_LCTL_FSD; /* Force Speed */
+ DEBUGOUT1("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg);
}
E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg);
+ if (!e1000_sgmii_active_82575(hw))
+ e1000_force_mac_fc_generic(hw);
+
return E1000_SUCCESS;
}
@@ -1324,72 +1329,6 @@ out:
}
/**
- * e1000_configure_pcs_link_82575 - Configure PCS link
- * @hw: pointer to the HW structure
- *
- * Configure the physical coding sub-layer (PCS) link. The PCS link is
- * only used on copper connections where the serialized gigabit media
- * independent interface (sgmii) is being used. Configures the link
- * for auto-negotiation or forces speed/duplex.
- **/
-static s32 e1000_configure_pcs_link_82575(struct e1000_hw *hw)
-{
- struct e1000_mac_info *mac = &hw->mac;
- u32 reg = 0;
-
- DEBUGFUNC("e1000_configure_pcs_link_82575");
-
- if (hw->phy.media_type != e1000_media_type_copper ||
- !(e1000_sgmii_active_82575(hw)))
- goto out;
-
- /* For SGMII, we need to issue a PCS autoneg restart */
- reg = E1000_READ_REG(hw, E1000_PCS_LCTL);
-
- /* AN time out should be disabled for SGMII mode */
- reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT);
-
- if (mac->autoneg) {
- /* Make sure forced speed and force link are not set */
- reg &= ~(E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
-
- /*
- * The PHY should be setup prior to calling this function.
- * All we need to do is restart autoneg and enable autoneg.
- */
- reg |= E1000_PCS_LCTL_AN_RESTART | E1000_PCS_LCTL_AN_ENABLE;
- } else {
- /* Set PCS register for forced speed */
-
- /* Turn off bits for full duplex, speed, and autoneg */
- reg &= ~(E1000_PCS_LCTL_FSV_1000 |
- E1000_PCS_LCTL_FSV_100 |
- E1000_PCS_LCTL_FDV_FULL |
- E1000_PCS_LCTL_AN_ENABLE);
-
- /* Check for duplex first */
- if (mac->forced_speed_duplex & E1000_ALL_FULL_DUPLEX)
- reg |= E1000_PCS_LCTL_FDV_FULL;
-
- /* Now set speed */
- if (mac->forced_speed_duplex & E1000_ALL_100_SPEED)
- reg |= E1000_PCS_LCTL_FSV_100;
-
- /* Force speed and force link */
- reg |= E1000_PCS_LCTL_FSD |
- E1000_PCS_LCTL_FORCE_LINK |
- E1000_PCS_LCTL_FLV_LINK_UP;
-
- DEBUGOUT1("Wrote 0x%08X to PCS_LCTL to configure forced link\n",
- reg);
- }
- E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg);
-
-out:
- return E1000_SUCCESS;
-}
-
-/**
* e1000_sgmii_active_82575 - Return sgmii state
* @hw: pointer to the HW structure
*
@@ -1467,6 +1406,28 @@ out:
}
/**
+ * e1000_config_collision_dist_82575 - Configure collision distance
+ * @hw: pointer to the HW structure
+ *
+ * Configures the collision distance to the default value and is used
+ * during link setup.
+ **/
+static void e1000_config_collision_dist_82575(struct e1000_hw *hw)
+{
+ u32 tctl_ext;
+
+ DEBUGFUNC("e1000_config_collision_dist_82575");
+
+ tctl_ext = E1000_READ_REG(hw, E1000_TCTL_EXT);
+
+ tctl_ext &= ~E1000_TCTL_EXT_COLD;
+ tctl_ext |= E1000_COLLISION_DISTANCE << E1000_TCTL_EXT_COLD_SHIFT;
+
+ E1000_WRITE_REG(hw, E1000_TCTL_EXT, tctl_ext);
+ E1000_WRITE_FLUSH(hw);
+}
+
+/**
* e1000_power_down_phy_copper_82575 - Remove link during PHY power down
* @hw: pointer to the HW structure
*
@@ -1476,13 +1437,12 @@ out:
static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
- struct e1000_mac_info *mac = &hw->mac;
if (!(phy->ops.check_reset_block))
return;
/* If the management interface is not enabled, then power down */
- if (!(mac->ops.check_mng_mode(hw) || phy->ops.check_reset_block(hw)))
+ if (!(e1000_enable_mng_pass_thru(hw) || phy->ops.check_reset_block(hw)))
e1000_power_down_phy_copper(hw);
return;
@@ -1548,7 +1508,8 @@ static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw)
E1000_READ_REG(hw, E1000_LENERRS);
/* This register should not be read in copper configurations */
- if (hw->phy.media_type == e1000_media_type_internal_serdes)
+ if ((hw->phy.media_type == e1000_media_type_internal_serdes) ||
+ e1000_sgmii_active_82575(hw))
E1000_READ_REG(hw, E1000_SCVPC);
}
@@ -1677,3 +1638,208 @@ out:
return ret_val;
}
+/**
+ * e1000_vmdq_set_loopback_pf - enable or disable vmdq loopback
+ * @hw: pointer to the hardware struct
+ * @enable: state to enter, either enabled or disabled
+ *
+ * enables/disables L2 switch loopback functionality.
+ **/
+void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable)
+{
+ u32 dtxswc;
+
+ switch (hw->mac.type) {
+ case e1000_82576:
+ dtxswc = E1000_READ_REG(hw, E1000_DTXSWC);
+ if (enable)
+ dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ else
+ dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN;
+ E1000_WRITE_REG(hw, E1000_DTXSWC, dtxswc);
+ break;
+ default:
+ /* Currently no other hardware supports loopback */
+ break;
+ }
+
+
+}
+
+/**
+ * e1000_vmdq_set_replication_pf - enable or disable vmdq replication
+ * @hw: pointer to the hardware struct
+ * @enable: state to enter, either enabled or disabled
+ *
+ * enables/disables replication of packets across multiple pools.
+ **/
+void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable)
+{
+ u32 vt_ctl = E1000_READ_REG(hw, E1000_VT_CTL);
+
+ if (enable)
+ vt_ctl |= E1000_VT_CTL_VM_REPL_EN;
+ else
+ vt_ctl &= ~E1000_VT_CTL_VM_REPL_EN;
+
+ E1000_WRITE_REG(hw, E1000_VT_CTL, vt_ctl);
+}
+
+/**
+ * e1000_read_phy_reg_82580 - Read 82580 MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the MDI control register in the PHY at offset and stores the
+ * information read to data.
+ **/
+static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ s32 ret_val;
+
+ DEBUGFUNC("e1000_read_phy_reg_82580");
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = e1000_read_phy_reg_mdic(hw, offset, data);
+
+ hw->phy.ops.release(hw);
+
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_write_phy_reg_82580 - Write 82580 MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write to register at offset
+ *
+ * Writes data to MDI control register in the PHY at offset.
+ **/
+static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ s32 ret_val;
+
+ DEBUGFUNC("e1000_write_phy_reg_82580");
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = e1000_write_phy_reg_mdic(hw, offset, data);
+
+ hw->phy.ops.release(hw);
+
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_reset_hw_82580 - Reset hardware
+ * @hw: pointer to the HW structure
+ *
+ * This resets function or entire device (all ports, etc.)
+ * to a known state.
+ **/
+static s32 e1000_reset_hw_82580(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+ /* BH SW mailbox bit in SW_FW_SYNC */
+ u16 swmbsw_mask = E1000_SW_SYNCH_MB;
+ u32 ctrl, icr;
+ bool global_device_reset = hw->dev_spec._82575.global_device_reset;
+
+ DEBUGFUNC("e1000_reset_hw_82580");
+
+ hw->dev_spec._82575.global_device_reset = FALSE;
+
+ /* Get current control state. */
+ ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+ /*
+ * Prevent the PCI-E bus from sticking if there is no TLP connection
+ * on the last TLP read/write transaction when MAC is reset.
+ */
+ ret_val = e1000_disable_pcie_master_generic(hw);
+ if (ret_val)
+ DEBUGOUT("PCI-E Master disable polling has failed.\n");
+
+ DEBUGOUT("Masking off all interrupts\n");
+ E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+ E1000_WRITE_REG(hw, E1000_RCTL, 0);
+ E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+ E1000_WRITE_FLUSH(hw);
+
+ msec_delay(10);
+
+ /* Determine whether or not a global dev reset is requested */
+ if (global_device_reset &&
+ e1000_acquire_swfw_sync_82575(hw, swmbsw_mask))
+ global_device_reset = FALSE;
+
+ if (global_device_reset &&
+ !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STAT_DEV_RST_SET))
+ ctrl |= E1000_CTRL_DEV_RST;
+ else
+ ctrl |= E1000_CTRL_RST;
+
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+ /* Add delay to insure DEV_RST has time to complete */
+ if (global_device_reset)
+ msec_delay(5);
+
+ ret_val = e1000_get_auto_rd_done_generic(hw);
+ if (ret_val) {
+ /*
+ * When auto config read does not complete, do not
+ * return with an error. This can happen in situations
+ * where there is no eeprom and prevents getting link.
+ */
+ DEBUGOUT("Auto Read Done did not complete\n");
+ }
+
+ /* If EEPROM is not present, run manual init scripts */
+ if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0)
+ e1000_reset_init_script_82575(hw);
+
+ /* clear global device reset status bit */
+ E1000_WRITE_REG(hw, E1000_STATUS, E1000_STAT_DEV_RST_SET);
+
+ /* Clear any pending interrupt events. */
+ E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+ icr = E1000_READ_REG(hw, E1000_ICR);
+
+ /* Install any alternate MAC address into RAR0 */
+ ret_val = e1000_check_alt_mac_addr_generic(hw);
+
+ /* Release semaphore */
+ if (global_device_reset)
+ e1000_release_swfw_sync_82575(hw, swmbsw_mask);
+
+ return ret_val;
+}
+
+/**
+ * e1000_rxpbs_adjust_82580 - adjust RXPBS value to reflect actual RX PBA size
+ * @data: data received by reading RXPBS register
+ *
+ * The 82580 uses a table based approach for packet buffer allocation sizes.
+ * This function converts the retrieved value into the correct table value
+ * 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
+ * 0x0 36 72 144 1 2 4 8 16
+ * 0x8 35 70 140 rsv rsv rsv rsv rsv
+ */
+u16 e1000_rxpbs_adjust_82580(u32 data)
+{
+ u16 ret_val = 0;
+
+ if (data < E1000_82580_RXPBS_TABLE_SIZE)
+ ret_val = e1000_82580_rxpbs_table[data];
+
+ return ret_val;
+}
diff --git a/sys/dev/e1000/e1000_82575.h b/sys/dev/e1000/e1000_82575.h
index 34e0d29398f7..1fc7e26dd590 100644
--- a/sys/dev/e1000/e1000_82575.h
+++ b/sys/dev/e1000/e1000_82575.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -49,12 +49,16 @@
* For 82576, there are an additional set of RARs that begin at an offset
* separate from the first set of RARs.
*/
-#define E1000_RAR_ENTRIES_82575 16
-#define E1000_RAR_ENTRIES_82576 24
+#define E1000_RAR_ENTRIES_82575 16
+#define E1000_RAR_ENTRIES_82576 24
+#define E1000_RAR_ENTRIES_82580 24
+#define E1000_SW_SYNCH_MB 0x00000100
+#define E1000_STAT_DEV_RST_SET 0x00100000
+#define E1000_CTRL_DEV_RST 0x20000000
#ifdef E1000_BIT_FIELDS
struct e1000_adv_data_desc {
- u64 buffer_addr; /* Address of the descriptor's data buffer */
+ __le64 buffer_addr; /* Address of the descriptor's data buffer */
union {
u32 data;
struct {
@@ -128,6 +132,7 @@ struct e1000_adv_context_desc {
#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000
#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000
#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000
+#define E1000_SRRCTL_TIMESTAMP 0x40000000
#define E1000_SRRCTL_DROP_EN 0x80000000
#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F
@@ -142,6 +147,7 @@ struct e1000_adv_context_desc {
#define E1000_MRQC_RSS_FIELD_IPV4_UDP 0x00400000
#define E1000_MRQC_RSS_FIELD_IPV6_UDP 0x00800000
#define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000
+#define E1000_MRQC_ENABLE_RSS_8Q 0x00000002
#define E1000_VMRCTL_MIRROR_PORT_SHIFT 8
#define E1000_VMRCTL_MIRROR_DSTPORT_MASK (7 << E1000_VMRCTL_MIRROR_PORT_SHIFT)
@@ -185,31 +191,31 @@ struct e1000_adv_context_desc {
/* Receive Descriptor - Advanced */
union e1000_adv_rx_desc {
struct {
- u64 pkt_addr; /* Packet buffer address */
- u64 hdr_addr; /* Header buffer address */
+ __le64 pkt_addr; /* Packet buffer address */
+ __le64 hdr_addr; /* Header buffer address */
} read;
struct {
struct {
union {
- u32 data;
+ __le32 data;
struct {
- u16 pkt_info; /* RSS type, Packet type */
- u16 hdr_info; /* Split Header,
- * header buffer length */
+ __le16 pkt_info; /*RSS type, Pkt type*/
+ __le16 hdr_info; /* Split Header,
+ * header buffer len*/
} hs_rss;
} lo_dword;
union {
- u32 rss; /* RSS Hash */
+ __le32 rss; /* RSS Hash */
struct {
- u16 ip_id; /* IP id */
- u16 csum; /* Packet Checksum */
+ __le16 ip_id; /* IP id */
+ __le16 csum; /* Packet Checksum */
} csum_ip;
} hi_dword;
} lower;
struct {
- u32 status_error; /* ext status/error */
- u16 length; /* Packet length */
- u16 vlan; /* VLAN tag */
+ __le32 status_error; /* ext status/error */
+ __le16 length; /* Packet length */
+ __le16 vlan; /* VLAN tag */
} upper;
} wb; /* writeback */
};
@@ -220,6 +226,8 @@ union e1000_adv_rx_desc {
#define E1000_RXDADV_HDRBUFLEN_SHIFT 5
#define E1000_RXDADV_SPLITHEADER_EN 0x00001000
#define E1000_RXDADV_SPH 0x8000
+#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */
+#define E1000_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */
#define E1000_RXDADV_ERR_HBO 0x00800000
/* RSS Hash results */
@@ -269,14 +277,14 @@ union e1000_adv_rx_desc {
/* Transmit Descriptor - Advanced */
union e1000_adv_tx_desc {
struct {
- u64 buffer_addr; /* Address of descriptor's data buf */
- u32 cmd_type_len;
- u32 olinfo_status;
+ __le64 buffer_addr; /* Address of descriptor's data buf */
+ __le32 cmd_type_len;
+ __le32 olinfo_status;
} read;
struct {
- u64 rsvd; /* Reserved */
- u32 nxtseq_seed;
- u32 status;
+ __le64 rsvd; /* Reserved */
+ __le32 nxtseq_seed;
+ __le32 status;
} wb;
};
@@ -303,10 +311,10 @@ union e1000_adv_tx_desc {
/* Context descriptors */
struct e1000_adv_tx_context_desc {
- u32 vlan_macip_lens;
- u32 seqnum_seed;
- u32 type_tucmd_mlhl;
- u32 mss_l4len_idx;
+ __le32 vlan_macip_lens;
+ __le32 seqnum_seed;
+ __le32 type_tucmd_mlhl;
+ __le32 mss_l4len_idx;
};
#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */
@@ -378,6 +386,14 @@ struct e1000_adv_tx_context_desc {
*/
#define E1000_ETQF_FILTER_EAPOL 0
+#define E1000_FTQF_VF_BP 0x00008000
+#define E1000_FTQF_1588_TIME_STAMP 0x08000000
+#define E1000_FTQF_MASK 0xF0000000
+#define E1000_FTQF_MASK_PROTO_BP 0x10000000
+#define E1000_FTQF_MASK_SOURCE_ADDR_BP 0x20000000
+#define E1000_FTQF_MASK_DEST_ADDR_BP 0x40000000
+#define E1000_FTQF_MASK_SOURCE_PORT_BP 0x80000000
+
#define E1000_NVM_APME_82575 0x0400
#define MAX_NUM_VFS 8
@@ -409,6 +425,7 @@ struct e1000_adv_tx_context_desc {
#define E1000_VMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */
#define E1000_VMOLR_STRCRC 0x80000000 /* CRC stripping enable */
+
#define E1000_VLVF_ARRAY_SIZE 32
#define E1000_VLVF_VLANID_MASK 0x00000FFF
#define E1000_VLVF_POOLSEL_SHIFT 12
@@ -416,6 +433,9 @@ struct e1000_adv_tx_context_desc {
#define E1000_VLVF_LVLAN 0x00100000
#define E1000_VLVF_VLANID_ENABLE 0x80000000
+#define E1000_VMVIR_VLANA_DEFAULT 0x40000000 /* Always use default VLAN */
+#define E1000_VMVIR_VLANA_NEVER 0x80000000 /* Never insert VLAN tag */
+
#define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */
#define E1000_IOVCTL 0x05BBC
@@ -424,8 +444,20 @@ struct e1000_adv_tx_context_desc {
#define E1000_RPLOLR_STRVLAN 0x40000000
#define E1000_RPLOLR_STRCRC 0x80000000
+#define E1000_TCTL_EXT_COLD 0x000FFC00
+#define E1000_TCTL_EXT_COLD_SHIFT 10
+
+#define E1000_DTXCTL_8023LL 0x0004
+#define E1000_DTXCTL_VLAN_ADDED 0x0008
+#define E1000_DTXCTL_OOS_ENABLE 0x0010
+#define E1000_DTXCTL_MDP_EN 0x0020
+#define E1000_DTXCTL_SPOOF_INT 0x0040
+
#define ALL_QUEUES 0xFFFF
+/* RX packet buffer size defines */
+#define E1000_RXPBS_SIZE_MASK_82576 0x0000007F
void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable);
void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable);
+u16 e1000_rxpbs_adjust_82580(u32 data);
#endif /* _E1000_82575_H_ */
diff --git a/sys/dev/e1000/e1000_api.c b/sys/dev/e1000/e1000_api.c
index 8188658d6bb1..bf9fa2abb903 100644
--- a/sys/dev/e1000/e1000_api.c
+++ b/sys/dev/e1000/e1000_api.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -232,6 +232,7 @@ s32 e1000_set_mac_type(struct e1000_hw *hw)
case E1000_DEV_ID_ICH8_IGP_M_AMT:
case E1000_DEV_ID_ICH8_IGP_AMT:
case E1000_DEV_ID_ICH8_IGP_C:
+ case E1000_DEV_ID_ICH8_82567V_3:
mac->type = e1000_ich8lan;
break;
case E1000_DEV_ID_ICH9_IFE:
@@ -269,9 +270,17 @@ s32 e1000_set_mac_type(struct e1000_hw *hw)
case E1000_DEV_ID_82576_SERDES:
case E1000_DEV_ID_82576_QUAD_COPPER:
case E1000_DEV_ID_82576_NS:
+ case E1000_DEV_ID_82576_NS_SERDES:
case E1000_DEV_ID_82576_SERDES_QUAD:
mac->type = e1000_82576;
break;
+ case E1000_DEV_ID_82580_COPPER:
+ case E1000_DEV_ID_82580_FIBER:
+ case E1000_DEV_ID_82580_SERDES:
+ case E1000_DEV_ID_82580_SGMII:
+ case E1000_DEV_ID_82580_COPPER_DUAL:
+ mac->type = e1000_82580;
+ break;
default:
/* Should never have loaded on this device */
ret_val = -E1000_ERR_MAC_INIT;
@@ -362,6 +371,7 @@ s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device)
break;
case e1000_82575:
case e1000_82576:
+ case e1000_82580:
e1000_init_function_pointers_82575(hw);
break;
default:
@@ -745,20 +755,6 @@ s32 e1000_validate_mdi_setting(struct e1000_hw *hw)
}
/**
- * e1000_mta_set - Sets multicast table bit
- * @hw: pointer to the HW structure
- * @hash_value: Multicast hash value.
- *
- * This sets the bit in the multicast table corresponding to the
- * hash value. This is a function pointer entry point called by drivers.
- **/
-void e1000_mta_set(struct e1000_hw *hw, u32 hash_value)
-{
- if (hw->mac.ops.mta_set)
- hw->mac.ops.mta_set(hw, hash_value);
-}
-
-/**
* e1000_hash_mc_addr - Determines address location in multicast table
* @hw: pointer to the HW structure
* @mc_addr: Multicast address to hash.
@@ -1237,6 +1233,18 @@ void e1000_power_down_phy(struct e1000_hw *hw)
}
/**
+ * e1000_power_up_fiber_serdes_link - Power up serdes link
+ * @hw: pointer to the HW structure
+ *
+ * Power on the optics and PCS.
+ **/
+void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw)
+{
+ if (hw->mac.ops.power_up_serdes)
+ hw->mac.ops.power_up_serdes(hw);
+}
+
+/**
* e1000_shutdown_fiber_serdes_link - Remove link during power down
* @hw: pointer to the HW structure
*
diff --git a/sys/dev/e1000/e1000_api.h b/sys/dev/e1000/e1000_api.h
index b492e577b1a0..b7bc14c8f314 100644
--- a/sys/dev/e1000/e1000_api.h
+++ b/sys/dev/e1000/e1000_api.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -47,6 +47,7 @@ extern void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw);
extern void e1000_init_function_pointers_82575(struct e1000_hw *hw);
extern void e1000_rx_fifo_flush_82575(struct e1000_hw *hw);
extern void e1000_init_function_pointers_vf(struct e1000_hw *hw);
+extern void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw);
extern void e1000_shutdown_fiber_serdes_link(struct e1000_hw *hw);
s32 e1000_set_mac_type(struct e1000_hw *hw);
@@ -67,7 +68,6 @@ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed,
s32 e1000_disable_pcie_master(struct e1000_hw *hw);
void e1000_config_collision_dist(struct e1000_hw *hw);
void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index);
-void e1000_mta_set(struct e1000_hw *hw, u32 hash_value);
u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr);
void e1000_update_mc_addr_list(struct e1000_hw *hw,
u8 *mc_addr_list, u32 mc_addr_count);
diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index d845fb23dc65..7ac5d8b8b1d7 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -146,12 +146,12 @@
#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Definable Pin 5 */
#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA
#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Definable Pin 6 */
-#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Definable Pin 7 */
+#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Definable Pin 3 */
/* SDP 4/5 (bits 8,9) are reserved in >= 82575 */
#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */
#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */
#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */
-#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */
+#define E1000_CTRL_EXT_SDP3_DIR 0x00000800 /* Direction of SDP3 0=in 1=out */
#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */
#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */
#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */
@@ -161,6 +161,8 @@
#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */
#define E1000_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clock Gating */
#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_82580_MASK 0x01C00000 /*82580 bit 24:22*/
+#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000
#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000
#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000
#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000
@@ -312,6 +314,11 @@
#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */
#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */
+#define E1000_MANC2H_PORT_623 0x00000020 /* Port 0x26f */
+#define E1000_MANC2H_PORT_664 0x00000040 /* Port 0x298 */
+#define E1000_MDEF_PORT_623 0x00000800 /* Port 0x26f */
+#define E1000_MDEF_PORT_664 0x00000400 /* Port 0x298 */
+
/* Receive Control */
#define E1000_RCTL_RST 0x00000001 /* Software reset */
#define E1000_RCTL_EN 0x00000002 /* enable */
@@ -386,6 +393,8 @@
#define E1000_SWFW_PHY0_SM 0x02
#define E1000_SWFW_PHY1_SM 0x04
#define E1000_SWFW_CSR_SM 0x08
+#define E1000_SWFW_PHY2_SM 0x20
+#define E1000_SWFW_PHY3_SM 0x40
/* FACTPS Definitions */
#define E1000_FACTPS_LFS 0x40000000 /* LAN Function Select */
@@ -414,6 +423,8 @@
* PHYRST_N pin */
#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external
* LINK_0 and LINK_1 pins */
+#define E1000_CTRL_LANPHYPC_OVERRIDE 0x00010000 /* SW control of LANPHYPC */
+#define E1000_CTRL_LANPHYPC_VALUE 0x00020000 /* SW value of LANPHYPC */
#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
@@ -697,6 +708,7 @@
/* Extended Configuration Control and Size */
#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020
#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001
+#define E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008
#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020
#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000
#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16
@@ -769,6 +781,7 @@
#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */
#define E1000_ICR_MNG 0x00040000 /* Manageability event */
#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */
+#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */
#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver
* should claim the interrupt */
#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* Q0 Rx desc FIFO parity error */
@@ -789,6 +802,7 @@
#define E1000_ICR_TXQ0 0x00400000 /* Tx Queue 0 Interrupt */
#define E1000_ICR_TXQ1 0x00800000 /* Tx Queue 1 Interrupt */
#define E1000_ICR_OTHER 0x01000000 /* Other Interrupts */
+#define E1000_ICR_FER 0x00400000 /* Fatal Error */
/* PBA ECC Register */
#define E1000_PBA_ECC_COUNTER_MASK 0xFFF00000 /* ECC counter mask */
@@ -860,6 +874,7 @@
#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */
#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */
#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_IMS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */
#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* Q0 Rx desc FIFO
* parity error */
#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* Q0 Tx desc FIFO
@@ -881,6 +896,7 @@
#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 /* Tx Queue 0 Interrupt */
#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 /* Tx Queue 1 Interrupt */
#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupts */
+#define E1000_IMS_FER E1000_ICR_FER /* Fatal Error */
/* Extended Interrupt Mask Set */
#define E1000_EIMS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */
@@ -913,6 +929,7 @@
#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */
#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */
#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Aserted */
#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* Q0 Rx desc FIFO
* parity error */
#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* Q0 Tx desc FIFO
@@ -943,6 +960,8 @@
#define E1000_EICS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */
#define E1000_EITR_ITR_INT_MASK 0x0000FFFF
+/* E1000_EITR_CNT_IGNR is only for 82576 and newer */
+#define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */
/* Transmit Descriptor Control */
#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
@@ -1036,6 +1055,56 @@
#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */
#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */
+#define E1000_TSYNCTXCTL_VALID 0x00000001 /* tx timestamp valid */
+#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable tx timestampping */
+
+#define E1000_TSYNCRXCTL_VALID 0x00000001 /* rx timestamp valid */
+#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* rx type mask */
+#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00
+#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02
+#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
+#define E1000_TSYNCRXCTL_TYPE_ALL 0x08
+#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
+#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable rx timestampping */
+
+#define E1000_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF
+#define E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00
+#define E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01
+#define E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02
+#define E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03
+#define E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04
+
+#define E1000_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00
+#define E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000
+#define E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300
+#define E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800
+#define E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00
+#define E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00
+#define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00
+#define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00
+
+#define E1000_TIMINCA_16NS_SHIFT 24
+/* TUPLE Filtering Configuration */
+#define E1000_TTQF_DISABLE_MASK 0xF0008000 /* TTQF Disable Mask */
+#define E1000_TTQF_QUEUE_ENABLE 0x100 /* TTQF Queue Enable Bit */
+#define E1000_TTQF_PROTOCOL_MASK 0xFF /* TTQF Protocol Mask */
+/* TTQF TCP Bit, shift with E1000_TTQF_PROTOCOL SHIFT */
+#define E1000_TTQF_PROTOCOL_TCP 0x0
+/* TTQF UDP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */
+#define E1000_TTQF_PROTOCOL_UDP 0x1
+/* TTQF SCTP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */
+#define E1000_TTQF_PROTOCOL_SCTP 0x2
+#define E1000_TTQF_PROTOCOL_SHIFT 5 /* TTQF Protocol Shift */
+#define E1000_TTQF_QUEUE_SHIFT 16 /* TTQF Queue Shfit */
+#define E1000_TTQF_RX_QUEUE_MASK 0x70000 /* TTQF Queue Mask */
+#define E1000_TTQF_MASK_ENABLE 0x10000000 /* TTQF Mask Enable Bit */
+#define E1000_IMIR_CLEAR_MASK 0xF001FFFF /* IMIR Reg Clear Mask */
+#define E1000_IMIR_PORT_BYPASS 0x20000 /* IMIR Port Bypass Bit */
+#define E1000_IMIR_PRIORITY_SHIFT 29 /* IMIR Priority Shift */
+#define E1000_IMIREXT_CLEAR_MASK 0x7FFFF /* IMIREXT Reg Clear Mask */
/* PCI Express Control */
#define E1000_GCR_RXD_NO_SNOOP 0x00000001
@@ -1227,6 +1296,10 @@
#define E1000_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */
#define E1000_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */
+#define E1000_NVM_CFG_DONE_PORT_2 0x100000 /* ...for third port */
+#define E1000_NVM_CFG_DONE_PORT_3 0x200000 /* ...for fourth port */
+
+#define NVM_82580_LAN_FUNC_OFFSET(a) (a ? (0x40 + (0x40 * a)) : 0)
/* Mask bits for fields in Word 0x0f of the NVM */
#define NVM_WORD0F_PAUSE_MASK 0x3000
@@ -1316,6 +1389,9 @@
#define PCI_HEADER_TYPE_MULTIFUNC 0x80
#define PCIE_LINK_WIDTH_MASK 0x3F0
#define PCIE_LINK_WIDTH_SHIFT 4
+#define PCIE_LINK_SPEED_MASK 0x0F
+#define PCIE_LINK_SPEED_2500 0x01
+#define PCIE_LINK_SPEED_5000 0x02
#define PCIE_DEVICE_CONTROL2_16ms 0x0005
#ifndef ETH_ADDR_LEN
@@ -1346,6 +1422,7 @@
#define BME1000_E_PHY_ID_R2 0x01410CB1
#define I82577_E_PHY_ID 0x01540050
#define I82578_E_PHY_ID 0x004DD040
+#define I82580_I_PHY_ID 0x015403A0
#define IGP04E1000_E_PHY_ID 0x02A80391
#define M88_VENDOR 0x0141
@@ -1442,6 +1519,7 @@
#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */
#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */
+
/* M88EC018 Rev 2 specific DownShift settings */
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00
#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000
@@ -1575,5 +1653,34 @@
#define E1000_LSECRXCTRL_RSV_MASK 0xFFFFFF33
+/* DMA Coalescing register fields */
+#define E1000_DMACR_DMACWT_MASK 0x00003FFF /* DMA Coalescing
+ * Watchdog Timer */
+#define E1000_DMACR_DMACTHR_MASK 0x00FF0000 /* DMA Coalescing Receive
+ * Threshold */
+#define E1000_DMACR_DMACTHR_SHIFT 16
+#define E1000_DMACR_DMAC_LX_MASK 0x30000000 /* Lx when no PCIe
+ * transactions */
+#define E1000_DMACR_DMAC_LX_SHIFT 28
+#define E1000_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */
+
+#define E1000_DMCTXTH_DMCTTHR_MASK 0x00000FFF /* DMA Coalescing Transmit
+ * Threshold */
+
+#define E1000_DMCTLX_TTLX_MASK 0x00000FFF /* Time to LX request */
+
+#define E1000_DMCRTRH_UTRESH_MASK 0x0007FFFF /* Receive Traffic Rate
+ * Threshold */
+#define E1000_DMCRTRH_LRPRCW 0x80000000 /* Rcv packet rate in
+ * current window */
+
+#define E1000_DMCCNT_CCOUNT_MASK 0x01FFFFFF /* DMA Coal Rcv Traffic
+ * Current Cnt */
+
+#define E1000_FCRTC_RTH_COAL_MASK 0x0003FFF0 /* Flow ctrl Rcv Threshold
+ * High val */
+#define E1000_FCRTC_RTH_COAL_SHIFT 4
+#define E1000_PCIEMISC_LX_DECISION 0x00000080 /* Lx power decision based
+ on DMA coal */
#endif /* _E1000_DEFINES_H_ */
diff --git a/sys/dev/e1000/e1000_hw.h b/sys/dev/e1000/e1000_hw.h
index 6afa4fbb8bd5..ce5dffdd67d3 100644
--- a/sys/dev/e1000/e1000_hw.h
+++ b/sys/dev/e1000/e1000_hw.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -100,6 +100,7 @@ struct e1000_hw;
#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098
#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA
#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB
+#define E1000_DEV_ID_ICH8_82567V_3 0x1501
#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049
#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A
#define E1000_DEV_ID_ICH8_IGP_C 0x104B
@@ -121,6 +122,7 @@ struct e1000_hw;
#define E1000_DEV_ID_ICH10_R_BM_V 0x10CE
#define E1000_DEV_ID_ICH10_D_BM_LM 0x10DE
#define E1000_DEV_ID_ICH10_D_BM_LF 0x10DF
+
#define E1000_DEV_ID_PCH_M_HV_LM 0x10EA
#define E1000_DEV_ID_PCH_M_HV_LC 0x10EB
#define E1000_DEV_ID_PCH_D_HV_DM 0x10EF
@@ -130,11 +132,17 @@ struct e1000_hw;
#define E1000_DEV_ID_82576_SERDES 0x10E7
#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8
#define E1000_DEV_ID_82576_NS 0x150A
-#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D
+#define E1000_DEV_ID_82576_NS_SERDES 0x1518
+#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D
#define E1000_DEV_ID_82575EB_COPPER 0x10A7
#define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9
#define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6
#define E1000_DEV_ID_82575GB_QUAD_COPPER_PM 0x10E2
+#define E1000_DEV_ID_82580_COPPER 0x150E
+#define E1000_DEV_ID_82580_FIBER 0x150F
+#define E1000_DEV_ID_82580_SERDES 0x1510
+#define E1000_DEV_ID_82580_SGMII 0x1511
+#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516
#define E1000_REVISION_0 0
#define E1000_REVISION_1 1
#define E1000_REVISION_2 2
@@ -143,9 +151,13 @@ struct e1000_hw;
#define E1000_FUNC_0 0
#define E1000_FUNC_1 1
+#define E1000_FUNC_2 2
+#define E1000_FUNC_3 3
#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN0 0
#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN1 3
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN2 6
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN3 9
enum e1000_mac_type {
e1000_undefined = 0,
@@ -173,6 +185,7 @@ enum e1000_mac_type {
e1000_pchlan,
e1000_82575,
e1000_82576,
+ e1000_82580,
e1000_num_macs /* List is 1-based, so subtract 1 for TRUE count. */
};
@@ -213,6 +226,7 @@ enum e1000_phy_type {
e1000_phy_bm,
e1000_phy_82578,
e1000_phy_82577,
+ e1000_phy_82580,
e1000_phy_vf,
};
@@ -587,11 +601,11 @@ struct e1000_mac_operations {
s32 (*reset_hw)(struct e1000_hw *);
s32 (*init_hw)(struct e1000_hw *);
void (*shutdown_serdes)(struct e1000_hw *);
+ void (*power_up_serdes)(struct e1000_hw *);
s32 (*setup_link)(struct e1000_hw *);
s32 (*setup_physical_interface)(struct e1000_hw *);
s32 (*setup_led)(struct e1000_hw *);
void (*write_vfta)(struct e1000_hw *, u32, u32);
- void (*mta_set)(struct e1000_hw *, u32);
void (*config_collision_dist)(struct e1000_hw *);
void (*rar_set)(struct e1000_hw *, u8*, u32);
s32 (*read_mac_addr)(struct e1000_hw *);
@@ -615,11 +629,13 @@ struct e1000_phy_operations {
s32 (*get_cable_length)(struct e1000_hw *);
s32 (*get_info)(struct e1000_hw *);
s32 (*read_reg)(struct e1000_hw *, u32, u16 *);
+ s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *);
void (*release)(struct e1000_hw *);
s32 (*reset)(struct e1000_hw *);
s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
s32 (*write_reg)(struct e1000_hw *, u32, u16);
+ s32 (*write_reg_locked)(struct e1000_hw *, u32, u16);
void (*power_up)(struct e1000_hw *);
void (*power_down)(struct e1000_hw *);
};
@@ -657,6 +673,7 @@ struct e1000_mac_info {
u16 ifs_ratio;
u16 ifs_step_size;
u16 mta_reg_count;
+ u16 uta_reg_count;
/* Maximum size of the MTA register table in all supported adapters */
#define MAX_MTA_REG 128
@@ -666,6 +683,7 @@ struct e1000_mac_info {
u8 forced_speed_duplex;
bool adaptive_ifs;
+ bool has_fwsm;
bool arc_subsystem_valid;
bool asf_firmware_present;
bool autoneg;
@@ -768,6 +786,10 @@ struct e1000_dev_spec_82571 {
u32 smb_counter;
};
+struct e1000_dev_spec_80003es2lan {
+ bool mdic_wa_enable;
+};
+
struct e1000_shadow_ram {
u16 value;
bool modified;
@@ -778,6 +800,9 @@ struct e1000_shadow_ram {
struct e1000_dev_spec_ich8lan {
bool kmrn_lock_loss_workaround_enabled;
struct e1000_shadow_ram shadow_ram[E1000_SHADOW_RAM_WORDS];
+ E1000_MUTEX nvm_mutex;
+ E1000_MUTEX swflag_mutex;
+ bool nvm_k1_enabled;
};
struct e1000_dev_spec_82575 {
@@ -810,6 +835,7 @@ struct e1000_hw {
struct e1000_dev_spec_82542 _82542;
struct e1000_dev_spec_82543 _82543;
struct e1000_dev_spec_82571 _82571;
+ struct e1000_dev_spec_80003es2lan _80003es2lan;
struct e1000_dev_spec_ich8lan ich8lan;
struct e1000_dev_spec_82575 _82575;
struct e1000_dev_spec_vf vf;
diff --git a/sys/dev/e1000/e1000_ich8lan.c b/sys/dev/e1000/e1000_ich8lan.c
index a80955a5a367..6b3c25d8218d 100644
--- a/sys/dev/e1000/e1000_ich8lan.c
+++ b/sys/dev/e1000/e1000_ich8lan.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -68,10 +68,12 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw);
static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw);
static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw);
static void e1000_release_swflag_ich8lan(struct e1000_hw *hw);
+static s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw);
+static void e1000_release_nvm_ich8lan(struct e1000_hw *hw);
static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw);
static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw);
-static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw);
+static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active);
static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw,
bool active);
static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw,
@@ -95,6 +97,7 @@ static s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw,
static s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw);
static s32 e1000_led_on_ich8lan(struct e1000_hw *hw);
static s32 e1000_led_off_ich8lan(struct e1000_hw *hw);
+static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
static s32 e1000_setup_led_pchlan(struct e1000_hw *hw);
static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw);
static s32 e1000_led_on_pchlan(struct e1000_hw *hw);
@@ -103,7 +106,6 @@ static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank);
static s32 e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout);
static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw);
-static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw);
static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw);
static s32 e1000_read_flash_byte_ich8lan(struct e1000_hw *hw,
@@ -120,6 +122,10 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
u8 size, u16 data);
static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw);
static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
+static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw);
+static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
+static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw);
+static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
/* Offset 04h HSFSTS */
@@ -171,6 +177,7 @@ union ich8_hws_flash_regacc {
static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
+ u32 ctrl;
s32 ret_val = E1000_SUCCESS;
DEBUGFUNC("e1000_init_phy_params_pchlan");
@@ -179,35 +186,88 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
phy->reset_delay_us = 100;
phy->ops.acquire = e1000_acquire_swflag_ich8lan;
- phy->ops.check_polarity = e1000_check_polarity_ife;
phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
- phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
- phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
phy->ops.get_cfg_done = e1000_get_cfg_done_ich8lan;
- phy->ops.get_info = e1000_get_phy_info_ich8lan;
phy->ops.read_reg = e1000_read_phy_reg_hv;
+ phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
phy->ops.release = e1000_release_swflag_ich8lan;
phy->ops.reset = e1000_phy_hw_reset_ich8lan;
- phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan;
- phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan;
+ phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
+ phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.write_reg = e1000_write_phy_reg_hv;
+ phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked;
phy->ops.power_up = e1000_power_up_phy_copper;
phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+ if ((hw->mac.type == e1000_pchlan) &&
+ (!(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))) {
+
+ /*
+ * The MAC-PHY interconnect may still be in SMBus mode
+ * after Sx->S0. Toggle the LANPHYPC Value bit to force
+ * the interconnect to PCIe mode, but only if there is no
+ * firmware present otherwise firmware will have done it.
+ */
+ ctrl = E1000_READ_REG(hw, E1000_CTRL);
+ ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
+ ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+ usec_delay(10);
+ ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+ msec_delay(50);
+ }
+
+ /*
+ * Reset the PHY before any acccess to it. Doing so, ensures that
+ * the PHY is in a known good state before we read/write PHY registers.
+ * The generic reset is sufficient here, because we haven't determined
+ * the PHY type yet.
+ */
+ ret_val = e1000_phy_hw_reset_generic(hw);
+ if (ret_val)
+ goto out;
+
phy->id = e1000_phy_unknown;
- e1000_get_phy_id(hw);
+ ret_val = e1000_get_phy_id(hw);
+ if (ret_val)
+ goto out;
+ if ((phy->id == 0) || (phy->id == PHY_REVISION_MASK)) {
+ /*
+ * In case the PHY needs to be in mdio slow mode (eg. 82577),
+ * set slow mode and try to get the PHY id again.
+ */
+ ret_val = e1000_set_mdio_slow_mode_hv(hw);
+ if (ret_val)
+ goto out;
+ ret_val = e1000_get_phy_id(hw);
+ if (ret_val)
+ goto out;
+ }
phy->type = e1000_get_phy_type_from_id(phy->id);
- if (phy->type == e1000_phy_82577) {
+ switch (phy->type) {
+ case e1000_phy_82577:
phy->ops.check_polarity = e1000_check_polarity_82577;
phy->ops.force_speed_duplex =
e1000_phy_force_speed_duplex_82577;
- phy->ops.get_cable_length = e1000_get_cable_length_82577;
+ phy->ops.get_cable_length = e1000_get_cable_length_82577;
phy->ops.get_info = e1000_get_phy_info_82577;
phy->ops.commit = e1000_phy_sw_reset_generic;
+ break;
+ case e1000_phy_82578:
+ phy->ops.check_polarity = e1000_check_polarity_m88;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+ phy->ops.get_cable_length = e1000_get_cable_length_m88;
+ phy->ops.get_info = e1000_get_phy_info_m88;
+ break;
+ default:
+ ret_val = -E1000_ERR_PHY;
+ break;
}
+out:
return ret_val;
}
@@ -229,12 +289,9 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
phy->reset_delay_us = 100;
phy->ops.acquire = e1000_acquire_swflag_ich8lan;
- phy->ops.check_polarity = e1000_check_polarity_ife;
phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
- phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
phy->ops.get_cfg_done = e1000_get_cfg_done_ich8lan;
- phy->ops.get_info = e1000_get_phy_info_ich8lan;
phy->ops.read_reg = e1000_read_phy_reg_igp;
phy->ops.release = e1000_release_swflag_ich8lan;
phy->ops.reset = e1000_phy_hw_reset_ich8lan;
@@ -273,12 +330,20 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
case IGP03E1000_E_PHY_ID:
phy->type = e1000_phy_igp_3;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+ phy->ops.read_reg_locked = e1000_read_phy_reg_igp_locked;
+ phy->ops.write_reg_locked = e1000_write_phy_reg_igp_locked;
+ phy->ops.get_info = e1000_get_phy_info_igp;
+ phy->ops.check_polarity = e1000_check_polarity_igp;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp;
break;
case IFE_E_PHY_ID:
case IFE_PLUS_E_PHY_ID:
case IFE_C_E_PHY_ID:
phy->type = e1000_phy_ife;
phy->autoneg_mask = E1000_ALL_NOT_GIG;
+ phy->ops.get_info = e1000_get_phy_info_ife;
+ phy->ops.check_polarity = e1000_check_polarity_ife;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
break;
case BME1000_E_PHY_ID:
phy->type = e1000_phy_bm;
@@ -286,6 +351,9 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
phy->ops.read_reg = e1000_read_phy_reg_bm;
phy->ops.write_reg = e1000_write_phy_reg_bm;
phy->ops.commit = e1000_phy_sw_reset_generic;
+ phy->ops.get_info = e1000_get_phy_info_m88;
+ phy->ops.check_polarity = e1000_check_polarity_m88;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
break;
default:
ret_val = -E1000_ERR_PHY;
@@ -353,10 +421,13 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
dev_spec->shadow_ram[i].value = 0xFFFF;
}
+ E1000_MUTEX_INIT(&dev_spec->nvm_mutex);
+ E1000_MUTEX_INIT(&dev_spec->swflag_mutex);
+
/* Function Pointers */
- nvm->ops.acquire = e1000_acquire_swflag_ich8lan;
+ nvm->ops.acquire = e1000_acquire_nvm_ich8lan;
+ nvm->ops.release = e1000_release_nvm_ich8lan;
nvm->ops.read = e1000_read_nvm_ich8lan;
- nvm->ops.release = e1000_release_swflag_ich8lan;
nvm->ops.update = e1000_update_nvm_checksum_ich8lan;
nvm->ops.valid_led_default = e1000_valid_led_default_ich8lan;
nvm->ops.validate = e1000_validate_nvm_checksum_ich8lan;
@@ -391,8 +462,12 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
mac->rar_entry_count--;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = TRUE;
- /* Set if manageability features are enabled. */
- mac->arc_subsystem_valid = TRUE;
+ /* FWSM register */
+ mac->has_fwsm = TRUE;
+ /* ARC subsystem not supported */
+ mac->arc_subsystem_valid = FALSE;
+ /* Adaptive IFS supported */
+ mac->adaptive_ifs = TRUE;
/* Function pointers */
@@ -409,15 +484,13 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
/* physical interface setup */
mac->ops.setup_physical_interface = e1000_setup_copper_link_ich8lan;
/* check for link */
- mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+ mac->ops.check_for_link = e1000_check_for_copper_link_ich8lan;
/* check management mode */
mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
/* link info */
mac->ops.get_link_up_info = e1000_get_link_up_info_ich8lan;
/* multicast address update */
mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* clear hardware counters */
mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_ich8lan;
@@ -460,11 +533,99 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
if (mac->type == e1000_ich8lan)
e1000_set_kmrn_lock_loss_workaround_ich8lan(hw, TRUE);
-
return E1000_SUCCESS;
}
/**
+ * e1000_check_for_copper_link_ich8lan - Check for link (Copper)
+ * @hw: pointer to the HW structure
+ *
+ * Checks to see of the link status of the hardware has changed. If a
+ * change in link status has been detected, then we read the PHY registers
+ * to get the current speed/duplex if link exists.
+ **/
+static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
+{
+ struct e1000_mac_info *mac = &hw->mac;
+ s32 ret_val;
+ bool link;
+
+ DEBUGFUNC("e1000_check_for_copper_link_ich8lan");
+
+ /*
+ * We only want to go out to the PHY registers to see if Auto-Neg
+ * has completed and/or if our link status has changed. The
+ * get_link_status flag is set upon receiving a Link Status
+ * Change or Rx Sequence Error interrupt.
+ */
+ if (!mac->get_link_status) {
+ ret_val = E1000_SUCCESS;
+ goto out;
+ }
+
+ /*
+ * First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ */
+ ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (hw->mac.type == e1000_pchlan) {
+ ret_val = e1000_k1_gig_workaround_hv(hw, link);
+ if (ret_val)
+ goto out;
+ }
+
+ if (!link)
+ goto out; /* No link detected */
+
+ mac->get_link_status = FALSE;
+
+ if (hw->phy.type == e1000_phy_82578) {
+ ret_val = e1000_link_stall_workaround_hv(hw);
+ if (ret_val)
+ goto out;
+ }
+
+ /*
+ * Check if there was DownShift, must be checked
+ * immediately after link-up
+ */
+ e1000_check_downshift_generic(hw);
+
+ /*
+ * If we are forcing speed/duplex, then we simply return since
+ * we have already determined whether we have link or not.
+ */
+ if (!mac->autoneg) {
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ /*
+ * Auto-Neg is enabled. Auto Speed Detection takes care
+ * of MAC speed/duplex configuration. So we only need to
+ * configure Collision Distance in the MAC.
+ */
+ e1000_config_collision_dist_generic(hw);
+
+ /*
+ * Configure Flow Control now that Auto-Neg has completed.
+ * First, we need to restore the desired flow control
+ * settings because we may have had to re-autoneg with a
+ * different link partner.
+ */
+ ret_val = e1000_config_fc_after_link_up_generic(hw);
+ if (ret_val)
+ DEBUGOUT("Error configuring flow control\n");
+
+out:
+ return ret_val;
+}
+
+/**
* e1000_init_function_pointers_ich8lan - Initialize ICH8 function pointers
* @hw: pointer to the HW structure
*
@@ -491,12 +652,41 @@ void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw)
}
/**
+ * e1000_acquire_nvm_ich8lan - Acquire NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Acquires the mutex for performing NVM operations.
+ **/
+static s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_acquire_nvm_ich8lan");
+
+ E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.nvm_mutex);
+
+ return E1000_SUCCESS;
+}
+
+/**
+ * e1000_release_nvm_ich8lan - Release NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Releases the mutex used while performing NVM operations.
+ **/
+static void e1000_release_nvm_ich8lan(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_release_nvm_ich8lan");
+
+ E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.nvm_mutex);
+
+ return;
+}
+
+/**
* e1000_acquire_swflag_ich8lan - Acquire software control flag
* @hw: pointer to the HW structure
*
- * Acquires the software control flag for performing NVM and PHY
- * operations. This is a function pointer entry point only called by
- * read/write routines for the PHY and NVM parts.
+ * Acquires the software control flag for performing PHY and select
+ * MAC CSR accesses.
**/
static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
{
@@ -505,23 +695,39 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
DEBUGFUNC("e1000_acquire_swflag_ich8lan");
+ E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
while (timeout) {
extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
+ break;
- if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)) {
- extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
- E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
-
- extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
- if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
- break;
- }
msec_delay_irq(1);
timeout--;
}
if (!timeout) {
DEBUGOUT("SW/FW/HW has locked the resource for too long.\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ timeout = SW_FLAG_TIMEOUT;
+
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+ E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+
+ while (timeout) {
+ extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+ break;
+
+ msec_delay_irq(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Failed to acquire the semaphore.\n");
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
ret_val = -E1000_ERR_CONFIG;
@@ -529,6 +735,9 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
}
out:
+ if (ret_val)
+ E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
return ret_val;
}
@@ -536,9 +745,8 @@ out:
* e1000_release_swflag_ich8lan - Release software control flag
* @hw: pointer to the HW structure
*
- * Releases the software control flag for performing NVM and PHY operations.
- * This is a function pointer entry point only called by read/write
- * routines for the PHY and NVM parts.
+ * Releases the software control flag for performing PHY and select
+ * MAC CSR accesses.
**/
static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
{
@@ -550,6 +758,8 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+ E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
return;
}
@@ -587,6 +797,9 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
DEBUGFUNC("e1000_check_reset_block_ich8lan");
+ if (hw->phy.reset_disable)
+ return E1000_BLK_PHY_RESET;
+
fwsm = E1000_READ_REG(hw, E1000_FWSM);
return (fwsm & E1000_ICH_FWSM_RSPCIPHY) ? E1000_SUCCESS
@@ -594,6 +807,324 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
}
/**
+ * e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration
+ * @hw: pointer to the HW structure
+ *
+ * SW should configure the LCD from the NVM extended configuration region
+ * as a workaround for certain parts.
+ **/
+static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask;
+ s32 ret_val = E1000_SUCCESS;
+ u16 word_addr, reg_data, reg_addr, phy_page = 0;
+
+ if (!(hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) &&
+ !(hw->mac.type == e1000_pchlan))
+ return ret_val;
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
+ /*
+ * Initialize the PHY from the NVM on ICH platforms. This
+ * is needed due to an issue where the NVM configuration is
+ * not properly autoloaded after power transitions.
+ * Therefore, after each PHY reset, we will load the
+ * configuration data out of the NVM manually.
+ */
+ if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_M_AMT) ||
+ (hw->device_id == E1000_DEV_ID_ICH8_IGP_M) ||
+ (hw->mac.type == e1000_pchlan))
+ sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
+ else
+ sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG;
+
+ data = E1000_READ_REG(hw, E1000_FEXTNVM);
+ if (!(data & sw_cfg_mask))
+ goto out;
+
+ /* Wait for basic configuration completes before proceeding */
+ e1000_lan_init_done_ich8lan(hw);
+
+ /*
+ * Make sure HW does not configure LCD from PHY
+ * extended configuration before SW configuration
+ */
+ data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE)
+ goto out;
+
+ cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE);
+ cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
+ cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
+ if (!cnf_size)
+ goto out;
+
+ cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
+ cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
+
+ if (!(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE) &&
+ (hw->mac.type == e1000_pchlan)) {
+ /*
+ * HW configures the SMBus address and LEDs when the
+ * OEM and LCD Write Enable bits are set in the NVM.
+ * When both NVM bits are cleared, SW will configure
+ * them instead.
+ */
+ data = E1000_READ_REG(hw, E1000_STRAP);
+ data &= E1000_STRAP_SMBUS_ADDRESS_MASK;
+ reg_data = data >> E1000_STRAP_SMBUS_ADDRESS_SHIFT;
+ reg_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
+ ret_val = e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR,
+ reg_data);
+ if (ret_val)
+ goto out;
+
+ data = E1000_READ_REG(hw, E1000_LEDCTL);
+ ret_val = e1000_write_phy_reg_hv_locked(hw, HV_LED_CONFIG,
+ (u16)data);
+ if (ret_val)
+ goto out;
+ }
+
+ /* Configure LCD from extended configuration region. */
+
+ /* cnf_base_addr is in DWORD */
+ word_addr = (u16)(cnf_base_addr << 1);
+
+ for (i = 0; i < cnf_size; i++) {
+ ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2), 1,
+ &reg_data);
+ if (ret_val)
+ goto out;
+
+ ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1),
+ 1, &reg_addr);
+ if (ret_val)
+ goto out;
+
+ /* Save off the PHY page for future writes. */
+ if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
+ phy_page = reg_data;
+ continue;
+ }
+
+ reg_addr &= PHY_REG_MASK;
+ reg_addr |= phy_page;
+
+ ret_val = phy->ops.write_reg_locked(hw, (u32)reg_addr,
+ reg_data);
+ if (ret_val)
+ goto out;
+ }
+
+out:
+ hw->phy.ops.release(hw);
+ return ret_val;
+}
+
+/**
+ * e1000_k1_gig_workaround_hv - K1 Si workaround
+ * @hw: pointer to the HW structure
+ * @link: link up bool flag
+ *
+ * If K1 is enabled for 1Gbps, the MAC might stall when transitioning
+ * from a lower speed. This workaround disables K1 whenever link is at 1Gig
+ * If link is down, the function will restore the default K1 setting located
+ * in the NVM.
+ **/
+static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 status_reg = 0;
+ bool k1_enable = hw->dev_spec.ich8lan.nvm_k1_enabled;
+
+ DEBUGFUNC("e1000_k1_gig_workaround_hv");
+
+ if (hw->mac.type != e1000_pchlan)
+ goto out;
+
+ /* Wrap the whole flow with the sw flag */
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ /* Disable K1 when link is 1Gbps, otherwise use the NVM setting */
+ if (link) {
+ if (hw->phy.type == e1000_phy_82578) {
+ ret_val = hw->phy.ops.read_reg_locked(hw, BM_CS_STATUS,
+ &status_reg);
+ if (ret_val)
+ goto release;
+
+ status_reg &= BM_CS_STATUS_LINK_UP |
+ BM_CS_STATUS_RESOLVED |
+ BM_CS_STATUS_SPEED_MASK;
+
+ if (status_reg == (BM_CS_STATUS_LINK_UP |
+ BM_CS_STATUS_RESOLVED |
+ BM_CS_STATUS_SPEED_1000))
+ k1_enable = FALSE;
+ }
+
+ if (hw->phy.type == e1000_phy_82577) {
+ ret_val = hw->phy.ops.read_reg_locked(hw, HV_M_STATUS,
+ &status_reg);
+ if (ret_val)
+ goto release;
+
+ status_reg &= HV_M_STATUS_LINK_UP |
+ HV_M_STATUS_AUTONEG_COMPLETE |
+ HV_M_STATUS_SPEED_MASK;
+
+ if (status_reg == (HV_M_STATUS_LINK_UP |
+ HV_M_STATUS_AUTONEG_COMPLETE |
+ HV_M_STATUS_SPEED_1000))
+ k1_enable = FALSE;
+ }
+
+ /* Link stall fix for link up */
+ ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
+ 0x0100);
+ if (ret_val)
+ goto release;
+
+ } else {
+ /* Link stall fix for link down */
+ ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
+ 0x4100);
+ if (ret_val)
+ goto release;
+ }
+
+ ret_val = e1000_configure_k1_ich8lan(hw, k1_enable);
+
+release:
+ hw->phy.ops.release(hw);
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_configure_k1_ich8lan - Configure K1 power state
+ * @hw: pointer to the HW structure
+ * @enable: K1 state to configure
+ *
+ * Configure the K1 power state based on the provided parameter.
+ * Assumes semaphore already acquired.
+ *
+ * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
+ **/
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u32 ctrl_reg = 0;
+ u32 ctrl_ext = 0;
+ u32 reg = 0;
+ u16 kmrn_reg = 0;
+
+ ret_val = e1000_read_kmrn_reg_locked(hw,
+ E1000_KMRNCTRLSTA_K1_CONFIG,
+ &kmrn_reg);
+ if (ret_val)
+ goto out;
+
+ if (k1_enable)
+ kmrn_reg |= E1000_KMRNCTRLSTA_K1_ENABLE;
+ else
+ kmrn_reg &= ~E1000_KMRNCTRLSTA_K1_ENABLE;
+
+ ret_val = e1000_write_kmrn_reg_locked(hw,
+ E1000_KMRNCTRLSTA_K1_CONFIG,
+ kmrn_reg);
+ if (ret_val)
+ goto out;
+
+ usec_delay(20);
+ ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+ ctrl_reg = E1000_READ_REG(hw, E1000_CTRL);
+
+ reg = ctrl_reg & ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
+ reg |= E1000_CTRL_FRCSPD;
+ E1000_WRITE_REG(hw, E1000_CTRL, reg);
+
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+ usec_delay(20);
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl_reg);
+ E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+ usec_delay(20);
+
+out:
+ return ret_val;
+}
+
+/**
+ * e1000_oem_bits_config_ich8lan - SW-based LCD Configuration
+ * @hw: pointer to the HW structure
+ * @d0_state: boolean if entering d0 or d3 device state
+ *
+ * SW will configure Gbe Disable and LPLU based on the NVM. The four bits are
+ * collectively called OEM bits. The OEM Write Enable bit and SW Config bit
+ * in NVM determines whether HW should configure LPLU and Gbe Disable.
+ **/
+s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
+{
+ s32 ret_val = 0;
+ u32 mac_reg;
+ u16 oem_reg;
+
+ if (hw->mac.type != e1000_pchlan)
+ return ret_val;
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
+ mac_reg = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)
+ goto out;
+
+ mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM);
+ if (!(mac_reg & E1000_FEXTNVM_SW_CONFIG_ICH8M))
+ goto out;
+
+ mac_reg = E1000_READ_REG(hw, E1000_PHY_CTRL);
+
+ ret_val = hw->phy.ops.read_reg_locked(hw, HV_OEM_BITS, &oem_reg);
+ if (ret_val)
+ goto out;
+
+ oem_reg &= ~(HV_OEM_BITS_GBE_DIS | HV_OEM_BITS_LPLU);
+
+ if (d0_state) {
+ if (mac_reg & E1000_PHY_CTRL_GBE_DISABLE)
+ oem_reg |= HV_OEM_BITS_GBE_DIS;
+
+ if (mac_reg & E1000_PHY_CTRL_D0A_LPLU)
+ oem_reg |= HV_OEM_BITS_LPLU;
+ } else {
+ if (mac_reg & E1000_PHY_CTRL_NOND0A_GBE_DISABLE)
+ oem_reg |= HV_OEM_BITS_GBE_DIS;
+
+ if (mac_reg & E1000_PHY_CTRL_NOND0A_LPLU)
+ oem_reg |= HV_OEM_BITS_LPLU;
+ }
+ /* Restart auto-neg to activate the bits */
+ if (!hw->phy.ops.check_reset_block(hw))
+ oem_reg |= HV_OEM_BITS_RESTART_AN;
+ ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg);
+
+out:
+ hw->phy.ops.release(hw);
+
+ return ret_val;
+}
+
+
+/**
* e1000_hv_phy_powerdown_workaround_ich8lan - Power down workaround on Sx
* @hw: pointer to the HW structure
**/
@@ -606,15 +1137,43 @@ s32 e1000_hv_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw)
}
/**
+ * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
+ * @hw: pointer to the HW structure
+ **/
+static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw)
+{
+ s32 ret_val;
+ u16 data;
+
+ ret_val = hw->phy.ops.read_reg(hw, HV_KMRN_MODE_CTRL, &data);
+ if (ret_val)
+ return ret_val;
+
+ data |= HV_KMRN_MDIO_SLOW;
+
+ ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_MODE_CTRL, data);
+
+ return ret_val;
+}
+
+/**
* e1000_hv_phy_workarounds_ich8lan - A series of Phy workarounds to be
* done after every PHY reset.
**/
static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
+ u16 phy_data;
if (hw->mac.type != e1000_pchlan)
- return ret_val;
+ goto out;
+
+ /* Set MDIO slow mode before any other MDIO access */
+ if (hw->phy.type == e1000_phy_82577) {
+ ret_val = e1000_set_mdio_slow_mode_hv(hw);
+ if (ret_val)
+ goto out;
+ }
/* Hanksville M Phy init for IEEE. */
if ((hw->revision_id == 2) &&
@@ -648,12 +1207,12 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
/* Disable generation of early preamble */
ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 25), 0x4431);
if (ret_val)
- return ret_val;
+ goto out;
/* Preamble tuning for SSC */
ret_val = hw->phy.ops.write_reg(hw, PHY_REG(770, 16), 0xA204);
if (ret_val)
- return ret_val;
+ goto out;
}
if (hw->phy.type == e1000_phy_82578) {
@@ -662,13 +1221,13 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
ret_val = hw->phy.ops.write_reg(hw, (1 << 6) | 0x29,
0x66C0);
if (ret_val)
- return ret_val;
+ goto out;
/* PHY config */
ret_val = hw->phy.ops.write_reg(hw, (1 << 6) | 0x1E,
0xFFFF);
if (ret_val)
- return ret_val;
+ goto out;
}
/*
@@ -691,20 +1250,46 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
*/
ret_val = hw->phy.ops.write_reg(hw, PHY_REG(768, 25), 0x0400);
if (ret_val)
- return ret_val;
+ goto out;
ret_val = hw->phy.ops.write_reg(hw, PHY_REG(768, 25), 0x0400);
if (ret_val)
- return ret_val;
+ goto out;
}
/* Select page 0 */
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
- return ret_val;
+ goto out;
+
hw->phy.addr = 1;
- e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
+ ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
hw->phy.ops.release(hw);
+ if (ret_val)
+ goto out;
+
+ /*
+ * Configure the K1 Si workaround during phy reset assuming there is
+ * link so that it disables K1 if link is in 1Gbps.
+ */
+ ret_val = e1000_k1_gig_workaround_hv(hw, TRUE);
+ if (ret_val)
+ goto out;
+ /* Workaround for link disconnects on a busy hub in half duplex */
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ ret_val = hw->phy.ops.read_reg_locked(hw,
+ PHY_REG(BM_PORT_CTRL_PAGE, 17),
+ &phy_data);
+ if (ret_val)
+ goto release;
+ ret_val = hw->phy.ops.write_reg_locked(hw,
+ PHY_REG(BM_PORT_CTRL_PAGE, 17),
+ phy_data & 0x00FF);
+release:
+ hw->phy.ops.release(hw);
+out:
return ret_val;
}
@@ -752,10 +1337,8 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
{
- struct e1000_phy_info *phy = &hw->phy;
- u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask;
- s32 ret_val;
- u16 word_addr, reg_data, reg_addr, phy_page = 0;
+ s32 ret_val = E1000_SUCCESS;
+ u16 reg;
DEBUGFUNC("e1000_phy_hw_reset_ich8lan");
@@ -766,168 +1349,62 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
/* Allow time for h/w to get to a quiescent state after reset */
msec_delay(10);
- if (hw->mac.type == e1000_pchlan) {
+ /* Perform any necessary post-reset workarounds */
+ switch (hw->mac.type) {
+ case e1000_pchlan:
ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
if (ret_val)
goto out;
+ break;
+ default:
+ break;
}
- /*
- * Initialize the PHY from the NVM on ICH platforms. This
- * is needed due to an issue where the NVM configuration is
- * not properly autoloaded after power transitions.
- * Therefore, after each PHY reset, we will load the
- * configuration data out of the NVM manually.
- */
- if (hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) {
- /* Check if SW needs configure the PHY */
- if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_M_AMT) ||
- (hw->device_id == E1000_DEV_ID_ICH8_IGP_M))
- sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
- else
- sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG;
-
- data = E1000_READ_REG(hw, E1000_FEXTNVM);
- if (!(data & sw_cfg_mask))
- goto out;
-
- /* Wait for basic configuration completes before proceeding */
- e1000_lan_init_done_ich8lan(hw);
-
- /*
- * Make sure HW does not configure LCD from PHY
- * extended configuration before SW configuration
- */
- data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
- if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE)
- goto out;
-
- cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE);
- cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
- cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
- if (!cnf_size)
- goto out;
-
- cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
- cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
-
- /* Configure LCD from extended configuration region. */
-
- /* cnf_base_addr is in DWORD */
- word_addr = (u16)(cnf_base_addr << 1);
-
- for (i = 0; i < cnf_size; i++) {
- ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2), 1,
- &reg_data);
- if (ret_val)
- goto out;
-
- ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1),
- 1, &reg_addr);
- if (ret_val)
- goto out;
-
- /* Save off the PHY page for future writes. */
- if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
- phy_page = reg_data;
- continue;
- }
+ /* Dummy read to clear the phy wakeup bit after lcd reset */
+ if (hw->mac.type == e1000_pchlan)
+ hw->phy.ops.read_reg(hw, BM_WUC, &reg);
- reg_addr |= phy_page;
+ /* Configure the LCD with the extended configuration region in NVM */
+ ret_val = e1000_sw_lcd_config_ich8lan(hw);
+ if (ret_val)
+ goto out;
- ret_val = phy->ops.write_reg(hw, (u32)reg_addr, reg_data);
- if (ret_val)
- goto out;
- }
- }
+ /* Configure the LCD with the OEM bits in NVM */
+ ret_val = e1000_oem_bits_config_ich8lan(hw, TRUE);
out:
return ret_val;
}
/**
- * e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info
- * @hw: pointer to the HW structure
- *
- * Wrapper for calling the get_phy_info routines for the appropriate phy type.
- **/
-static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw)
-{
- s32 ret_val = -E1000_ERR_PHY_TYPE;
-
- DEBUGFUNC("e1000_get_phy_info_ich8lan");
-
- switch (hw->phy.type) {
- case e1000_phy_ife:
- ret_val = e1000_get_phy_info_ife_ich8lan(hw);
- break;
- case e1000_phy_igp_3:
- case e1000_phy_bm:
- case e1000_phy_82578:
- case e1000_phy_82577:
- ret_val = e1000_get_phy_info_igp(hw);
- break;
- default:
- break;
- }
-
- return ret_val;
-}
-
-/**
- * e1000_get_phy_info_ife_ich8lan - Retrieves various IFE PHY states
+ * e1000_set_lplu_state_pchlan - Set Low Power Link Up state
* @hw: pointer to the HW structure
+ * @active: TRUE to enable LPLU, FALSE to disable
*
- * Populates "phy" structure with various feature states.
- * This function is only called by other family-specific
- * routines.
+ * Sets the LPLU state according to the active flag. For PCH, if OEM write
+ * bit are disabled in the NVM, writing the LPLU bits in the MAC will not set
+ * the phy speed. This function will manually set the LPLU bit and restart
+ * auto-neg as hw would do. D3 and D0 LPLU will call the same function
+ * since it configures the same bit.
**/
-static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw)
+static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active)
{
- struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
- u16 data;
- bool link;
+ s32 ret_val = E1000_SUCCESS;
+ u16 oem_reg;
- DEBUGFUNC("e1000_get_phy_info_ife_ich8lan");
+ DEBUGFUNC("e1000_set_lplu_state_pchlan");
- ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = hw->phy.ops.read_reg(hw, HV_OEM_BITS, &oem_reg);
if (ret_val)
goto out;
- if (!link) {
- DEBUGOUT("Phy info is only valid if link is up\n");
- ret_val = -E1000_ERR_CONFIG;
- goto out;
- }
-
- ret_val = phy->ops.read_reg(hw, IFE_PHY_SPECIAL_CONTROL, &data);
- if (ret_val)
- goto out;
- phy->polarity_correction = (data & IFE_PSC_AUTO_POLARITY_DISABLE)
- ? FALSE : TRUE;
-
- if (phy->polarity_correction) {
- ret_val = e1000_check_polarity_ife(hw);
- if (ret_val)
- goto out;
- } else {
- /* Polarity is forced */
- phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
- ? e1000_rev_polarity_reversed
- : e1000_rev_polarity_normal;
- }
-
- ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
- if (ret_val)
- goto out;
-
- phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? TRUE : FALSE;
+ if (active)
+ oem_reg |= HV_OEM_BITS_LPLU;
+ else
+ oem_reg &= ~HV_OEM_BITS_LPLU;
- /* The following parameters are undefined for 10/100 operation. */
- phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
- phy->local_rx = e1000_1000t_rx_status_undefined;
- phy->remote_rx = e1000_1000t_rx_status_undefined;
+ oem_reg |= HV_OEM_BITS_RESTART_AN;
+ ret_val = hw->phy.ops.write_reg(hw, HV_OEM_BITS, oem_reg);
out:
return ret_val;
@@ -1170,7 +1647,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
if (ret_val)
goto out;
if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
- E1000_ICH_NVM_SIG_VALUE) {
+ E1000_ICH_NVM_SIG_VALUE) {
*bank = 0;
goto out;
}
@@ -1178,11 +1655,11 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
/* Check bank 1 */
ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset +
bank1_offset,
- &sig_byte);
+ &sig_byte);
if (ret_val)
goto out;
if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
- E1000_ICH_NVM_SIG_VALUE) {
+ E1000_ICH_NVM_SIG_VALUE) {
*bank = 1;
goto out;
}
@@ -1223,17 +1700,18 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
goto out;
}
- ret_val = nvm->ops.acquire(hw);
- if (ret_val)
- goto out;
+ nvm->ops.acquire(hw);
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
- if (ret_val != E1000_SUCCESS)
- goto release;
+ if (ret_val != E1000_SUCCESS) {
+ DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+ bank = 0;
+ }
act_offset = (bank) ? nvm->flash_bank_size : 0;
act_offset += offset;
+ ret_val = E1000_SUCCESS;
for (i = 0; i < words; i++) {
if ((dev_spec->shadow_ram) &&
(dev_spec->shadow_ram[offset+i].modified)) {
@@ -1248,7 +1726,6 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
}
}
-release:
nvm->ops.release(hw);
out:
@@ -1534,9 +2011,7 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
goto out;
}
- ret_val = nvm->ops.acquire(hw);
- if (ret_val)
- goto out;
+ nvm->ops.acquire(hw);
for (i = 0; i < words; i++) {
dev_spec->shadow_ram[offset+i].modified = TRUE;
@@ -1577,9 +2052,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
if (nvm->type != e1000_nvm_flash_sw)
goto out;
- ret_val = nvm->ops.acquire(hw);
- if (ret_val)
- goto out;
+ nvm->ops.acquire(hw);
/*
* We're writing to the opposite bank so if we're on bank 1,
@@ -1588,26 +2061,22 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val != E1000_SUCCESS) {
- nvm->ops.release(hw);
- goto out;
+ DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+ bank = 0;
}
if (bank == 0) {
new_bank_offset = nvm->flash_bank_size;
old_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
- if (ret_val) {
- nvm->ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
} else {
old_bank_offset = nvm->flash_bank_size;
new_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
- if (ret_val) {
- nvm->ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
}
for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
@@ -1662,8 +2131,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
if (ret_val) {
DEBUGOUT("Flash commit failed.\n");
- nvm->ops.release(hw);
- goto out;
+ goto release;
}
/*
@@ -1674,18 +2142,15 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
- if (ret_val) {
- nvm->ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
+
data &= 0xBFFF;
ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
act_offset * 2 + 1,
(u8)(data >> 8));
- if (ret_val) {
- nvm->ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
/*
* And invalidate the previously valid segment by setting
@@ -1695,10 +2160,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1;
ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0);
- if (ret_val) {
- nvm->ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
/* Great! Everything worked, we can now clear the cached entries. */
for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
@@ -1706,14 +2169,17 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
dev_spec->shadow_ram[i].value = 0xFFFF;
}
+release:
nvm->ops.release(hw);
/*
* Reload the EEPROM, or else modifications will not appear
* until after the next adapter reset.
*/
- nvm->ops.reload(hw);
- msec_delay(10);
+ if (!ret_val) {
+ nvm->ops.reload(hw);
+ msec_delay(10);
+ }
out:
if (ret_val)
@@ -1829,10 +2295,10 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
* try...ICH_FLASH_CYCLE_REPEAT_COUNT times.
*/
hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
- if (hsfsts.hsf_status.flcerr == 1) {
+ if (hsfsts.hsf_status.flcerr == 1)
/* Repeat for some time before giving up. */
continue;
- } else if (hsfsts.hsf_status.flcdone == 0) {
+ if (hsfsts.hsf_status.flcdone == 0) {
DEBUGOUT("Timeout error - flash cycle "
"did not complete.");
break;
@@ -1960,7 +2426,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
/* Start with the base address, then add the sector offset. */
flash_linear_addr = hw->nvm.flash_base_addr;
- flash_linear_addr += (bank) ? (sector_size * iteration) : 0;
+ flash_linear_addr += (bank) ? flash_bank_size : 0;
for (j = 0; j < iteration ; j++) {
do {
@@ -2153,6 +2619,8 @@ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
{
+ struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+ u16 reg;
u32 ctrl, icr, kab;
s32 ret_val;
@@ -2188,6 +2656,18 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
E1000_WRITE_REG(hw, E1000_PBS, E1000_PBS_16K);
}
+ if (hw->mac.type == e1000_pchlan) {
+ /* Save the NVM K1 bit setting*/
+ ret_val = e1000_read_nvm(hw, E1000_NVM_K1_CONFIG, 1, &reg);
+ if (ret_val)
+ return ret_val;
+
+ if (reg & E1000_NVM_K1_ENABLE)
+ dev_spec->nvm_k1_enabled = TRUE;
+ else
+ dev_spec->nvm_k1_enabled = FALSE;
+ }
+
ctrl = E1000_READ_REG(hw, E1000_CTRL);
if (!hw->phy.ops.check_reset_block(hw) && !hw->phy.reset_disable) {
@@ -2213,6 +2693,17 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
if (!ret_val)
e1000_release_swflag_ich8lan(hw);
+ /* Perform any necessary post-reset workarounds */
+ switch (hw->mac.type) {
+ case e1000_pchlan:
+ ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+ if (ret_val)
+ goto out;
+ break;
+ default:
+ break;
+ }
+
if (ctrl & E1000_CTRL_PHY_RST)
ret_val = hw->phy.ops.get_cfg_done(hw);
@@ -2229,6 +2720,24 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
DEBUGOUT("Auto Read Done did not complete\n");
}
}
+ /* Dummy read to clear the phy wakeup bit after lcd reset */
+ if (hw->mac.type == e1000_pchlan)
+ hw->phy.ops.read_reg(hw, BM_WUC, &reg);
+
+ ret_val = e1000_sw_lcd_config_ich8lan(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = e1000_oem_bits_config_ich8lan(hw, TRUE);
+ if (ret_val)
+ goto out;
+ /*
+ * For PCH, this write will make sure that any noise
+ * will be detected as a CRC error and be dropped rather than show up
+ * as a bad packet to the DMA engine.
+ */
+ if (hw->mac.type == e1000_pchlan)
+ E1000_WRITE_REG(hw, E1000_CRC_OFFSET, 0x65656565);
E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
icr = E1000_READ_REG(hw, E1000_ICR);
@@ -2237,9 +2746,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
kab |= E1000_KABGTXD_BGSQLBIAS;
E1000_WRITE_REG(hw, E1000_KABGTXD, kab);
- if (hw->mac.type == e1000_pchlan)
- ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
-
+out:
return ret_val;
}
@@ -2269,8 +2776,8 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
/* Initialize identification LED */
ret_val = mac->ops.id_led_init(hw);
if (ret_val)
- /* This is not fatal and we should not stop init due to this */
DEBUGOUT("Error initializing identification LED\n");
+ /* This is not fatal and we should not stop init due to this */
/* Setup the receive address. */
e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
@@ -2316,7 +2823,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
if (mac->type == e1000_ich8lan)
snoop = PCIE_ICH8_SNOOP_ALL;
else
- snoop = (u32)~(PCIE_NO_SNOOP_ALL);
+ snoop = (u32) ~(PCIE_NO_SNOOP_ALL);
e1000_set_pcie_no_snoop_generic(hw, snoop);
ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
@@ -2387,6 +2894,14 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw)
E1000_WRITE_REG(hw, E1000_STATUS, reg);
}
+ /*
+ * work-around descriptor data corruption issue during nfs v2 udp
+ * traffic, just disable the nfs filtering capability
+ */
+ reg = E1000_READ_REG(hw, E1000_RFCTL);
+ reg |= (E1000_RFCTL_NFSW_DIS | E1000_RFCTL_NFSR_DIS);
+ E1000_WRITE_REG(hw, E1000_RFCTL, reg);
+
return;
}
@@ -2473,8 +2988,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
* and increase the max iterations when polling the phy;
* this fixes erroneous timeouts at 10Mbps.
*/
- ret_val = e1000_write_kmrn_reg_generic(hw,
- E1000_KMRNCTRLSTA_TIMEOUTS,
+ ret_val = e1000_write_kmrn_reg_generic(hw, E1000_KMRNCTRLSTA_TIMEOUTS,
0xFFFF);
if (ret_val)
goto out;
@@ -2788,6 +3302,7 @@ void e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw)
u32 phy_ctrl;
switch (hw->mac.type) {
+ case e1000_ich8lan:
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
@@ -2796,9 +3311,8 @@ void e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw)
E1000_PHY_CTRL_GBE_DISABLE;
E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
- /* Workaround SWFLAG unexpectedly set during S0->Sx */
if (hw->mac.type == e1000_pchlan)
- usec_delay(500);
+ e1000_phy_hw_reset_ich8lan(hw);
default:
break;
}
@@ -2814,17 +3328,14 @@ void e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw)
{
- s32 ret_val = E1000_SUCCESS;
-
DEBUGFUNC("e1000_cleanup_led_ich8lan");
if (hw->phy.type == e1000_phy_ife)
- ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
- 0);
- else
- E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default);
+ return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+ 0);
- return ret_val;
+ E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default);
+ return E1000_SUCCESS;
}
/**
@@ -2835,17 +3346,14 @@ static s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_led_on_ich8lan(struct e1000_hw *hw)
{
- s32 ret_val = E1000_SUCCESS;
-
DEBUGFUNC("e1000_led_on_ich8lan");
if (hw->phy.type == e1000_phy_ife)
- ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+ return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
(IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON));
- else
- E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2);
- return ret_val;
+ E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2);
+ return E1000_SUCCESS;
}
/**
@@ -2856,18 +3364,14 @@ static s32 e1000_led_on_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
{
- s32 ret_val = E1000_SUCCESS;
-
DEBUGFUNC("e1000_led_off_ich8lan");
if (hw->phy.type == e1000_phy_ife)
- ret_val = hw->phy.ops.write_reg(hw,
- IFE_PHY_SPECIAL_CONTROL_LED,
+ return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
(IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF));
- else
- E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1);
- return ret_val;
+ E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1);
+ return E1000_SUCCESS;
}
/**
@@ -2982,18 +3486,17 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
if (hw->mac.type >= e1000_pchlan) {
u32 status = E1000_READ_REG(hw, E1000_STATUS);
- if (status & E1000_STATUS_PHYRA) {
+ if (status & E1000_STATUS_PHYRA)
E1000_WRITE_REG(hw, E1000_STATUS, status &
~E1000_STATUS_PHYRA);
- } else
+ else
DEBUGOUT("PHY Reset Asserted not set - needs delay\n");
}
e1000_get_cfg_done_generic(hw);
/* If EEPROM is not marked present, init the IGP 3 PHY manually */
- if ((hw->mac.type != e1000_ich10lan) &&
- (hw->mac.type != e1000_pchlan)) {
+ if (hw->mac.type <= e1000_ich9lan) {
if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) &&
(hw->phy.type == e1000_phy_igp_3)) {
e1000_phy_init_script_igp3(hw);
diff --git a/sys/dev/e1000/e1000_ich8lan.h b/sys/dev/e1000/e1000_ich8lan.h
index 5416eeb72b29..f26b7b9f70a5 100644
--- a/sys/dev/e1000/e1000_ich8lan.h
+++ b/sys/dev/e1000/e1000_ich8lan.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -140,6 +140,38 @@
#define HV_TNCRS_UPPER PHY_REG(778, 29) /* Transmit with no CRS */
#define HV_TNCRS_LOWER PHY_REG(778, 30)
+#define E1000_FCRTV_PCH 0x05F40 /* PCH Flow Control Refresh Timer Value */
+
+#define E1000_NVM_K1_CONFIG 0x1B /* NVM K1 Config Word */
+#define E1000_NVM_K1_ENABLE 0x1 /* NVM Enable K1 bit */
+
+/* SMBus Address Phy Register */
+#define HV_SMB_ADDR PHY_REG(768, 26)
+#define HV_SMB_ADDR_PEC_EN 0x0200
+#define HV_SMB_ADDR_VALID 0x0080
+
+/* Strapping Option Register - RO */
+#define E1000_STRAP 0x0000C
+#define E1000_STRAP_SMBUS_ADDRESS_MASK 0x00FE0000
+#define E1000_STRAP_SMBUS_ADDRESS_SHIFT 17
+
+/* OEM Bits Phy Register */
+#define HV_OEM_BITS PHY_REG(768, 25)
+#define HV_OEM_BITS_LPLU 0x0004 /* Low Power Link Up */
+#define HV_OEM_BITS_GBE_DIS 0x0040 /* Gigabit Disable */
+#define HV_OEM_BITS_RESTART_AN 0x0400 /* Restart Auto-negotiation */
+
+#define LCD_CFG_PHY_ADDR_BIT 0x0020 /* Phy address bit from LCD Config word */
+
+/* KMRN Mode Control */
+#define HV_KMRN_MODE_CTRL PHY_REG(769, 16)
+#define HV_KMRN_MDIO_SLOW 0x0400
+
+/* PHY Power Management Control */
+#define HV_PM_CTRL PHY_REG(770, 17)
+
+#define SW_FLAG_TIMEOUT 1000 /* SW Semaphore flag timeout in milliseconds */
+
/*
* Additional interrupts need to be handled for ICH family:
* DSW = The FW changed the status of the DISSW bit in FWSM
@@ -163,12 +195,12 @@
#define E1000_RXDEXT_LINKSEC_ERROR_REPLAY_ERROR 0x40000000
#define E1000_RXDEXT_LINKSEC_ERROR_BAD_SIG 0x60000000
-
void e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
bool state);
void e1000_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw);
void e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw);
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
+s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_config);
s32 e1000_hv_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
-
#endif
diff --git a/sys/dev/e1000/e1000_mac.c b/sys/dev/e1000/e1000_mac.c
index db6e5f52e088..d4d2bec27bdc 100644
--- a/sys/dev/e1000/e1000_mac.c
+++ b/sys/dev/e1000/e1000_mac.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -78,7 +78,6 @@ void e1000_init_mac_ops_generic(struct e1000_hw *hw)
mac->ops.update_mc_addr_list = e1000_null_update_mc;
mac->ops.clear_vfta = e1000_null_mac_generic;
mac->ops.write_vfta = e1000_null_write_vfta;
- mac->ops.mta_set = e1000_null_mta_set;
mac->ops.rar_set = e1000_rar_set_generic;
mac->ops.validate_mdi_setting = e1000_validate_mdi_setting_generic;
}
@@ -144,16 +143,6 @@ void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b)
}
/**
- * e1000_null_set_mta - No-op function, return void
- * @hw: pointer to the HW structure
- **/
-void e1000_null_mta_set(struct e1000_hw *hw, u32 a)
-{
- DEBUGFUNC("e1000_null_mta_set");
- return;
-}
-
-/**
* e1000_null_rar_set - No-op function, return void
* @hw: pointer to the HW structure
**/
@@ -230,24 +219,36 @@ s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
struct e1000_bus_info *bus = &hw->bus;
-
s32 ret_val;
u16 pcie_link_status;
DEBUGFUNC("e1000_get_bus_info_pcie_generic");
bus->type = e1000_bus_type_pci_express;
- bus->speed = e1000_bus_speed_2500;
ret_val = e1000_read_pcie_cap_reg(hw,
PCIE_LINK_STATUS,
&pcie_link_status);
- if (ret_val)
+ if (ret_val) {
bus->width = e1000_bus_width_unknown;
- else
+ bus->speed = e1000_bus_speed_unknown;
+ } else {
+ switch (pcie_link_status & PCIE_LINK_SPEED_MASK) {
+ case PCIE_LINK_SPEED_2500:
+ bus->speed = e1000_bus_speed_2500;
+ break;
+ case PCIE_LINK_SPEED_5000:
+ bus->speed = e1000_bus_speed_5000;
+ break;
+ default:
+ bus->speed = e1000_bus_speed_unknown;
+ break;
+ }
+
bus->width = (enum e1000_bus_width)((pcie_link_status &
PCIE_LINK_WIDTH_MASK) >>
PCIE_LINK_WIDTH_SHIFT);
+ }
mac->ops.set_lan_id(hw);
@@ -408,6 +409,11 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
if (hw->bus.func == E1000_FUNC_1)
nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
+ if (hw->bus.func == E1000_FUNC_2)
+ nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN2;
+
+ if (hw->bus.func == E1000_FUNC_3)
+ nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN3;
for (i = 0; i < ETH_ADDR_LEN; i += 2) {
offset = nvm_alt_mac_addr_offset + (i >> 1);
ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
@@ -478,42 +484,6 @@ void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
}
/**
- * e1000_mta_set_generic - Set multicast filter table address
- * @hw: pointer to the HW structure
- * @hash_value: determines the MTA register and bit to set
- *
- * The multicast table address is a register array of 32-bit registers.
- * The hash_value is used to determine what register the bit is in, the
- * current value is read, the new bit is OR'd in and the new value is
- * written back into the register.
- **/
-void e1000_mta_set_generic(struct e1000_hw *hw, u32 hash_value)
-{
- u32 hash_bit, hash_reg, mta;
-
- DEBUGFUNC("e1000_mta_set_generic");
- /*
- * The MTA is a register array of 32-bit registers. It is
- * treated like an array of (32*mta_reg_count) bits. We want to
- * set bit BitArray[hash_value]. So we figure out what register
- * the bit is in, read it, OR in the new bit, then write
- * back the new value. The (hw->mac.mta_reg_count - 1) serves as a
- * mask to bits 31:5 of the hash value which gives us the
- * register we're modifying. The hash bit within that register
- * is determined by the lower 5 bits of the hash value.
- */
- hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
- hash_bit = hash_value & 0x1F;
-
- mta = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg);
-
- mta |= (1 << hash_bit);
-
- E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg, mta);
- E1000_WRITE_FLUSH(hw);
-}
-
-/**
* e1000_update_mc_addr_list_generic - Update Multicast addresses
* @hw: pointer to the HW structure
* @mc_addr_list: array of multicast addresses to program
@@ -556,8 +526,7 @@ void e1000_update_mc_addr_list_generic(struct e1000_hw *hw,
* @mc_addr: pointer to a multicast address
*
* Generates a multicast address hash value which is used to determine
- * the multicast filter table array address and new table value. See
- * e1000_mta_set_generic()
+ * the multicast filter table array address and new table value.
**/
u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr)
{
@@ -750,12 +719,6 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw)
mac->get_link_status = FALSE;
- if (hw->phy.type == e1000_phy_82578) {
- ret_val = e1000_link_stall_workaround_hv(hw);
- if (ret_val)
- goto out;
- }
-
/*
* Check if there was DownShift, must be checked
* immediately after link-up
@@ -776,7 +739,7 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw)
* of MAC speed/duplex configuration. So we only need to
* configure Collision Distance in the MAC.
*/
- e1000_config_collision_dist_generic(hw);
+ mac->ops.config_collision_dist(hw);
/*
* Configure Flow Control now that Auto-Neg has completed.
@@ -994,9 +957,8 @@ s32 e1000_setup_link_generic(struct e1000_hw *hw)
* In the case of the phy reset being blocked, we already have a link.
* We do not need to set it up again.
*/
- if (hw->phy.ops.check_reset_block)
- if (hw->phy.ops.check_reset_block(hw))
- goto out;
+ if (e1000_check_reset_block(hw))
+ goto out;
/*
* If requested flow control is set to default, set flow control
@@ -1050,6 +1012,7 @@ out:
**/
s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw)
{
+ struct e1000_mac_info *mac = &hw->mac;
u32 ctrl;
s32 ret_val = E1000_SUCCESS;
@@ -1060,7 +1023,7 @@ s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw)
/* Take the link out of reset */
ctrl &= ~E1000_CTRL_LRST;
- e1000_config_collision_dist_generic(hw);
+ mac->ops.config_collision_dist(hw);
ret_val = e1000_commit_fc_settings_generic(hw);
if (ret_val)
@@ -1100,8 +1063,7 @@ out:
* @hw: pointer to the HW structure
*
* Configures the collision distance to the default value and is used
- * during link setup. Currently no func pointer exists and all
- * implementations are handled in the generic version of this function.
+ * during link setup.
**/
void e1000_config_collision_dist_generic(struct e1000_hw *hw)
{
@@ -1155,7 +1117,7 @@ s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw)
* link up if we detect a signal. This will allow us to
* communicate with non-autonegotiating link partners.
*/
- ret_val = hw->mac.ops.check_for_link(hw);
+ ret_val = mac->ops.check_for_link(hw);
if (ret_val) {
DEBUGOUT("Error while checking for link\n");
goto out;
@@ -1212,7 +1174,7 @@ s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw)
* Rx Flow control is enabled and Tx Flow control is disabled
* by a software over-ride. Since there really isn't a way to
* advertise that we are capable of Rx Pause ONLY, we will
- * advertise that we support both symmetric and asymmetric RX
+ * advertise that we support both symmetric and asymmetric Rx
* PAUSE. Later, we will disable the adapter's ability to send
* PAUSE frames.
*/
@@ -1256,7 +1218,6 @@ out:
**/
s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw)
{
- s32 ret_val = E1000_SUCCESS;
u32 fcrtl = 0, fcrth = 0;
DEBUGFUNC("e1000_set_fc_watermarks_generic");
@@ -1283,7 +1244,7 @@ s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw)
E1000_WRITE_REG(hw, E1000_FCRTL, fcrtl);
E1000_WRITE_REG(hw, E1000_FCRTH, fcrth);
- return ret_val;
+ return E1000_SUCCESS;
}
/**
@@ -1512,7 +1473,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw)
/*
* Now we need to check if the user selected Rx ONLY
* of pause frames. In this case, we had to advertise
- * FULL flow control because we could not advertise RX
+ * FULL flow control because we could not advertise Rx
* ONLY. Hence, we must now check to see if we need to
* turn OFF the TRANSMISSION of PAUSE frames.
*/
@@ -1522,7 +1483,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw)
} else {
hw->fc.current_mode = e1000_fc_rx_pause;
DEBUGOUT("Flow Control = "
- "RX PAUSE frames only.\r\n");
+ "Rx PAUSE frames only.\r\n");
}
}
/*
@@ -1538,7 +1499,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw)
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc.current_mode = e1000_fc_tx_pause;
- DEBUGOUT("Flow Control = TX PAUSE frames only.\r\n");
+ DEBUGOUT("Flow Control = Tx PAUSE frames only.\r\n");
}
/*
* For transmitting PAUSE frames ONLY.
@@ -1553,7 +1514,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw)
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc.current_mode = e1000_fc_rx_pause;
- DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
+ DEBUGOUT("Flow Control = Rx PAUSE frames only.\r\n");
} else {
/*
* Per the IEEE spec, at this point flow control
@@ -1895,19 +1856,10 @@ out:
**/
s32 e1000_cleanup_led_generic(struct e1000_hw *hw)
{
- s32 ret_val = E1000_SUCCESS;
-
DEBUGFUNC("e1000_cleanup_led_generic");
- if (hw->mac.ops.cleanup_led != e1000_cleanup_led_generic) {
- ret_val = -E1000_ERR_CONFIG;
- goto out;
- }
-
E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default);
-
-out:
- return ret_val;
+ return E1000_SUCCESS;
}
/**
@@ -2033,7 +1985,7 @@ out:
* e1000_disable_pcie_master_generic - Disables PCI-express master access
* @hw: pointer to the HW structure
*
- * Returns 0 (E1000_SUCCESS) if successful, else returns -10
+ * Returns E1000_SUCCESS if successful, else returns -10
* (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not caused
* the master requests to be disabled.
*
@@ -2066,7 +2018,6 @@ s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw)
if (!timeout) {
DEBUGOUT("Master requests are pending.\n");
ret_val = -E1000_ERR_MASTER_REQUESTS_PENDING;
- goto out;
}
out:
@@ -2151,7 +2102,7 @@ out:
* Verify that when not using auto-negotiation that MDI/MDIx is correctly
* set, which is forced to MDI mode only.
**/
-s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw)
+static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
diff --git a/sys/dev/e1000/e1000_mac.h b/sys/dev/e1000/e1000_mac.h
index b7a5b2c5ecb0..348d660f9734 100644
--- a/sys/dev/e1000/e1000_mac.h
+++ b/sys/dev/e1000/e1000_mac.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -46,7 +46,6 @@ s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d);
bool e1000_null_mng_mode(struct e1000_hw *hw);
void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a);
void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b);
-void e1000_null_mta_set(struct e1000_hw *hw, u32 a);
void e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a);
s32 e1000_blink_led_generic(struct e1000_hw *hw);
s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw);
@@ -87,7 +86,6 @@ void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw);
void e1000_clear_vfta_generic(struct e1000_hw *hw);
void e1000_config_collision_dist_generic(struct e1000_hw *hw);
void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count);
-void e1000_mta_set_generic(struct e1000_hw *hw, u32 hash_value);
void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw);
void e1000_put_hw_semaphore_generic(struct e1000_hw *hw);
void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index);
diff --git a/sys/dev/e1000/e1000_manage.c b/sys/dev/e1000/e1000_manage.c
index b1f6541ad0c1..0b295f8d384c 100644
--- a/sys/dev/e1000/e1000_manage.c
+++ b/sys/dev/e1000/e1000_manage.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2008, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -74,10 +74,16 @@ s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw)
{
u32 hicr;
s32 ret_val = E1000_SUCCESS;
- u8 i;
+ u8 i;
DEBUGFUNC("e1000_mng_enable_host_if_generic");
+ if (!(hw->mac.arc_subsystem_valid)) {
+ DEBUGOUT("ARC subsystem not valid.\n");
+ ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND;
+ goto out;
+ }
+
/* Check that the host interface is enabled. */
hicr = E1000_READ_REG(hw, E1000_HICR);
if ((hicr & E1000_HICR_EN) == 0) {
@@ -112,18 +118,17 @@ out:
**/
bool e1000_check_mng_mode_generic(struct e1000_hw *hw)
{
- u32 fwsm;
+ u32 fwsm = E1000_READ_REG(hw, E1000_FWSM);
DEBUGFUNC("e1000_check_mng_mode_generic");
- fwsm = E1000_READ_REG(hw, E1000_FWSM);
return (fwsm & E1000_FWSM_MODE_MASK) ==
(E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT);
}
/**
- * e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on TX
+ * e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on Tx
* @hw: pointer to the HW structure
*
* Enables packet filtering on transmit packets if manageability is enabled
@@ -136,13 +141,14 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
u32 offset;
s32 ret_val, hdr_csum, csum;
u8 i, len;
- bool tx_filter = TRUE;
DEBUGFUNC("e1000_enable_tx_pkt_filtering_generic");
+ hw->mac.tx_pkt_filtering = TRUE;
+
/* No manageability, no filtering */
if (!hw->mac.ops.check_mng_mode(hw)) {
- tx_filter = FALSE;
+ hw->mac.tx_pkt_filtering = FALSE;
goto out;
}
@@ -152,18 +158,16 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
*/
ret_val = hw->mac.ops.mng_enable_host_if(hw);
if (ret_val != E1000_SUCCESS) {
- tx_filter = FALSE;
+ hw->mac.tx_pkt_filtering = FALSE;
goto out;
}
/* Read in the header. Length and offset are in dwords. */
len = E1000_MNG_DHCP_COOKIE_LENGTH >> 2;
offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2;
- for (i = 0; i < len; i++) {
- *(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw,
- E1000_HOST_IF,
+ for (i = 0; i < len; i++)
+ *(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
offset + i);
- }
hdr_csum = hdr->checksum;
hdr->checksum = 0;
csum = e1000_calculate_checksum((u8 *)hdr,
@@ -173,18 +177,19 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
* the cookie area isn't considered valid, in which case we
* take the safe route of assuming Tx filtering is enabled.
*/
- if (hdr_csum != csum)
- goto out;
- if (hdr->signature != E1000_IAMT_SIGNATURE)
+ if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) {
+ hw->mac.tx_pkt_filtering = TRUE;
goto out;
+ }
/* Cookie area is valid, make the final check for filtering. */
- if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING))
- tx_filter = FALSE;
+ if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) {
+ hw->mac.tx_pkt_filtering = FALSE;
+ goto out;
+ }
out:
- hw->mac.tx_pkt_filtering = tx_filter;
- return tx_filter;
+ return hw->mac.tx_pkt_filtering;
}
/**
@@ -344,10 +349,11 @@ out:
}
/**
- * e1000_enable_mng_pass_thru - Enable processing of ARP's
+ * e1000_enable_mng_pass_thru - Check if management passthrough is needed
* @hw: pointer to the HW structure
*
- * Verifies the hardware needs to allow ARPs to be processed by the host.
+ * Verifies the hardware needs to leave interface enabled so that frames can
+ * be directed to and from the management interface.
**/
bool e1000_enable_mng_pass_thru(struct e1000_hw *hw)
{
@@ -362,11 +368,10 @@ bool e1000_enable_mng_pass_thru(struct e1000_hw *hw)
manc = E1000_READ_REG(hw, E1000_MANC);
- if (!(manc & E1000_MANC_RCV_TCO_EN) ||
- !(manc & E1000_MANC_EN_MAC_ADDR_FILTER))
+ if (!(manc & E1000_MANC_RCV_TCO_EN))
goto out;
- if (hw->mac.arc_subsystem_valid) {
+ if (hw->mac.has_fwsm) {
fwsm = E1000_READ_REG(hw, E1000_FWSM);
factps = E1000_READ_REG(hw, E1000_FACTPS);
@@ -376,12 +381,23 @@ bool e1000_enable_mng_pass_thru(struct e1000_hw *hw)
ret_val = TRUE;
goto out;
}
- } else {
- if ((manc & E1000_MANC_SMBUS_EN) &&
- !(manc & E1000_MANC_ASF_EN)) {
+ } else if ((hw->mac.type == e1000_82574) ||
+ (hw->mac.type == e1000_82583)) {
+ u16 data;
+
+ factps = E1000_READ_REG(hw, E1000_FACTPS);
+ e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &data);
+
+ if (!(factps & E1000_FACTPS_MNGCG) &&
+ ((data & E1000_NVM_INIT_CTRL2_MNGM) ==
+ (e1000_mng_mode_pt << 13))) {
ret_val = TRUE;
goto out;
}
+ } else if ((manc & E1000_MANC_SMBUS_EN) &&
+ !(manc & E1000_MANC_ASF_EN)) {
+ ret_val = TRUE;
+ goto out;
}
out:
diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h
index b478f29fb0f7..ff505cc19ca1 100644
--- a/sys/dev/e1000/e1000_osdep.h
+++ b/sys/dev/e1000/e1000_osdep.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2008, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,8 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
@@ -57,10 +59,8 @@
#define ASSERT(x) if(!(x)) panic("EM: x")
-/* The happy-fun DELAY macro is defined in /usr/src/sys/i386/include/clock.h */
#define usec_delay(x) DELAY(x)
#define msec_delay(x) DELAY(1000*(x))
-/* TODO: Should we be paranoid about delaying in interrupt context? */
#define msec_delay_irq(x) DELAY(1000*(x))
#define MSGOUT(S, A, B) printf(S "\n", A, B)
@@ -73,16 +73,21 @@
#define STATIC static
#define FALSE 0
-#define false FALSE /* shared code stupidity */
+#define false FALSE
#define TRUE 1
#define true TRUE
#define CMD_MEM_WRT_INVALIDATE 0x0010 /* BIT_4 */
#define PCI_COMMAND_REGISTER PCIR_COMMAND
-/*
-** These typedefs are necessary due to the new
-** shared code, they are native to Linux.
-*/
+/* Mutex used in the shared code */
+#define E1000_MUTEX struct mtx
+#define E1000_MUTEX_INIT(mutex) mtx_init((mutex), #mutex, \
+ MTX_NETWORK_LOCK, MTX_DEF)
+#define E1000_MUTEX_DESTROY(mutex) mtx_destroy(mutex)
+#define E1000_MUTEX_LOCK(mutex) mtx_lock(mutex)
+#define E1000_MUTEX_TRYLOCK(mutex) mtx_trylock(mutex)
+#define E1000_MUTEX_UNLOCK(mutex) mtx_unlock(mutex)
+
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
@@ -97,6 +102,28 @@ typedef boolean_t bool;
#define __le32 u32
#define __le64 u64
+#if __FreeBSD_version < 800000 /* Now in HEAD */
+#if defined(__i386__) || defined(__amd64__)
+#define mb() __asm volatile("mfence" ::: "memory")
+#define wmb() __asm volatile("sfence" ::: "memory")
+#define rmb() __asm volatile("lfence" ::: "memory")
+#else
+#define mb()
+#define rmb()
+#define wmb()
+#endif
+#endif /*__FreeBSD_version < 800000 */
+
+#if defined(__i386__) || defined(__amd64__)
+static __inline
+void prefetch(void *x)
+{
+ __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x));
+}
+#else
+#define prefetch(x)
+#endif
+
struct e1000_osdep
{
bus_space_tag_t mem_bus_space_tag;
diff --git a/sys/dev/e1000/e1000_phy.c b/sys/dev/e1000/e1000_phy.c
index 513f2e67fb4a..24ca36f6ebb0 100644
--- a/sys/dev/e1000/e1000_phy.c
+++ b/sys/dev/e1000/e1000_phy.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -83,11 +83,13 @@ void e1000_init_phy_ops_generic(struct e1000_hw *hw)
phy->ops.get_cable_length = e1000_null_ops_generic;
phy->ops.get_info = e1000_null_ops_generic;
phy->ops.read_reg = e1000_null_read_reg;
+ phy->ops.read_reg_locked = e1000_null_read_reg;
phy->ops.release = e1000_null_phy_generic;
phy->ops.reset = e1000_null_ops_generic;
phy->ops.set_d0_lplu_state = e1000_null_lplu_state;
phy->ops.set_d3_lplu_state = e1000_null_lplu_state;
phy->ops.write_reg = e1000_null_write_reg;
+ phy->ops.write_reg_locked = e1000_null_write_reg;
phy->ops.power_up = e1000_null_phy_generic;
phy->ops.power_down = e1000_null_phy_generic;
phy->ops.cfg_on_link_up = e1000_null_ops_generic;
@@ -189,22 +191,9 @@ s32 e1000_get_phy_id(struct e1000_hw *hw)
if (phy->id != 0 && phy->id != PHY_REVISION_MASK)
goto out;
- /*
- * If the PHY ID is still unknown, we may have an 82577 without link.
- * We will try again after setting Slow MDIC mode. No harm in trying
- * again in this case since the PHY ID is unknown at this point anyway
- */
- ret_val = e1000_set_mdio_slow_mode_hv(hw, TRUE);
- if (ret_val)
- goto out;
-
retry_count++;
}
out:
- /* Revert to MDIO fast mode, if applicable */
- if (retry_count)
- ret_val = e1000_set_mdio_slow_mode_hv(hw, FALSE);
-
return ret_val;
}
@@ -250,6 +239,11 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
DEBUGFUNC("e1000_read_phy_reg_mdic");
+ if (offset > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", offset);
+ return -E1000_ERR_PARAM;
+ }
+
/*
* Set up Op-code, Phy Address, and register offset in the MDI
* Control register. The MAC will take care of interfacing with the
@@ -308,6 +302,11 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
DEBUGFUNC("e1000_write_phy_reg_mdic");
+ if (offset > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", offset);
+ return -E1000_ERR_PARAM;
+ }
+
/*
* Set up Op-code, Phy Address, and register offset in the MDI
* Control register. The MAC will take care of interfacing with the
@@ -351,6 +350,105 @@ out:
}
/**
+ * e1000_read_phy_reg_i2c - Read PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset using the i2c interface and stores the
+ * retrieved information in data.
+ **/
+s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u32 i, i2ccmd = 0;
+
+ DEBUGFUNC("e1000_read_phy_reg_i2c");
+
+ /*
+ * Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ (E1000_I2CCMD_OPCODE_READ));
+
+ E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ usec_delay(50);
+ i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ DEBUGOUT("I2CCMD Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ DEBUGOUT("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ /* Need to byte-swap the 16-bit value. */
+ *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+
+ return E1000_SUCCESS;
+}
+
+/**
+ * e1000_write_phy_reg_i2c - Write PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset using the i2c interface.
+ **/
+s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u32 i, i2ccmd = 0;
+ u16 phy_data_swapped;
+
+ DEBUGFUNC("e1000_write_phy_reg_i2c");
+
+ /* Swap the data bytes for the I2C interface */
+ phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
+
+ /*
+ * Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ E1000_I2CCMD_OPCODE_WRITE |
+ phy_data_swapped);
+
+ E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ usec_delay(50);
+ i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ DEBUGOUT("I2CCMD Write did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ DEBUGOUT("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/**
* e1000_read_phy_reg_m88 - Read m88 PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
@@ -414,111 +512,179 @@ out:
}
/**
- * e1000_read_phy_reg_igp - Read igp PHY register
+ * __e1000_read_phy_reg_igp - Read igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retrieved information in data. Release any acquired
+ * and stores the retrieved information in data. Release any acquired
* semaphores before exiting.
**/
-s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
s32 ret_val = E1000_SUCCESS;
- DEBUGFUNC("e1000_read_phy_reg_igp");
+ DEBUGFUNC("__e1000_read_phy_reg_igp");
- if (!(hw->phy.ops.acquire))
- goto out;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire))
+ goto out;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ }
if (offset > MAX_PHY_MULTI_PAGE_REG) {
ret_val = e1000_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(u16)offset);
- if (ret_val) {
- hw->phy.ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
}
ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
-
+release:
+ if (!locked)
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
/**
+ * e1000_read_phy_reg_igp - Read igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset and stores the
+ * retrieved information in data.
+ * Release the acquired semaphore before exiting.
+ **/
+s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_igp(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_read_phy_reg_igp_locked - Read igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset and stores the retrieved information
+ * in data. Assumes semaphore already acquired.
+ **/
+s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_igp(hw, offset, data, TRUE);
+}
+
+/**
* e1000_write_phy_reg_igp - Write igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then writes the data to PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
-s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
s32 ret_val = E1000_SUCCESS;
DEBUGFUNC("e1000_write_phy_reg_igp");
- if (!(hw->phy.ops.acquire))
- goto out;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire))
+ goto out;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ }
if (offset > MAX_PHY_MULTI_PAGE_REG) {
ret_val = e1000_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT,
(u16)offset);
- if (ret_val) {
- hw->phy.ops.release(hw);
- goto out;
- }
+ if (ret_val)
+ goto release;
}
ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
+release:
+ if (!locked)
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
/**
- * e1000_read_kmrn_reg_generic - Read kumeran register
+ * e1000_write_phy_reg_igp - Write igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to PHY register
+ * at the offset. Release any acquired semaphores before exiting.
+ **/
+s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_igp(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_write_phy_reg_igp_locked - Write igp PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_igp(hw, offset, data, TRUE);
+}
+
+/**
+ * __e1000_read_kmrn_reg - Read kumeran register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary. Then reads the PHY register at offset
* using the kumeran interface. The information retrieved is stored in data.
* Release any acquired semaphores before exiting.
**/
-s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
u32 kmrnctrlsta;
s32 ret_val = E1000_SUCCESS;
- DEBUGFUNC("e1000_read_kmrn_reg_generic");
+ DEBUGFUNC("__e1000_read_kmrn_reg");
- if (!(hw->phy.ops.acquire))
- goto out;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire))
+ goto out;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ }
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
@@ -529,48 +695,113 @@ s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data)
kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA);
*data = (u16)kmrnctrlsta;
- hw->phy.ops.release(hw);
+ if (!locked)
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
/**
- * e1000_write_kmrn_reg_generic - Write kumeran register
+ * e1000_read_kmrn_reg_generic - Read kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset using the
+ * kumeran interface. The information retrieved is stored in data.
+ * Release the acquired semaphore before exiting.
+ **/
+s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_kmrn_reg(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_read_kmrn_reg_locked - Read kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset using the kumeran interface. The
+ * information retrieved is stored in data.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_kmrn_reg(hw, offset, data, TRUE);
+}
+
+/**
+ * __e1000_write_kmrn_reg - Write kumeran register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary. Then write the data to PHY register
* at the offset using the kumeran interface. Release any acquired semaphores
* before exiting.
**/
-s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
u32 kmrnctrlsta;
s32 ret_val = E1000_SUCCESS;
DEBUGFUNC("e1000_write_kmrn_reg_generic");
- if (!(hw->phy.ops.acquire))
- goto out;
+ if (!locked) {
+ if (!(hw->phy.ops.acquire))
+ goto out;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ }
kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
E1000_KMRNCTRLSTA_OFFSET) | data;
E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
usec_delay(2);
- hw->phy.ops.release(hw);
+
+ if (!locked)
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
/**
+ * e1000_write_kmrn_reg_generic - Write kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to the PHY register at the offset
+ * using the kumeran interface. Release the acquired semaphore before exiting.
+ **/
+s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_kmrn_reg(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_write_kmrn_reg_locked - Write kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Write the data to PHY register at the offset using the kumeran interface.
+ * Assumes semaphore already acquired.
+ **/
+s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_kmrn_reg(hw, offset, data, TRUE);
+}
+
+/**
* e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link
* @hw: pointer to the HW structure
*
@@ -578,19 +809,26 @@ out:
**/
s32 e1000_copper_link_setup_82577(struct e1000_hw *hw)
{
- struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data;
DEBUGFUNC("e1000_copper_link_setup_82577");
- if (phy->reset_disable) {
+ if (hw->phy.reset_disable) {
ret_val = E1000_SUCCESS;
goto out;
}
+ if (hw->phy.type == e1000_phy_82580) {
+ ret_val = hw->phy.ops.reset(hw);
+ if (ret_val) {
+ DEBUGOUT("Error resetting the PHY.\n");
+ goto out;
+ }
+ }
+
/* Enable CRS on TX. This must be set for half-duplex operation. */
- ret_val = phy->ops.read_reg(hw, I82577_CFG_REG, &phy_data);
+ ret_val = hw->phy.ops.read_reg(hw, I82577_CFG_REG, &phy_data);
if (ret_val)
goto out;
@@ -599,16 +837,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw)
/* Enable downshift */
phy_data |= I82577_CFG_ENABLE_DOWNSHIFT;
- ret_val = phy->ops.write_reg(hw, I82577_CFG_REG, phy_data);
- if (ret_val)
- goto out;
-
- /* Set number of link attempts before downshift */
- ret_val = phy->ops.read_reg(hw, I82577_CTRL_REG, &phy_data);
- if (ret_val)
- goto out;
- phy_data &= ~I82577_CTRL_DOWNSHIFT_MASK;
- ret_val = phy->ops.write_reg(hw, I82577_CTRL_REG, phy_data);
+ ret_val = hw->phy.ops.write_reg(hw, I82577_CFG_REG, phy_data);
out:
return ret_val;
@@ -634,7 +863,7 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw)
goto out;
}
- /* Enable CRS on TX. This must be set for half-duplex operation. */
+ /* Enable CRS on Tx. This must be set for half-duplex operation. */
ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
goto out;
@@ -1326,18 +1555,22 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out;
if (!link) {
- /*
- * We didn't get link.
- * Reset the DSP and cross our fingers.
- */
- ret_val = phy->ops.write_reg(hw,
- M88E1000_PHY_PAGE_SELECT,
- 0x001d);
- if (ret_val)
- goto out;
- ret_val = e1000_phy_reset_dsp_generic(hw);
- if (ret_val)
- goto out;
+ if (hw->phy.type != e1000_phy_m88) {
+ DEBUGOUT("Link taking longer than expected.\n");
+ } else {
+ /*
+ * We didn't get link.
+ * Reset the DSP and cross our fingers.
+ */
+ ret_val = phy->ops.write_reg(hw,
+ M88E1000_PHY_PAGE_SELECT,
+ 0x001d);
+ if (ret_val)
+ goto out;
+ ret_val = e1000_phy_reset_dsp_generic(hw);
+ if (ret_val)
+ goto out;
+ }
}
/* Try once more */
@@ -1347,6 +1580,9 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw)
goto out;
}
+ if (hw->phy.type != e1000_phy_m88)
+ goto out;
+
ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
goto out;
@@ -1393,11 +1629,6 @@ s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw)
DEBUGFUNC("e1000_phy_force_speed_duplex_ife");
- if (phy->type != e1000_phy_ife) {
- ret_val = e1000_phy_force_speed_duplex_igp(hw);
- goto out;
- }
-
ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data);
if (ret_val)
goto out;
@@ -1625,12 +1856,11 @@ s32 e1000_check_downshift_generic(struct e1000_hw *hw)
case e1000_phy_gg82563:
case e1000_phy_bm:
case e1000_phy_82578:
- case e1000_phy_82577:
offset = M88E1000_PHY_SPEC_STATUS;
mask = M88E1000_PSSR_DOWNSHIFT;
break;
- case e1000_phy_igp_2:
case e1000_phy_igp:
+ case e1000_phy_igp_2:
case e1000_phy_igp_3:
offset = IGP01E1000_PHY_LINK_HEALTH;
mask = IGP01E1000_PLHR_SS_DOWNGRADE;
@@ -1825,16 +2055,14 @@ s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
* it across the board.
*/
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
- if (ret_val) {
+ if (ret_val)
/*
* If the first read fails, another entity may have
* ownership of the resources, wait and try again to
* see if they have relinquished the resources yet.
*/
usec_delay(usec_interval);
- ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
- &phy_status);
- }
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
if (ret_val)
break;
if (phy_status & MII_SR_LINK_STATUS)
@@ -1879,13 +2107,13 @@ s32 e1000_get_cable_length_m88(struct e1000_hw *hw)
index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
M88E1000_PSSR_CABLE_LENGTH_SHIFT;
- if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE + 1) {
- ret_val = E1000_ERR_PHY;
+ if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
+ ret_val = -E1000_ERR_PHY;
goto out;
}
phy->min_cable_length = e1000_m88_cable_length_table[index];
- phy->max_cable_length = e1000_m88_cable_length_table[index+1];
+ phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
@@ -1986,7 +2214,7 @@ s32 e1000_get_phy_info_m88(struct e1000_hw *hw)
DEBUGFUNC("e1000_get_phy_info_m88");
- if (hw->phy.media_type != e1000_media_type_copper) {
+ if (phy->media_type != e1000_media_type_copper) {
DEBUGOUT("Phy info is only valid for copper media\n");
ret_val = -E1000_ERR_CONFIG;
goto out;
@@ -2088,7 +2316,7 @@ s32 e1000_get_phy_info_igp(struct e1000_hw *hw)
if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
IGP01E1000_PSSR_SPEED_1000MBPS) {
- ret_val = hw->phy.ops.get_cable_length(hw);
+ ret_val = phy->ops.get_cable_length(hw);
if (ret_val)
goto out;
@@ -2114,6 +2342,63 @@ out:
}
/**
+ * e1000_get_phy_info_ife - Retrieves various IFE PHY states
+ * @hw: pointer to the HW structure
+ *
+ * Populates "phy" structure with various feature states.
+ **/
+s32 e1000_get_phy_info_ife(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 data;
+ bool link;
+
+ DEBUGFUNC("e1000_get_phy_info_ife");
+
+ ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link) {
+ DEBUGOUT("Phy info is only valid if link is up\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ ret_val = phy->ops.read_reg(hw, IFE_PHY_SPECIAL_CONTROL, &data);
+ if (ret_val)
+ goto out;
+ phy->polarity_correction = (data & IFE_PSC_AUTO_POLARITY_DISABLE)
+ ? FALSE : TRUE;
+
+ if (phy->polarity_correction) {
+ ret_val = e1000_check_polarity_ife(hw);
+ if (ret_val)
+ goto out;
+ } else {
+ /* Polarity is forced */
+ phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
+ ? e1000_rev_polarity_reversed
+ : e1000_rev_polarity_normal;
+ }
+
+ ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
+ if (ret_val)
+ goto out;
+
+ phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? TRUE : FALSE;
+
+ /* The following parameters are undefined for 10/100 operation. */
+ phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+ phy->local_rx = e1000_1000t_rx_status_undefined;
+ phy->remote_rx = e1000_1000t_rx_status_undefined;
+
+out:
+ return ret_val;
+}
+
+/**
* e1000_phy_sw_reset_generic - PHY software reset
* @hw: pointer to the HW structure
*
@@ -2302,7 +2587,7 @@ enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id)
{
enum e1000_phy_type phy_type = e1000_phy_unknown;
- switch (phy_id) {
+ switch (phy_id) {
case M88E1000_I_PHY_ID:
case M88E1000_E_PHY_ID:
case M88E1111_I_PHY_ID:
@@ -2333,6 +2618,9 @@ enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id)
case I82577_E_PHY_ID:
phy_type = e1000_phy_82577;
break;
+ case I82580_I_PHY_ID:
+ phy_type = e1000_phy_82580;
+ break;
default:
phy_type = e1000_phy_unknown;
break;
@@ -2416,6 +2704,10 @@ s32 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
DEBUGFUNC("e1000_write_phy_reg_bm");
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
@@ -2423,10 +2715,6 @@ s32 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2446,18 +2734,15 @@ s32 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
/* Page is shifted left, PHY expects (page x 32) */
ret_val = e1000_write_phy_reg_mdic(hw, page_select,
(page << page_shift));
- if (ret_val) {
- hw->phy.ops.release(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
-
out:
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2480,6 +2765,10 @@ s32 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
DEBUGFUNC("e1000_read_phy_reg_bm");
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
@@ -2487,10 +2776,6 @@ s32 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset);
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2510,17 +2795,14 @@ s32 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
/* Page is shifted left, PHY expects (page x 32) */
ret_val = e1000_write_phy_reg_mdic(hw, page_select,
(page << page_shift));
- if (ret_val) {
- hw->phy.ops.release(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
-
out:
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2541,6 +2823,10 @@ s32 e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
DEBUGFUNC("e1000_write_phy_reg_bm2");
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data,
@@ -2548,10 +2834,6 @@ s32 e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = 1;
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2560,17 +2842,14 @@ s32 e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
ret_val = e1000_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
page);
- if (ret_val) {
- hw->phy.ops.release(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
-
out:
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2590,6 +2869,10 @@ s32 e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
DEBUGFUNC("e1000_write_phy_reg_bm2");
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data,
@@ -2597,10 +2880,6 @@ s32 e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = 1;
if (offset > MAX_PHY_MULTI_PAGE_REG) {
@@ -2608,18 +2887,15 @@ s32 e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
ret_val = e1000_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT,
page);
- if (ret_val) {
- hw->phy.ops.release(hw);
+ if (ret_val)
goto out;
- }
}
ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release(hw);
-
out:
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2639,6 +2915,8 @@ out:
* 3) Write the address using the address opcode (0x11)
* 4) Read or write the data using the data opcode (0x12)
* 5) Restore 769_17.2 to its original value
+ *
+ * Assumes semaphore already acquired.
**/
static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
u16 *data, bool read)
@@ -2646,7 +2924,6 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
s32 ret_val;
u16 reg = BM_PHY_REG_NUM(offset);
u16 phy_reg = 0;
- u8 phy_acquired = 1;
DEBUGFUNC("e1000_access_phy_wakeup_reg_bm");
@@ -2655,13 +2932,6 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
(!(E1000_READ_REG(hw, E1000_PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
DEBUGOUT("Attempting to access page 800 while gig enabled.\n");
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val) {
- DEBUGOUT("Could not acquire PHY\n");
- phy_acquired = 0;
- goto out;
- }
-
/* All operations in this function are phy address 1 */
hw->phy.addr = 1;
@@ -2733,8 +3003,6 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
}
out:
- if (phy_acquired == 1)
- hw->phy.ops.release(hw);
return ret_val;
}
@@ -2775,62 +3043,30 @@ void e1000_power_down_phy_copper(struct e1000_hw *hw)
msec_delay(1);
}
-s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
-{
- s32 ret_val = E1000_SUCCESS;
- u16 data = 0;
-
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
-
- /* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
- hw->phy.addr = 1;
- ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release(hw);
- return ret_val;
- }
- ret_val = e1000_write_phy_reg_mdic(hw, BM_CS_CTRL1,
- (0x2180 | (slow << 10)));
-
- /* dummy read when reverting to fast mode - throw away result */
- if (!slow)
- e1000_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
-
- hw->phy.ops.release(hw);
-
- return ret_val;
-}
-
/**
- * e1000_read_phy_reg_hv - Read HV PHY register
+ * __e1000_read_phy_reg_hv - Read HV PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
* @data: pointer to the read data
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retrieved information in data. Release any acquired
+ * and stores the retrieved information in data. Release any acquired
* semaphore before exiting.
**/
-s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
+ bool locked)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
- bool in_slow_mode = FALSE;
- DEBUGFUNC("e1000_read_phy_reg_hv");
+ DEBUGFUNC("__e1000_read_phy_reg_hv");
- /* Workaround failure in MDIO access while cable is disconnected */
- if ((hw->phy.type == e1000_phy_82577) &&
- !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
- ret_val = e1000_set_mdio_slow_mode_hv(hw, TRUE);
+ if (!locked) {
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
- goto out;
-
- in_slow_mode = TRUE;
+ return ret_val;
}
/* Page 800 works differently than the rest so it has its own func */
@@ -2846,73 +3082,87 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (page == HV_INTC_FC_PAGE_START)
page = 0;
if (reg > MAX_PHY_MULTI_PAGE_REG) {
- if ((hw->phy.type != e1000_phy_82578) ||
- ((reg != I82578_ADDR_REG) &&
- (reg != I82578_ADDR_REG + 1))) {
- u32 phy_addr = hw->phy.addr;
+ u32 phy_addr = hw->phy.addr;
- hw->phy.addr = 1;
+ hw->phy.addr = 1;
- /* Page is shifted left, PHY expects (page x 32) */
- ret_val = e1000_write_phy_reg_mdic(hw,
- IGP01E1000_PHY_PAGE_SELECT,
- (page << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release(hw);
- goto out;
- }
- hw->phy.addr = phy_addr;
- }
+ /* Page is shifted left, PHY expects (page x 32) */
+ ret_val = e1000_write_phy_reg_mdic(hw,
+ IGP01E1000_PHY_PAGE_SELECT,
+ (page << IGP_PAGE_SHIFT));
+ hw->phy.addr = phy_addr;
+
+ if (ret_val)
+ goto out;
}
ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
- hw->phy.ops.release(hw);
-
out:
- /* Revert to MDIO fast mode, if applicable */
- if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
- ret_val = e1000_set_mdio_slow_mode_hv(hw, FALSE);
+ if (!locked)
+ hw->phy.ops.release(hw);
return ret_val;
}
/**
- * e1000_write_phy_reg_hv - Write HV PHY register
+ * e1000_read_phy_reg_hv - Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore then reads the PHY register at offset and stores
+ * the retrieved information in data. Release the acquired semaphore
+ * before exiting.
+ **/
+s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_hv(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_read_phy_reg_hv_locked - Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset and stores the retrieved information
+ * in data. Assumes semaphore already acquired.
+ **/
+s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ return __e1000_read_phy_reg_hv(hw, offset, data, TRUE);
+}
+
+/**
+ * __e1000_write_phy_reg_hv - Write HV PHY register
* @hw: pointer to the HW structure
* @offset: register offset to write to
* @data: data to write at register offset
+ * @locked: semaphore has already been acquired or not
*
* Acquires semaphore, if necessary, then writes the data to PHY register
* at the offset. Release any acquired semaphores before exiting.
**/
-s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
+static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
+ bool locked)
{
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
- bool in_slow_mode = FALSE;
- DEBUGFUNC("e1000_write_phy_reg_hv");
+ DEBUGFUNC("__e1000_write_phy_reg_hv");
- /* Workaround failure in MDIO access while cable is disconnected */
- if ((hw->phy.type == e1000_phy_82577) &&
- !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
- ret_val = e1000_set_mdio_slow_mode_hv(hw, TRUE);
+ if (!locked) {
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
- goto out;
-
- in_slow_mode = TRUE;
+ return ret_val;
}
/* Page 800 works differently than the rest so it has its own func */
@@ -2928,10 +3178,6 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
-
hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (page == HV_INTC_FC_PAGE_START)
@@ -2947,50 +3193,66 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
((MAX_PHY_REG_ADDRESS & reg) == 0) &&
(data & (1 << 11))) {
u16 data2 = 0x7EFF;
- hw->phy.ops.release(hw);
ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
&data2, FALSE);
if (ret_val)
goto out;
-
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- goto out;
}
if (reg > MAX_PHY_MULTI_PAGE_REG) {
- if ((hw->phy.type != e1000_phy_82578) ||
- ((reg != I82578_ADDR_REG) &&
- (reg != I82578_ADDR_REG + 1))) {
- u32 phy_addr = hw->phy.addr;
+ u32 phy_addr = hw->phy.addr;
- hw->phy.addr = 1;
+ hw->phy.addr = 1;
- /* Page is shifted left, PHY expects (page x 32) */
- ret_val = e1000_write_phy_reg_mdic(hw,
- IGP01E1000_PHY_PAGE_SELECT,
- (page << IGP_PAGE_SHIFT));
- if (ret_val) {
- hw->phy.ops.release(hw);
- goto out;
- }
- hw->phy.addr = phy_addr;
- }
+ /* Page is shifted left, PHY expects (page x 32) */
+ ret_val = e1000_write_phy_reg_mdic(hw,
+ IGP01E1000_PHY_PAGE_SELECT,
+ (page << IGP_PAGE_SHIFT));
+ hw->phy.addr = phy_addr;
+
+ if (ret_val)
+ goto out;
}
ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
- hw->phy.ops.release(hw);
out:
- /* Revert to MDIO fast mode, if applicable */
- if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
- ret_val = e1000_set_mdio_slow_mode_hv(hw, FALSE);
+ if (!locked)
+ hw->phy.ops.release(hw);
return ret_val;
}
/**
+ * e1000_write_phy_reg_hv - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore then writes the data to PHY register at the offset.
+ * Release the acquired semaphores before exiting.
+ **/
+s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_hv(hw, offset, data, FALSE);
+}
+
+/**
+ * e1000_write_phy_reg_hv_locked - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset. Assumes semaphore
+ * already acquired.
+ **/
+s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ return __e1000_write_phy_reg_hv(hw, offset, data, TRUE);
+}
+
+/**
* e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
* @page: page to be accessed
**/
@@ -3011,10 +3273,9 @@ static u32 e1000_get_phy_addr_for_hv_page(u32 page)
* @data: pointer to the data to be read or written
* @read: determines if operation is read or written
*
- * Acquires semaphore, if necessary, then reads the PHY register at offset
- * and storing the retreived information in data. Release any acquired
- * semaphores before exiting. Note that the procedure to read these regs
- * uses the address port and data port to read/write.
+ * Reads the PHY register at offset and stores the retreived information
+ * in data. Assumes semaphore already acquired. Note that the procedure
+ * to read these regs uses the address port and data port to read/write.
**/
static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
u16 *data, bool read)
@@ -3022,7 +3283,6 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
s32 ret_val;
u32 addr_reg = 0;
u32 data_reg = 0;
- u8 phy_acquired = 1;
DEBUGFUNC("e1000_access_phy_debug_regs_hv");
@@ -3031,13 +3291,6 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
I82578_ADDR_REG : I82577_ADDR_REG;
data_reg = addr_reg + 1;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val) {
- DEBUGOUT("Could not acquire PHY\n");
- phy_acquired = 0;
- goto out;
- }
-
/* All operations in this function are phy address 2 */
hw->phy.addr = 2;
@@ -3060,8 +3313,6 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
}
out:
- if (phy_acquired == 1)
- hw->phy.ops.release(hw);
return ret_val;
}
@@ -3090,7 +3341,7 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw)
hw->phy.ops.read_reg(hw, PHY_CONTROL, &data);
if (data & PHY_CONTROL_LB)
goto out;
-
+
/* check if link is up and at 1Gbps */
ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data);
if (ret_val)
@@ -3151,9 +3402,7 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw)
* e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY
* @hw: pointer to the HW structure
*
- * Calls the PHY setup function to force speed and duplex. Clears the
- * auto-crossover to force MDI manually. Waits for link and returns
- * successful if link up is successful, else -E1000_ERR_PHY (-2).
+ * Calls the PHY setup function to force speed and duplex.
**/
s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
{
@@ -3174,23 +3423,6 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
if (ret_val)
goto out;
- /*
- * Clear Auto-Crossover to force MDI manually. 82577 requires MDI
- * forced whenever speed and duplex are forced.
- */
- ret_val = phy->ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data);
- if (ret_val)
- goto out;
-
- phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX;
- phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX;
-
- ret_val = phy->ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data);
- if (ret_val)
- goto out;
-
- DEBUGOUT1("I82577_PHY_CTRL_2: %X\n", phy_data);
-
usec_delay(1);
if (phy->autoneg_wait_to_complete) {
@@ -3309,7 +3541,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw)
I82577_DSTATUS_CABLE_LENGTH_SHIFT;
if (length == E1000_CABLE_LENGTH_UNDEFINED)
- ret_val = E1000_ERR_PHY;
+ ret_val = -E1000_ERR_PHY;
phy->cable_length = length;
diff --git a/sys/dev/e1000/e1000_phy.h b/sys/dev/e1000/e1000_phy.h
index 28ed0c15852f..692cbaa91eb9 100644
--- a/sys/dev/e1000/e1000_phy.h
+++ b/sys/dev/e1000/e1000_phy.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -45,6 +45,7 @@ s32 e1000_check_polarity_m88(struct e1000_hw *hw);
s32 e1000_check_polarity_igp(struct e1000_hw *hw);
s32 e1000_check_polarity_ife(struct e1000_hw *hw);
s32 e1000_check_reset_block_generic(struct e1000_hw *hw);
+s32 e1000_phy_setup_autoneg(struct e1000_hw *hw);
s32 e1000_copper_link_autoneg(struct e1000_hw *hw);
s32 e1000_copper_link_setup_igp(struct e1000_hw *hw);
s32 e1000_copper_link_setup_m88(struct e1000_hw *hw);
@@ -57,19 +58,23 @@ s32 e1000_get_cfg_done_generic(struct e1000_hw *hw);
s32 e1000_get_phy_id(struct e1000_hw *hw);
s32 e1000_get_phy_info_igp(struct e1000_hw *hw);
s32 e1000_get_phy_info_m88(struct e1000_hw *hw);
+s32 e1000_get_phy_info_ife(struct e1000_hw *hw);
s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw);
void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl);
s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw);
s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw);
-s32 e1000_phy_setup_autoneg(struct e1000_hw *hw);
s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active);
s32 e1000_setup_copper_link_generic(struct e1000_hw *hw);
s32 e1000_wait_autoneg_generic(struct e1000_hw *hw);
s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data);
+s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data);
+s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_phy_reset_dsp(struct e1000_hw *hw);
s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
@@ -85,9 +90,12 @@ void e1000_power_up_phy_copper(struct e1000_hw *hw);
void e1000_power_down_phy_copper(struct e1000_hw *hw);
s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
+s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data);
-s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow);
+s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw);
s32 e1000_copper_link_setup_82577(struct e1000_hw *hw);
s32 e1000_check_polarity_82577(struct e1000_hw *hw);
@@ -140,7 +148,6 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
#define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15)
#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */
#define I82577_CTRL_REG 23
-#define I82577_CTRL_DOWNSHIFT_MASK (7 << 10)
/* 82577 specific PHY registers */
#define I82577_PHY_CTRL_2 18
@@ -175,6 +182,13 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
#define BM_CS_STATUS_SPEED_MASK 0xC000
#define BM_CS_STATUS_SPEED_1000 0x8000
+/* 82577 Mobile Phy Status Register */
+#define HV_M_STATUS 26
+#define HV_M_STATUS_AUTONEG_COMPLETE 0x1000
+#define HV_M_STATUS_SPEED_MASK 0x0300
+#define HV_M_STATUS_SPEED_1000 0x0200
+#define HV_M_STATUS_LINK_UP 0x0040
+
#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4
#define IGP01E1000_PHY_POLARITY_MASK 0x0078
@@ -220,6 +234,8 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */
#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */
#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */
+#define E1000_KMRNCTRLSTA_K1_CONFIG 0x7
+#define E1000_KMRNCTRLSTA_K1_ENABLE 0x0002
#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10
#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Control */
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index 3a62d0a61a3a..b2a477e82345 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -43,6 +43,12 @@
#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */
#define E1000_FLA 0x0001C /* Flash Access - RW */
#define E1000_MDIC 0x00020 /* MDI Control - RW */
+#define E1000_MDICNFG 0x00E04 /* MDI Config - RW */
+#define E1000_REGISTER_SET_SIZE 0x20000 /* CSR Size */
+#define E1000_EEPROM_INIT_CTRL_WORD_2 0x0F /* EEPROM Init Ctrl Word 2 */
+#define E1000_BARCTRL 0x5BBC /* BAR ctrl reg */
+#define E1000_BARCTRL_FLSIZE 0x0700 /* BAR ctrl Flsize */
+#define E1000_BARCTRL_CSRSIZE 0x2000 /* BAR ctrl CSR size */
#define E1000_SCTL 0x00024 /* SerDes Control - RW */
#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
@@ -59,7 +65,7 @@
#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */
#define E1000_IVAR 0x000E4 /* Interrupt Vector Allocation Register - RW */
#define E1000_SVCR 0x000F0
-#define E1000_SVT 0x000F4
+#define E1000_SVT 0x000F4
#define E1000_RCTL 0x00100 /* Rx Control - RW */
#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */
#define E1000_TXCW 0x00178 /* Tx Configuration Word - RW */
@@ -121,11 +127,7 @@
#define E1000_RDPUCTL 0x025DC /* DMA Rx Descriptor uC Control - RW */
#define E1000_PBDIAG 0x02458 /* Packet Buffer Diagnostic - RW */
#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
-#define E1000_RXCTL(_n) (0x0C014 + (0x40 * (_n)))
-#define E1000_RQDPC(_n) (0x0C030 + (0x40 * (_n)))
-#define E1000_TXCTL(_n) (0x0E014 + (0x40 * (_n)))
-#define E1000_RXCTL(_n) (0x0C014 + (0x40 * (_n)))
-#define E1000_RQDPC(_n) (0x0C030 + (0x40 * (_n)))
+#define E1000_IRPBS 0x02404 /* Same as RXPBS, renamed for newer adapters - RW */
#define E1000_RDTR 0x02820 /* Rx Delay Timer - RW */
#define E1000_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer - RW */
/*
@@ -146,10 +148,15 @@
(0x0C00C + ((_n) * 0x40)))
#define E1000_RDH(_n) ((_n) < 4 ? (0x02810 + ((_n) * 0x100)) : \
(0x0C010 + ((_n) * 0x40)))
+#define E1000_RXCTL(_n) ((_n) < 4 ? (0x02814 + ((_n) * 0x100)) : \
+ (0x0C014 + ((_n) * 0x40)))
+#define E1000_DCA_RXCTRL(_n) E1000_RXCTL(_n)
#define E1000_RDT(_n) ((_n) < 4 ? (0x02818 + ((_n) * 0x100)) : \
(0x0C018 + ((_n) * 0x40)))
#define E1000_RXDCTL(_n) ((_n) < 4 ? (0x02828 + ((_n) * 0x100)) : \
(0x0C028 + ((_n) * 0x40)))
+#define E1000_RQDPC(_n) ((_n) < 4 ? (0x02830 + ((_n) * 0x100)) : \
+ (0x0C030 + ((_n) * 0x40)))
#define E1000_TDBAL(_n) ((_n) < 4 ? (0x03800 + ((_n) * 0x100)) : \
(0x0E000 + ((_n) * 0x40)))
#define E1000_TDBAH(_n) ((_n) < 4 ? (0x03804 + ((_n) * 0x100)) : \
@@ -158,17 +165,18 @@
(0x0E008 + ((_n) * 0x40)))
#define E1000_TDH(_n) ((_n) < 4 ? (0x03810 + ((_n) * 0x100)) : \
(0x0E010 + ((_n) * 0x40)))
+#define E1000_TXCTL(_n) ((_n) < 4 ? (0x03814 + ((_n) * 0x100)) : \
+ (0x0E014 + ((_n) * 0x40)))
+#define E1000_DCA_TXCTRL(_n) E1000_TXCTL(_n)
#define E1000_TDT(_n) ((_n) < 4 ? (0x03818 + ((_n) * 0x100)) : \
(0x0E018 + ((_n) * 0x40)))
#define E1000_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) : \
(0x0E028 + ((_n) * 0x40)))
-#define E1000_TARC(_n) (0x03840 + (_n << 8))
-#define E1000_DCA_TXCTRL(_n) (0x03814 + (_n << 8))
-#define E1000_DCA_RXCTRL(_n) (0x02814 + (_n << 8))
#define E1000_TDWBAL(_n) ((_n) < 4 ? (0x03838 + ((_n) * 0x100)) : \
(0x0E038 + ((_n) * 0x40)))
#define E1000_TDWBAH(_n) ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) : \
(0x0E03C + ((_n) * 0x40)))
+#define E1000_TARC(_n) (0x03840 + ((_n) * 0x100))
#define E1000_RSRPD 0x02C00 /* Rx Small Packet Detect - RW */
#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
#define E1000_TXDMAC 0x03000 /* Tx DMA Control - RW */
@@ -187,6 +195,7 @@
#define E1000_PBSLAC 0x03100 /* Packet Buffer Slave Access Control */
#define E1000_PBSLAD(_n) (0x03110 + (0x4 * (_n))) /* Packet Buffer DWORD (_n) */
#define E1000_TXPBS 0x03404 /* Tx Packet Buffer Size - RW */
+#define E1000_ITPBS 0x03404 /* Same as TXPBS, renamed for newer adpaters - RW */
#define E1000_TDFH 0x03410 /* Tx Data FIFO Head - RW */
#define E1000_TDFT 0x03418 /* Tx Data FIFO Tail - RW */
#define E1000_TDFHS 0x03420 /* Tx Data FIFO Head Saved - RW */
@@ -271,6 +280,18 @@
#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Min Thresh Count */
#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Desc Min Thresh Count */
#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */
+#define E1000_CRC_OFFSET 0x05F50 /* CRC Offset register */
+
+/* Virtualization statistical counters */
+#define E1000_PFVFGPRC(_n) (0x010010 + (0x100 * (_n)))
+#define E1000_PFVFGPTC(_n) (0x010014 + (0x100 * (_n)))
+#define E1000_PFVFGORC(_n) (0x010018 + (0x100 * (_n)))
+#define E1000_PFVFGOTC(_n) (0x010034 + (0x100 * (_n)))
+#define E1000_PFVFMPRC(_n) (0x010038 + (0x100 * (_n)))
+#define E1000_PFVFGPRLBC(_n) (0x010040 + (0x100 * (_n)))
+#define E1000_PFVFGPTLBC(_n) (0x010044 + (0x100 * (_n)))
+#define E1000_PFVFGORLBC(_n) (0x010048 + (0x100 * (_n)))
+#define E1000_PFVFGOTLBC(_n) (0x010050 + (0x100 * (_n)))
#define E1000_LSECTXUT 0x04300 /* LinkSec Tx Untagged Packet Count - OutPktsUntagged */
#define E1000_LSECTXPKTE 0x04304 /* LinkSec Encrypted Tx Packets Count - OutPktsEncrypted */
@@ -376,6 +397,7 @@
#define E1000_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */
#define E1000_MDPHYA 0x0003C /* PHY address - RW */
#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */
+#define E1000_MDEF(_n) (0x05890 + (4 * (_n))) /* Mngmt Decision Filters */
#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */
#define E1000_CCMCTL 0x05B48 /* CCM Control Register */
#define E1000_GIOCTL 0x05B44 /* GIO Analog Control Register */
@@ -392,6 +414,7 @@
#define E1000_SWSM2 0x05B58 /* Driver-only SW semaphore (not used by BOOT agents) */
#define E1000_DCA_ID 0x05B70 /* DCA Requester ID Information - RO */
#define E1000_DCA_CTRL 0x05B74 /* DCA Control - RW */
+#define E1000_UFUSE 0x05B78 /* UFUSE - RO */
#define E1000_FFLT_DBG 0x05F04 /* Debug Register */
#define E1000_HICR 0x08F00 /* Host Interface Control */
@@ -437,6 +460,7 @@
#define E1000_VMOLR(_n) (0x05AD0 + (4 * (_n)))
#define E1000_VLVF(_n) (0x05D00 + (4 * (_n))) /* VLAN Virtual Machine
* Filter - RW */
+#define E1000_VMVIR(_n) (0x03700 + (4 * (_n)))
/* Time Sync */
#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */
#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */
@@ -450,6 +474,8 @@
#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */
#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */
#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */
+#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
+#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */
#define E1000_RXMTRL 0x0B634 /* Time sync Rx EtherType and Msg Type - RW */
#define E1000_RXUDP 0x0B638 /* Time Sync Rx UDP Port - RW */
@@ -493,4 +519,15 @@
#define E1000_RTTBCNACH 0x0B214 /* Tx BCN Control High */
#define E1000_RTTBCNACL 0x0B210 /* Tx BCN Control Low */
+/* DMA Coalescing registers */
+#define E1000_DMACR 0x02508 /* Control Register */
+#define E1000_DMCTXTH 0x03550 /* Transmit Threshold */
+#define E1000_DMCTLX 0x02514 /* Time to Lx Request */
+#define E1000_DMCRTRH 0x05DD0 /* Receive Packet Rate Threshold */
+#define E1000_DMCCNT 0x05DD4 /* Current RX Count */
+#define E1000_FCRTC 0x02170 /* Flow Control Rx high watermark */
+#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
+
+/* PCIe Parity Status Register */
+#define E1000_PCIEERRSTS 0x05BA8
#endif
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 96d0788c377e..f8329f89663c 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -54,9 +54,7 @@
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
-#if __FreeBSD_version >= 700029
#include <sys/eventhandler.h>
-#endif
#include <machine/bus.h>
#include <machine/resource.h>
@@ -79,6 +77,7 @@
#include <netinet/udp.h>
#include <machine/in_cksum.h>
+#include <dev/led/led.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
@@ -94,7 +93,7 @@ int em_display_debug_stats = 0;
/*********************************************************************
* Driver version:
*********************************************************************/
-char em_driver_version[] = "6.9.14";
+char em_driver_version[] = "7.0.0";
/*********************************************************************
@@ -110,51 +109,6 @@ char em_driver_version[] = "6.9.14";
static em_vendor_info_t em_vendor_info_array[] =
{
/* Intel(R) PRO/1000 Network Connection */
- { 0x8086, E1000_DEV_ID_82540EM, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82540EM_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82540EP, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82540EP_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82540EP_LP, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82541EI, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541ER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541ER_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541GI, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541GI_LF, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82541GI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82542, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82543GC_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82543GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82544EI_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82544EI_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82544GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82544GC_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82545EM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82545EM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82545GM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82545GM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82545GM_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82546EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546EB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546EB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_PCIE, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3,
- PCI_ANY_ID, PCI_ANY_ID, 0},
-
- { 0x8086, E1000_DEV_ID_82547EI, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82547EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
- { 0x8086, E1000_DEV_ID_82547GI, PCI_ANY_ID, PCI_ANY_ID, 0},
-
{ 0x8086, E1000_DEV_ID_82571EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82571EB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82571EB_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
@@ -194,7 +148,7 @@ static em_vendor_info_t em_vendor_info_array[] =
{ 0x8086, E1000_DEV_ID_ICH8_IFE_GT, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH8_IFE_G, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH8_IGP_M, PCI_ANY_ID, PCI_ANY_ID, 0},
-
+ { 0x8086, E1000_DEV_ID_ICH8_82567V_3, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH9_IGP_M_AMT, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH9_IGP_AMT, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH9_IGP_C, PCI_ANY_ID, PCI_ANY_ID, 0},
@@ -211,6 +165,10 @@ static em_vendor_info_t em_vendor_info_array[] =
{ 0x8086, E1000_DEV_ID_ICH10_R_BM_V, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH10_D_BM_LM, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_ICH10_D_BM_LF, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_PCH_M_HV_LM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_PCH_M_HV_LC, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_PCH_D_HV_DM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_PCH_D_HV_DC, PCI_ANY_ID, PCI_ANY_ID, 0},
/* required last entry */
{ 0, 0, 0, 0, 0}
};
@@ -233,14 +191,14 @@ static int em_shutdown(device_t);
static int em_suspend(device_t);
static int em_resume(device_t);
static void em_start(struct ifnet *);
-static void em_start_locked(struct ifnet *ifp);
+static void em_start_locked(struct ifnet *, struct tx_ring *);
#if __FreeBSD_version >= 800000
static int em_mq_start(struct ifnet *, struct mbuf *);
-static int em_mq_start_locked(struct ifnet *, struct mbuf *);
+static int em_mq_start_locked(struct ifnet *,
+ struct tx_ring *, struct mbuf *);
static void em_qflush(struct ifnet *);
#endif
static int em_ioctl(struct ifnet *, u_long, caddr_t);
-static void em_watchdog(struct adapter *);
static void em_init(void *);
static void em_init_locked(struct adapter *);
static void em_stop(void *);
@@ -248,55 +206,49 @@ static void em_media_status(struct ifnet *, struct ifmediareq *);
static int em_media_change(struct ifnet *);
static void em_identify_hardware(struct adapter *);
static int em_allocate_pci_resources(struct adapter *);
-static int em_allocate_legacy(struct adapter *adapter);
-static int em_allocate_msix(struct adapter *adapter);
+static int em_allocate_legacy(struct adapter *);
+static int em_allocate_msix(struct adapter *);
+static int em_allocate_queues(struct adapter *);
static int em_setup_msix(struct adapter *);
static void em_free_pci_resources(struct adapter *);
static void em_local_timer(void *);
-static int em_hardware_init(struct adapter *);
+static void em_reset(struct adapter *);
static void em_setup_interface(device_t, struct adapter *);
+
static void em_setup_transmit_structures(struct adapter *);
static void em_initialize_transmit_unit(struct adapter *);
+static int em_allocate_transmit_buffers(struct tx_ring *);
+static void em_free_transmit_structures(struct adapter *);
+static void em_free_transmit_buffers(struct tx_ring *);
+
static int em_setup_receive_structures(struct adapter *);
+static int em_allocate_receive_buffers(struct rx_ring *);
static void em_initialize_receive_unit(struct adapter *);
+static void em_free_receive_structures(struct adapter *);
+static void em_free_receive_buffers(struct rx_ring *);
+
static void em_enable_intr(struct adapter *);
static void em_disable_intr(struct adapter *);
-static void em_free_transmit_structures(struct adapter *);
-static void em_free_receive_structures(struct adapter *);
static void em_update_stats_counters(struct adapter *);
-static void em_txeof(struct adapter *);
-static void em_tx_purge(struct adapter *);
-static int em_allocate_receive_structures(struct adapter *);
-static int em_allocate_transmit_structures(struct adapter *);
-static int em_rxeof(struct adapter *, int);
+static bool em_txeof(struct tx_ring *);
+static int em_rxeof(struct rx_ring *, int);
#ifndef __NO_STRICT_ALIGNMENT
-static int em_fixup_rx(struct adapter *);
+static int em_fixup_rx(struct rx_ring *);
#endif
-static void em_receive_checksum(struct adapter *, struct e1000_rx_desc *,
- struct mbuf *);
-static void em_transmit_checksum_setup(struct adapter *, struct mbuf *,
+static void em_receive_checksum(struct e1000_rx_desc *, struct mbuf *);
+static void em_transmit_checksum_setup(struct tx_ring *, struct mbuf *,
u32 *, u32 *);
-#if __FreeBSD_version >= 700000
-static bool em_tso_setup(struct adapter *, struct mbuf *,
- u32 *, u32 *);
-#endif /* FreeBSD_version >= 700000 */
+static bool em_tso_setup(struct tx_ring *, struct mbuf *, u32 *, u32 *);
static void em_set_promisc(struct adapter *);
static void em_disable_promisc(struct adapter *);
static void em_set_multi(struct adapter *);
static void em_print_hw_stats(struct adapter *);
static void em_update_link_status(struct adapter *);
-static int em_get_buf(struct adapter *, int);
-#if __FreeBSD_version >= 700029
+static void em_refresh_mbufs(struct rx_ring *, int);
static void em_register_vlan(void *, struct ifnet *, u16);
static void em_unregister_vlan(void *, struct ifnet *, u16);
static void em_setup_vlan_hw_support(struct adapter *);
-#endif
-static int em_xmit(struct adapter *, struct mbuf **);
-static void em_smartspeed(struct adapter *);
-static int em_82547_fifo_workaround(struct adapter *, int);
-static void em_82547_update_fifo_head(struct adapter *, int);
-static int em_82547_tx_fifo_reset(struct adapter *);
-static void em_82547_move_tail(void *);
+static int em_xmit(struct tx_ring *, struct mbuf **);
static int em_dma_malloc(struct adapter *, bus_size_t,
struct em_dma_alloc *, int);
static void em_dma_free(struct adapter *, struct em_dma_alloc *);
@@ -305,8 +257,6 @@ static void em_print_nvm_info(struct adapter *);
static int em_is_valid_ether_addr(u8 *);
static int em_sysctl_stats(SYSCTL_HANDLER_ARGS);
static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS);
-static u32 em_fill_descriptors (bus_addr_t address, u32 length,
- PDESC_ARRAY desc_array);
static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS);
static void em_add_int_delay_sysctl(struct adapter *, const char *,
const char *, struct em_int_delay_info *, int, int);
@@ -315,29 +265,23 @@ static void em_init_manageability(struct adapter *);
static void em_release_manageability(struct adapter *);
static void em_get_hw_control(struct adapter *);
static void em_release_hw_control(struct adapter *);
+static void em_get_wakeup(device_t);
static void em_enable_wakeup(device_t);
+static int em_enable_phy_wakeup(struct adapter *);
+static void em_led_func(void *, int);
-#ifdef EM_LEGACY_IRQ
-static void em_intr(void *);
-#else /* FAST IRQ */
-#if __FreeBSD_version < 700000
-static void em_irq_fast(void *);
-#else
static int em_irq_fast(void *);
-#endif
/* MSIX handlers */
static void em_msix_tx(void *);
static void em_msix_rx(void *);
static void em_msix_link(void *);
-static void em_handle_rx(void *context, int pending);
static void em_handle_tx(void *context, int pending);
-
-static void em_handle_rxtx(void *context, int pending);
+static void em_handle_rx(void *context, int pending);
static void em_handle_link(void *context, int pending);
+
static void em_add_rx_process_limit(struct adapter *, const char *,
const char *, int *, int);
-#endif /* ~EM_LEGACY_IRQ */
#ifdef DEVICE_POLLING
static poll_handler_t em_poll;
@@ -362,7 +306,7 @@ static driver_t em_driver = {
"em", em_methods, sizeof(struct adapter),
};
-static devclass_t em_devclass;
+devclass_t em_devclass;
DRIVER_MODULE(em, pci, em_driver, em_devclass, 0, 0);
MODULE_DEPEND(em, pci, 1, 1, 1);
MODULE_DEPEND(em, ether, 1, 1, 1);
@@ -382,31 +326,35 @@ MODULE_DEPEND(em, ether, 1, 1, 1);
static int em_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV);
static int em_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR);
-static int em_tx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_TADV);
-static int em_rx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_RADV);
-static int em_rxd = EM_DEFAULT_RXD;
-static int em_txd = EM_DEFAULT_TXD;
-static int em_smart_pwr_down = FALSE;
-/* Controls whether promiscuous also shows bad packets */
-static int em_debug_sbp = FALSE;
-/* Local switch for MSI/MSIX */
-static int em_enable_msi = TRUE;
-
TUNABLE_INT("hw.em.tx_int_delay", &em_tx_int_delay_dflt);
TUNABLE_INT("hw.em.rx_int_delay", &em_rx_int_delay_dflt);
+
+static int em_tx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_TADV);
+static int em_rx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_RADV);
TUNABLE_INT("hw.em.tx_abs_int_delay", &em_tx_abs_int_delay_dflt);
TUNABLE_INT("hw.em.rx_abs_int_delay", &em_rx_abs_int_delay_dflt);
+
+static int em_rxd = EM_DEFAULT_RXD;
+static int em_txd = EM_DEFAULT_TXD;
TUNABLE_INT("hw.em.rxd", &em_rxd);
TUNABLE_INT("hw.em.txd", &em_txd);
+
+static int em_smart_pwr_down = FALSE;
TUNABLE_INT("hw.em.smart_pwr_down", &em_smart_pwr_down);
+
+/* Controls whether promiscuous also shows bad packets */
+static int em_debug_sbp = FALSE;
TUNABLE_INT("hw.em.sbp", &em_debug_sbp);
-TUNABLE_INT("hw.em.enable_msi", &em_enable_msi);
-#ifndef EM_LEGACY_IRQ
+/* Local controls for MSI/MSIX */
+static int em_enable_msix = TRUE;
+static int em_msix_queues = 2; /* for 82574, can be 1 or 2 */
+TUNABLE_INT("hw.em.enable_msix", &em_enable_msix);
+TUNABLE_INT("hw.em.msix_queues", &em_msix_queues);
+
/* How many packets rxeof tries to clean at a time */
static int em_rx_process_limit = 100;
TUNABLE_INT("hw.em.rx_process_limit", &em_rx_process_limit);
-#endif
/* Flow control setting - default to FULL */
static int em_fc_setting = e1000_fc_full;
@@ -488,17 +436,13 @@ static int
em_attach(device_t dev)
{
struct adapter *adapter;
- int tsize, rsize;
int error = 0;
- u16 eeprom_data, device_id;
INIT_DEBUGOUT("em_attach: begin");
adapter = device_get_softc(dev);
adapter->dev = adapter->osdep.dev = dev;
EM_CORE_LOCK_INIT(adapter, device_get_nameunit(dev));
- EM_TX_LOCK_INIT(adapter, device_get_nameunit(dev));
- EM_RX_LOCK_INIT(adapter, device_get_nameunit(dev));
/* SYSCTL stuff */
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
@@ -512,7 +456,6 @@ em_attach(device_t dev)
em_sysctl_stats, "I", "Statistics");
callout_init_mtx(&adapter->timer, &adapter->core_mtx, 0);
- callout_init_mtx(&adapter->tx_fifo_timer, &adapter->tx_mtx, 0);
/* Determine hardware and mac info */
em_identify_hardware(adapter);
@@ -531,6 +474,7 @@ em_attach(device_t dev)
** identified
*/
if ((adapter->hw.mac.type == e1000_ich8lan) ||
+ (adapter->hw.mac.type == e1000_pchlan) ||
(adapter->hw.mac.type == e1000_ich9lan) ||
(adapter->hw.mac.type == e1000_ich10lan)) {
int rid = EM_BAR_TYPE_FLASH;
@@ -565,25 +509,21 @@ em_attach(device_t dev)
em_add_int_delay_sysctl(adapter, "tx_int_delay",
"transmit interrupt delay in usecs", &adapter->tx_int_delay,
E1000_REGISTER(&adapter->hw, E1000_TIDV), em_tx_int_delay_dflt);
- if (adapter->hw.mac.type >= e1000_82540) {
- em_add_int_delay_sysctl(adapter, "rx_abs_int_delay",
- "receive interrupt delay limit in usecs",
- &adapter->rx_abs_int_delay,
- E1000_REGISTER(&adapter->hw, E1000_RADV),
- em_rx_abs_int_delay_dflt);
- em_add_int_delay_sysctl(adapter, "tx_abs_int_delay",
- "transmit interrupt delay limit in usecs",
- &adapter->tx_abs_int_delay,
- E1000_REGISTER(&adapter->hw, E1000_TADV),
- em_tx_abs_int_delay_dflt);
- }
+ em_add_int_delay_sysctl(adapter, "rx_abs_int_delay",
+ "receive interrupt delay limit in usecs",
+ &adapter->rx_abs_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_RADV),
+ em_rx_abs_int_delay_dflt);
+ em_add_int_delay_sysctl(adapter, "tx_abs_int_delay",
+ "transmit interrupt delay limit in usecs",
+ &adapter->tx_abs_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_TADV),
+ em_tx_abs_int_delay_dflt);
-#ifndef EM_LEGACY_IRQ
/* Sysctls for limiting the amount of work done in the taskqueue */
em_add_rx_process_limit(adapter, "rx_processing_limit",
"max number of rx packets to process", &adapter->rx_process_limit,
em_rx_process_limit);
-#endif
/*
* Validate number of transmit and receive descriptors. It
@@ -591,18 +531,15 @@ em_attach(device_t dev)
* of E1000_DBA_ALIGN.
*/
if (((em_txd * sizeof(struct e1000_tx_desc)) % EM_DBA_ALIGN) != 0 ||
- (adapter->hw.mac.type >= e1000_82544 && em_txd > EM_MAX_TXD) ||
- (adapter->hw.mac.type < e1000_82544 && em_txd > EM_MAX_TXD_82543) ||
- (em_txd < EM_MIN_TXD)) {
+ (em_txd > EM_MAX_TXD) || (em_txd < EM_MIN_TXD)) {
device_printf(dev, "Using %d TX descriptors instead of %d!\n",
EM_DEFAULT_TXD, em_txd);
adapter->num_tx_desc = EM_DEFAULT_TXD;
} else
adapter->num_tx_desc = em_txd;
+
if (((em_rxd * sizeof(struct e1000_rx_desc)) % EM_DBA_ALIGN) != 0 ||
- (adapter->hw.mac.type >= e1000_82544 && em_rxd > EM_MAX_RXD) ||
- (adapter->hw.mac.type < e1000_82544 && em_rxd > EM_MAX_RXD_82543) ||
- (em_rxd < EM_MIN_RXD)) {
+ (em_rxd > EM_MAX_RXD) || (em_rxd < EM_MIN_RXD)) {
device_printf(dev, "Using %d RX descriptors instead of %d!\n",
EM_DEFAULT_RXD, em_rxd);
adapter->num_rx_desc = EM_DEFAULT_RXD;
@@ -612,10 +549,6 @@ em_attach(device_t dev)
adapter->hw.mac.autoneg = DO_AUTO_NEG;
adapter->hw.phy.autoneg_wait_to_complete = FALSE;
adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT;
- adapter->rx_buffer_len = 2048;
-
- e1000_init_script_state_82541(&adapter->hw, TRUE);
- e1000_set_tbi_compatibility_82543(&adapter->hw, TRUE);
/* Copper options */
if (adapter->hw.phy.media_type == e1000_media_type_copper) {
@@ -637,29 +570,13 @@ em_attach(device_t dev)
*/
adapter->hw.mac.report_tx_early = 1;
- tsize = roundup2(adapter->num_tx_desc * sizeof(struct e1000_tx_desc),
- EM_DBA_ALIGN);
-
- /* Allocate Transmit Descriptor ring */
- if (em_dma_malloc(adapter, tsize, &adapter->txdma, BUS_DMA_NOWAIT)) {
- device_printf(dev, "Unable to allocate tx_desc memory\n");
- error = ENOMEM;
- goto err_tx_desc;
- }
- adapter->tx_desc_base =
- (struct e1000_tx_desc *)adapter->txdma.dma_vaddr;
-
- rsize = roundup2(adapter->num_rx_desc * sizeof(struct e1000_rx_desc),
- EM_DBA_ALIGN);
-
- /* Allocate Receive Descriptor ring */
- if (em_dma_malloc(adapter, rsize, &adapter->rxdma, BUS_DMA_NOWAIT)) {
- device_printf(dev, "Unable to allocate rx_desc memory\n");
+ /*
+ ** Get queue/ring memory
+ */
+ if (em_allocate_queues(adapter)) {
error = ENOMEM;
- goto err_rx_desc;
+ goto err_pci;
}
- adapter->rx_desc_base =
- (struct e1000_rx_desc *)adapter->rxdma.dma_vaddr;
/*
** Start from a known state, this is
@@ -679,7 +596,7 @@ em_attach(device_t dev)
device_printf(dev,
"The EEPROM Checksum Is Not Valid\n");
error = EIO;
- goto err_hw_init;
+ goto err_late;
}
}
@@ -688,49 +605,35 @@ em_attach(device_t dev)
device_printf(dev, "EEPROM read error while reading MAC"
" address\n");
error = EIO;
- goto err_hw_init;
+ goto err_late;
}
if (!em_is_valid_ether_addr(adapter->hw.mac.addr)) {
device_printf(dev, "Invalid MAC address\n");
error = EIO;
- goto err_hw_init;
- }
-
- /* Initialize the hardware */
- if (em_hardware_init(adapter)) {
- device_printf(dev, "Unable to initialize the hardware\n");
- error = EIO;
- goto err_hw_init;
- }
-
- /* Allocate transmit descriptors and buffers */
- if (em_allocate_transmit_structures(adapter)) {
- device_printf(dev, "Could not setup transmit structures\n");
- error = ENOMEM;
- goto err_tx_struct;
- }
-
- /* Allocate receive descriptors and buffers */
- if (em_allocate_receive_structures(adapter)) {
- device_printf(dev, "Could not setup receive structures\n");
- error = ENOMEM;
- goto err_rx_struct;
+ goto err_late;
}
/*
** Do interrupt configuration
*/
- if (adapter->msi > 1) /* Do MSI/X */
+ if (adapter->msix > 1) /* Do MSIX */
error = em_allocate_msix(adapter);
else /* MSI or Legacy */
error = em_allocate_legacy(adapter);
if (error)
- goto err_rx_struct;
+ goto err_late;
+
+ /*
+ * Get Wake-on-Lan and Management info for later use
+ */
+ em_get_wakeup(dev);
/* Setup OS specific network interface */
em_setup_interface(dev, adapter);
+ em_reset(adapter);
+
/* Initialize statistics */
em_update_stats_counters(adapter);
@@ -742,104 +645,32 @@ em_attach(device_t dev)
device_printf(dev,
"PHY reset is blocked due to SOL/IDER session.\n");
- /* Determine if we have to control management hardware */
- adapter->has_manage = e1000_enable_mng_pass_thru(&adapter->hw);
-
- /*
- * Setup Wake-on-Lan
- */
- switch (adapter->hw.mac.type) {
-
- case e1000_82542:
- case e1000_82543:
- break;
- case e1000_82546:
- case e1000_82546_rev_3:
- case e1000_82571:
- case e1000_80003es2lan:
- if (adapter->hw.bus.func == 1)
- e1000_read_nvm(&adapter->hw,
- NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
- else
- e1000_read_nvm(&adapter->hw,
- NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
- eeprom_data &= EM_EEPROM_APME;
- break;
- default:
- /* APME bit in EEPROM is mapped to WUC.APME */
- eeprom_data = E1000_READ_REG(&adapter->hw, E1000_WUC) &
- E1000_WUC_APME;
- break;
- }
- if (eeprom_data)
- adapter->wol = E1000_WUFC_MAG;
- /*
- * We have the eeprom settings, now apply the special cases
- * where the eeprom may be wrong or the board won't support
- * wake on lan on a particular port
- */
- device_id = pci_get_device(dev);
- switch (device_id) {
- case E1000_DEV_ID_82546GB_PCIE:
- adapter->wol = 0;
- break;
- case E1000_DEV_ID_82546EB_FIBER:
- case E1000_DEV_ID_82546GB_FIBER:
- case E1000_DEV_ID_82571EB_FIBER:
- /* Wake events only supported on port A for dual fiber
- * regardless of eeprom setting */
- if (E1000_READ_REG(&adapter->hw, E1000_STATUS) &
- E1000_STATUS_FUNC_1)
- adapter->wol = 0;
- break;
- case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
- case E1000_DEV_ID_82571EB_QUAD_COPPER:
- case E1000_DEV_ID_82571EB_QUAD_FIBER:
- case E1000_DEV_ID_82571EB_QUAD_COPPER_LP:
- /* if quad port adapter, disable WoL on all but port A */
- if (global_quad_port_a != 0)
- adapter->wol = 0;
- /* Reset for multiple quad port adapters */
- if (++global_quad_port_a == 4)
- global_quad_port_a = 0;
- break;
- }
-
- /* Do we need workaround for 82544 PCI-X adapter? */
- if (adapter->hw.bus.type == e1000_bus_type_pcix &&
- adapter->hw.mac.type == e1000_82544)
- adapter->pcix_82544 = TRUE;
- else
- adapter->pcix_82544 = FALSE;
-
-#if __FreeBSD_version >= 700029
/* Register for VLAN events */
adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
em_register_vlan, adapter, EVENTHANDLER_PRI_FIRST);
adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
em_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST);
-#endif
+
+ /* Non-AMT based hardware can now take control from firmware */
+ if (adapter->has_manage && !adapter->has_amt)
+ em_get_hw_control(adapter);
/* Tell the stack that the interface is not active */
adapter->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ adapter->led_dev = led_create(em_led_func, adapter,
+ device_get_nameunit(dev));
+
INIT_DEBUGOUT("em_attach: end");
return (0);
-err_rx_struct:
+err_late:
em_free_transmit_structures(adapter);
-err_tx_struct:
-err_hw_init:
+ em_free_receive_structures(adapter);
em_release_hw_control(adapter);
- em_dma_free(adapter, &adapter->rxdma);
-err_rx_desc:
- em_dma_free(adapter, &adapter->txdma);
-err_tx_desc:
err_pci:
em_free_pci_resources(adapter);
- EM_TX_LOCK_DESTROY(adapter);
- EM_RX_LOCK_DESTROY(adapter);
EM_CORE_LOCK_DESTROY(adapter);
return (error);
@@ -864,11 +695,7 @@ em_detach(device_t dev)
INIT_DEBUGOUT("em_detach: begin");
/* Make sure VLANS are not using driver */
-#if __FreeBSD_version >= 700000
if (adapter->ifp->if_vlantrunk != NULL) {
-#else
- if (adapter->ifp->if_nvlans != 0) {
-#endif
device_printf(dev,"Vlan in use, detach first\n");
return (EBUSY);
}
@@ -879,41 +706,24 @@ em_detach(device_t dev)
#endif
EM_CORE_LOCK(adapter);
- EM_TX_LOCK(adapter);
adapter->in_detach = 1;
em_stop(adapter);
+ EM_CORE_UNLOCK(adapter);
+ EM_CORE_LOCK_DESTROY(adapter);
+
e1000_phy_hw_reset(&adapter->hw);
em_release_manageability(adapter);
+ em_release_hw_control(adapter);
- if (((adapter->hw.mac.type == e1000_82573) ||
- (adapter->hw.mac.type == e1000_82583) ||
- (adapter->hw.mac.type == e1000_ich8lan) ||
- (adapter->hw.mac.type == e1000_ich10lan) ||
- (adapter->hw.mac.type == e1000_ich9lan)) &&
- e1000_check_mng_mode(&adapter->hw))
- em_release_hw_control(adapter);
-
- if (adapter->wol) {
- E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
- E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol);
- em_enable_wakeup(dev);
- }
-
- EM_TX_UNLOCK(adapter);
- EM_CORE_UNLOCK(adapter);
-
-#if __FreeBSD_version >= 700029
/* Unregister VLAN events */
if (adapter->vlan_attach != NULL)
EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach);
if (adapter->vlan_detach != NULL)
EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach);
-#endif
ether_ifdetach(adapter->ifp);
callout_drain(&adapter->timer);
- callout_drain(&adapter->tx_fifo_timer);
em_free_pci_resources(adapter);
bus_generic_detach(dev);
@@ -922,21 +732,7 @@ em_detach(device_t dev)
em_free_transmit_structures(adapter);
em_free_receive_structures(adapter);
- /* Free Transmit Descriptor ring */
- if (adapter->tx_desc_base) {
- em_dma_free(adapter, &adapter->txdma);
- adapter->tx_desc_base = NULL;
- }
-
- /* Free Receive Descriptor ring */
- if (adapter->rx_desc_base) {
- em_dma_free(adapter, &adapter->rxdma);
- adapter->rx_desc_base = NULL;
- }
-
- EM_TX_LOCK_DESTROY(adapter);
- EM_RX_LOCK_DESTROY(adapter);
- EM_CORE_LOCK_DESTROY(adapter);
+ em_release_hw_control(adapter);
return (0);
}
@@ -963,25 +759,9 @@ em_suspend(device_t dev)
EM_CORE_LOCK(adapter);
- EM_TX_LOCK(adapter);
- em_stop(adapter);
- EM_TX_UNLOCK(adapter);
-
em_release_manageability(adapter);
-
- if (((adapter->hw.mac.type == e1000_82573) ||
- (adapter->hw.mac.type == e1000_82583) ||
- (adapter->hw.mac.type == e1000_ich8lan) ||
- (adapter->hw.mac.type == e1000_ich10lan) ||
- (adapter->hw.mac.type == e1000_ich9lan)) &&
- e1000_check_mng_mode(&adapter->hw))
- em_release_hw_control(adapter);
-
- if (adapter->wol) {
- E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
- E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol);
- em_enable_wakeup(dev);
- }
+ em_release_hw_control(adapter);
+ em_enable_wakeup(dev);
EM_CORE_UNLOCK(adapter);
@@ -994,6 +774,9 @@ em_resume(device_t dev)
struct adapter *adapter = device_get_softc(dev);
struct ifnet *ifp = adapter->ifp;
+ if (adapter->led_dev != NULL)
+ led_destroy(adapter->led_dev);
+
EM_CORE_LOCK(adapter);
em_init_locked(adapter);
em_init_manageability(adapter);
@@ -1016,69 +799,45 @@ em_resume(device_t dev)
#if __FreeBSD_version >= 800000
static int
-em_mq_start_locked(struct ifnet *ifp, struct mbuf *m)
+em_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m)
{
- struct adapter *adapter = ifp->if_softc;
- struct mbuf *next;
- int error = E1000_SUCCESS;
+ struct adapter *adapter = txr->adapter;
+ struct mbuf *next;
+ int err = 0, enq = 0;
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING || adapter->link_active == 0) {
+ if (m != NULL)
+ err = drbr_enqueue(ifp, txr->br, m);
+ return (err);
+ }
- EM_TX_LOCK_ASSERT(adapter);
- /* To allow being called from a tasklet */
+ enq = 0;
if (m == NULL)
- goto process;
+ next = drbr_dequeue(ifp, txr->br);
+ else
+ next = m;
- if (((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
- IFF_DRV_RUNNING)
- || (!adapter->link_active)) {
- error = drbr_enqueue(ifp, adapter->br, m);
- return (error);
- } else if (!drbr_needs_enqueue(ifp, adapter->br) &&
- (adapter->num_tx_desc_avail > EM_TX_OP_THRESHOLD)) {
- if ((error = em_xmit(adapter, &m)) != 0) {
- if (m != NULL)
- error = drbr_enqueue(ifp, adapter->br, m);
- return (error);
- } else {
- /*
- * We've bypassed the buf ring so we need to update
- * ifp directly
- */
- drbr_stats_update(ifp, m->m_pkthdr.len, m->m_flags);
- /*
- ** Send a copy of the frame to the BPF
- ** listener and set the watchdog on.
- */
- ETHER_BPF_MTAP(ifp, m);
- adapter->watchdog_timer = EM_TX_TIMEOUT;
- }
- } else if ((error = drbr_enqueue(ifp, adapter->br, m)) != 0)
- return (error);
-
-process:
- if (drbr_empty(ifp, adapter->br))
- return(error);
- /* Process the queue */
- while (TRUE) {
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
- break;
- next = drbr_dequeue(ifp, adapter->br);
- if (next == NULL)
- break;
- if ((error = em_xmit(adapter, &next)) != 0) {
- if (next != NULL)
- error = drbr_enqueue(ifp, adapter->br, next);
+ /* Process the queue */
+ while (next != NULL) {
+ if ((err = em_xmit(txr, &next)) != 0) {
+ if (next != NULL)
+ err = drbr_enqueue(ifp, txr->br, next);
break;
}
+ enq++;
drbr_stats_update(ifp, next->m_pkthdr.len, next->m_flags);
- ETHER_BPF_MTAP(ifp, next);
- /* Set the watchdog */
- adapter->watchdog_timer = EM_TX_TIMEOUT;
- }
-
- if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD)
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ ETHER_BPF_MTAP(ifp, next);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
+ next = drbr_dequeue(ifp, txr->br);
+ }
- return (error);
+ if (enq > 0) {
+ /* Set the watchdog */
+ txr->watchdog_check = TRUE;
+ }
+ return (err);
}
/*
@@ -1088,45 +847,61 @@ process:
static int
em_mq_start(struct ifnet *ifp, struct mbuf *m)
{
-
- struct adapter *adapter = ifp->if_softc;
- int error = 0;
+ struct adapter *adapter = ifp->if_softc;
+ struct tx_ring *txr;
+ int i, error = 0;
- if (EM_TX_TRYLOCK(adapter)) {
+ /* Which queue to use */
+ if ((m->m_flags & M_FLOWID) != 0)
+ i = m->m_pkthdr.flowid % adapter->num_queues;
+ else
+ i = curcpu % adapter->num_queues;
+
+ txr = &adapter->tx_rings[i];
+
+ if (EM_TX_TRYLOCK(txr)) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- error = em_mq_start_locked(ifp, m);
- EM_TX_UNLOCK(adapter);
+ error = em_mq_start_locked(ifp, txr, m);
+ EM_TX_UNLOCK(txr);
} else
- error = drbr_enqueue(ifp, adapter->br, m);
+ error = drbr_enqueue(ifp, txr->br, m);
return (error);
}
+/*
+** Flush all ring buffers
+*/
static void
em_qflush(struct ifnet *ifp)
{
- struct mbuf *m;
- struct adapter *adapter = (struct adapter *)ifp->if_softc;
+ struct adapter *adapter = ifp->if_softc;
+ struct tx_ring *txr = adapter->tx_rings;
+ struct mbuf *m;
- EM_TX_LOCK(adapter);
- while ((m = buf_ring_dequeue_sc(adapter->br)) != NULL)
- m_freem(m);
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ EM_TX_LOCK(txr);
+ while ((m = buf_ring_dequeue_sc(txr->br)) != NULL)
+ m_freem(m);
+ EM_TX_UNLOCK(txr);
+ }
if_qflush(ifp);
- EM_TX_UNLOCK(adapter);
}
+
#endif /* FreeBSD_version */
static void
-em_start_locked(struct ifnet *ifp)
+em_start_locked(struct ifnet *ifp, struct tx_ring *txr)
{
struct adapter *adapter = ifp->if_softc;
struct mbuf *m_head;
- EM_TX_LOCK_ASSERT(adapter);
+ EM_TX_LOCK_ASSERT(txr);
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
return;
+
if (!adapter->link_active)
return;
@@ -1139,7 +914,7 @@ em_start_locked(struct ifnet *ifp)
* Encapsulation can modify our pointer, and or make it
* NULL on failure. In that event, we can't requeue.
*/
- if (em_xmit(adapter, &m_head)) {
+ if (em_xmit(txr, &m_head)) {
if (m_head == NULL)
break;
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
@@ -1151,10 +926,8 @@ em_start_locked(struct ifnet *ifp)
ETHER_BPF_MTAP(ifp, m_head);
/* Set timeout in case hardware has problems transmitting. */
- adapter->watchdog_timer = EM_TX_TIMEOUT;
+ txr->watchdog_check = TRUE;
}
- if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD)
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
return;
}
@@ -1162,12 +935,15 @@ em_start_locked(struct ifnet *ifp)
static void
em_start(struct ifnet *ifp)
{
- struct adapter *adapter = ifp->if_softc;
+ struct adapter *adapter = ifp->if_softc;
+ struct tx_ring *txr = adapter->tx_rings;
- EM_TX_LOCK(adapter);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- em_start_locked(ifp);
- EM_TX_UNLOCK(adapter);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ EM_TX_LOCK(txr);
+ em_start_locked(ifp, txr);
+ EM_TX_UNLOCK(txr);
+ }
+ return;
}
/*********************************************************************
@@ -1209,8 +985,7 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
em_init_locked(adapter);
EM_CORE_UNLOCK(adapter);
}
- if (!(ifp->if_flags & IFF_NOARP))
- arp_ifinit(ifp, ifa);
+ arp_ifinit(ifp, ifa);
} else
#endif
error = ether_ioctl(ifp, command, data);
@@ -1218,34 +993,23 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
case SIOCSIFMTU:
{
int max_frame_size;
- u16 eeprom_data = 0;
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)");
EM_CORE_LOCK(adapter);
switch (adapter->hw.mac.type) {
- case e1000_82573:
- /*
- * 82573 only supports jumbo frames
- * if ASPM is disabled.
- */
- e1000_read_nvm(&adapter->hw,
- NVM_INIT_3GIO_3, 1, &eeprom_data);
- if (eeprom_data & NVM_WORD1A_ASPM_MASK) {
- max_frame_size = ETHER_MAX_LEN;
- break;
- }
- /* Allow Jumbo frames - fall thru */
case e1000_82571:
case e1000_82572:
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_82574:
- case e1000_80003es2lan: /* Limit Jumbo Frame size */
+ case e1000_80003es2lan: /* 9K Jumbo Frame size */
max_frame_size = 9234;
break;
+ case e1000_pchlan:
+ max_frame_size = 4096;
+ break;
/* Adapters that do not support jumbo frames */
- case e1000_82542:
case e1000_82583:
case e1000_ich8lan:
max_frame_size = ETHER_MAX_LEN;
@@ -1281,11 +1045,8 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
} else
em_init_locked(adapter);
} else
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- EM_TX_LOCK(adapter);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
em_stop(adapter);
- EM_TX_UNLOCK(adapter);
- }
adapter->if_flags = ifp->if_flags;
EM_CORE_UNLOCK(adapter);
break;
@@ -1296,10 +1057,6 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
EM_CORE_LOCK(adapter);
em_disable_intr(adapter);
em_set_multi(adapter);
- if (adapter->hw.mac.type == e1000_82542 &&
- adapter->hw.revision_id == E1000_REVISION_2) {
- em_initialize_receive_unit(adapter);
- }
#ifdef DEVICE_POLLING
if (!(ifp->if_capenable & IFCAP_POLLING))
#endif
@@ -1353,22 +1110,24 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_capenable ^= IFCAP_HWCSUM;
reinit = 1;
}
-#if __FreeBSD_version >= 700000
if (mask & IFCAP_TSO4) {
ifp->if_capenable ^= IFCAP_TSO4;
reinit = 1;
}
-#endif
-
if (mask & IFCAP_VLAN_HWTAGGING) {
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
reinit = 1;
}
+ if ((mask & IFCAP_WOL) &&
+ (ifp->if_capabilities & IFCAP_WOL) != 0) {
+ if (mask & IFCAP_WOL_MCAST)
+ ifp->if_capenable ^= IFCAP_WOL_MCAST;
+ if (mask & IFCAP_WOL_MAGIC)
+ ifp->if_capenable ^= IFCAP_WOL_MAGIC;
+ }
if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING))
em_init(adapter);
-#if __FreeBSD_version >= 700000
VLAN_CAPABILITIES(ifp);
-#endif
break;
}
@@ -1380,53 +1139,6 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
return (error);
}
-/*********************************************************************
- * Watchdog timer:
- *
- * This routine is called from the local timer every second.
- * As long as transmit descriptors are being cleaned the value
- * is non-zero and we do nothing. Reaching 0 indicates a tx hang
- * and we then reset the device.
- *
- **********************************************************************/
-
-static void
-em_watchdog(struct adapter *adapter)
-{
-
- EM_CORE_LOCK_ASSERT(adapter);
-
- /*
- ** The timer is set to 5 every time start queues a packet.
- ** Then txeof keeps resetting it as long as it cleans at
- ** least one descriptor.
- ** Finally, anytime all descriptors are clean the timer is
- ** set to 0.
- */
- EM_TX_LOCK(adapter);
- if ((adapter->watchdog_timer == 0) || (--adapter->watchdog_timer)) {
- EM_TX_UNLOCK(adapter);
- return;
- }
-
- /* If we are in this routine because of pause frames, then
- * don't reset the hardware.
- */
- if (E1000_READ_REG(&adapter->hw, E1000_STATUS) &
- E1000_STATUS_TXOFF) {
- adapter->watchdog_timer = EM_TX_TIMEOUT;
- EM_TX_UNLOCK(adapter);
- return;
- }
-
- if (e1000_check_for_link(&adapter->hw) == 0)
- device_printf(adapter->dev, "watchdog timeout -- resetting\n");
- adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
- adapter->watchdog_events++;
- EM_TX_UNLOCK(adapter);
-
- em_init_locked(adapter);
-}
/*********************************************************************
* Init entry point
@@ -1450,33 +1162,15 @@ em_init_locked(struct adapter *adapter)
EM_CORE_LOCK_ASSERT(adapter);
- EM_TX_LOCK(adapter);
- em_stop(adapter);
- EM_TX_UNLOCK(adapter);
+ em_disable_intr(adapter);
+ callout_stop(&adapter->timer);
/*
* Packet Buffer Allocation (PBA)
* Writing PBA sets the receive portion of the buffer
* the remainder is used for the transmit buffer.
- *
- * Devices before the 82547 had a Packet Buffer of 64K.
- * Default allocation: PBA=48K for Rx, leaving 16K for Tx.
- * After the 82547 the buffer was reduced to 40K.
- * Default allocation: PBA=30K for Rx, leaving 10K for Tx.
- * Note: default does not leave enough room for Jumbo Frame >10k.
*/
switch (adapter->hw.mac.type) {
- case e1000_82547:
- case e1000_82547_rev_2: /* 82547: Total Packet Buffer is 40K */
- if (adapter->max_frame_size > 8192)
- pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */
- else
- pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */
- adapter->tx_fifo_head = 0;
- adapter->tx_head_addr = pba << EM_TX_HEAD_ADDR_SHIFT;
- adapter->tx_fifo_size =
- (E1000_PBA_40K - pba) << EM_PBA_BYTES_SHIFT;
- break;
/* Total Packet Buffer on these is 48K */
case e1000_82571:
case e1000_82572:
@@ -1492,11 +1186,13 @@ em_init_locked(struct adapter *adapter)
break;
case e1000_ich9lan:
case e1000_ich10lan:
+ case e1000_pchlan:
+ pba = E1000_PBA_10K;
+ break;
case e1000_ich8lan:
pba = E1000_PBA_8K;
break;
default:
- /* Devices before 82547 had a Packet Buffer of 64K. */
if (adapter->max_frame_size > 8192)
pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */
else
@@ -1526,37 +1222,21 @@ em_init_locked(struct adapter *adapter)
}
/* Initialize the hardware */
- if (em_hardware_init(adapter)) {
- device_printf(dev, "Unable to initialize the hardware\n");
- return;
- }
+ em_reset(adapter);
em_update_link_status(adapter);
/* Setup VLAN support, basic and offload if available */
E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN);
-#if __FreeBSD_version < 700029
- if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) {
- u32 ctrl;
- ctrl = E1000_READ_REG(&adapter->hw, E1000_CTRL);
- ctrl |= E1000_CTRL_VME;
- E1000_WRITE_REG(&adapter->hw, E1000_CTRL, ctrl);
- }
-#else
/* Use real VLAN Filter support */
em_setup_vlan_hw_support(adapter);
-#endif
/* Set hardware offload abilities */
ifp->if_hwassist = 0;
- if (adapter->hw.mac.type >= e1000_82543) {
- if (ifp->if_capenable & IFCAP_TXCSUM)
- ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
-#if __FreeBSD_version >= 700000
- if (ifp->if_capenable & IFCAP_TSO4)
- ifp->if_hwassist |= CSUM_TSO;
-#endif
- }
+ if (ifp->if_capenable & IFCAP_TXCSUM)
+ ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
+ if (ifp->if_capenable & IFCAP_TSO4)
+ ifp->if_hwassist |= CSUM_TSO;
/* Configure for OS presence */
em_init_manageability(adapter);
@@ -1571,9 +1251,7 @@ em_init_locked(struct adapter *adapter)
/* Prepare receive descriptors and buffers */
if (em_setup_receive_structures(adapter)) {
device_printf(dev, "Could not setup receive structures\n");
- EM_TX_LOCK(adapter);
em_stop(adapter);
- EM_TX_UNLOCK(adapter);
return;
}
em_initialize_receive_unit(adapter);
@@ -1593,14 +1271,8 @@ em_init_locked(struct adapter *adapter)
tmp = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
tmp |= E1000_CTRL_EXT_PBA_CLR;
E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, tmp);
- /*
- ** Set the IVAR - interrupt vector routing.
- ** Each nibble represents a vector, high bit
- ** is enable, other 3 bits are the MSIX table
- ** entry, we map RXQ0 to 0, TXQ0 to 1, and
- ** Link (other) to 2, hence the magic number.
- */
- E1000_WRITE_REG(&adapter->hw, E1000_IVAR, 0x800A0908);
+ /* Set the IVAR - interrupt vector routing. */
+ E1000_WRITE_REG(&adapter->hw, E1000_IVAR, adapter->ivars);
}
#ifdef DEVICE_POLLING
@@ -1614,6 +1286,10 @@ em_init_locked(struct adapter *adapter)
#endif /* DEVICE_POLLING */
em_enable_intr(adapter);
+ /* AMT based hardware can now take control from firmware */
+ if (adapter->has_manage && adapter->has_amt)
+ em_get_hw_control(adapter);
+
/* Don't reset the phy next time init gets called */
adapter->hw.phy.reset_disable = TRUE;
}
@@ -1632,13 +1308,15 @@ em_init(void *arg)
#ifdef DEVICE_POLLING
/*********************************************************************
*
- * Legacy polling routine
+ * Legacy polling routine: note this only works with single queue
*
*********************************************************************/
static int
em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
struct adapter *adapter = ifp->if_softc;
+ struct tx_ring *txr = adapter->tx_rings;
+ struct rx_ring *rxr = adapter->rx_rings;
u32 reg_icr, rx_done = 0;
EM_CORE_LOCK(adapter);
@@ -1659,137 +1337,30 @@ em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
}
EM_CORE_UNLOCK(adapter);
- rx_done = em_rxeof(adapter, count);
+ rx_done = em_rxeof(rxr, count);
- EM_TX_LOCK(adapter);
- em_txeof(adapter);
+ EM_TX_LOCK(txr);
+ em_txeof(txr);
#if __FreeBSD_version >= 800000
- if (!drbr_empty(ifp, adapter->br))
- em_mq_start_locked(ifp, NULL);
+ if (!drbr_empty(ifp, txr->br))
+ em_mq_start_locked(ifp, txr, NULL);
#else
- if (!IFQ_DRV_IS_EMPTY(&ifp->snd))
- em_start_locked(ifp);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ em_start_locked(ifp, txr);
#endif
- EM_TX_UNLOCK(adapter);
+ EM_TX_UNLOCK(txr);
+
return (rx_done);
}
#endif /* DEVICE_POLLING */
-#ifdef EM_LEGACY_IRQ
-/*********************************************************************
- *
- * Legacy Interrupt Service routine
- *
- *********************************************************************/
-
-static void
-em_intr(void *arg)
-{
- struct adapter *adapter = arg;
- struct ifnet *ifp = adapter->ifp;
- u32 reg_icr;
-
-
- if (ifp->if_capenable & IFCAP_POLLING)
- return;
-
- EM_CORE_LOCK(adapter);
- reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
- if (reg_icr & E1000_ICR_RXO)
- adapter->rx_overruns++;
- if ((reg_icr == 0xffffffff) || (reg_icr == 0)||
- (adapter->hw.mac.type >= e1000_82571 &&
- (reg_icr & E1000_ICR_INT_ASSERTED) == 0))
- goto out;
-
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
- goto out;
-
- if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
- callout_stop(&adapter->timer);
- adapter->hw.mac.get_link_status = 1;
- em_update_link_status(adapter);
- /* Deal with TX cruft when link lost */
- em_tx_purge(adapter);
- callout_reset(&adapter->timer, hz,
- em_local_timer, adapter);
- goto out;
- }
-
- EM_TX_LOCK(adapter);
- em_txeof(adapter);
- em_rxeof(adapter, -1);
- em_txeof(adapter);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
- !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- em_start_locked(ifp);
- EM_TX_UNLOCK(adapter);
-
-out:
- EM_CORE_UNLOCK(adapter);
- return;
-}
-
-#else /* EM_FAST_IRQ, then fast interrupt routines only */
-
-static void
-em_handle_link(void *context, int pending)
-{
- struct adapter *adapter = context;
- struct ifnet *ifp = adapter->ifp;
-
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- return;
-
- EM_CORE_LOCK(adapter);
- callout_stop(&adapter->timer);
- em_update_link_status(adapter);
- /* Deal with TX cruft when link lost */
- em_tx_purge(adapter);
- callout_reset(&adapter->timer, hz, em_local_timer, adapter);
- EM_CORE_UNLOCK(adapter);
-}
-
-
-/* Combined RX/TX handler, used by Legacy and MSI */
-static void
-em_handle_rxtx(void *context, int pending)
-{
- struct adapter *adapter = context;
- struct ifnet *ifp = adapter->ifp;
-
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if (em_rxeof(adapter, adapter->rx_process_limit) != 0)
- taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
- EM_TX_LOCK(adapter);
- em_txeof(adapter);
-
-#if __FreeBSD_version >= 800000
- if (!drbr_empty(ifp, adapter->br))
- em_mq_start_locked(ifp, NULL);
-#else
- if (!IFQ_DRV_IS_EMPTY(&ifp->snd))
- em_start_locked(ifp);
-#endif
- EM_TX_UNLOCK(adapter);
- }
-
- em_enable_intr(adapter);
-}
/*********************************************************************
*
* Fast Legacy/MSI Combined Interrupt Service routine
*
*********************************************************************/
-#if __FreeBSD_version < 700000
-#define FILTER_STRAY
-#define FILTER_HANDLED
-static void
-#else
static int
-#endif
em_irq_fast(void *arg)
{
struct adapter *adapter = arg;
@@ -1816,13 +1387,8 @@ em_irq_fast(void *arg)
(reg_icr & E1000_ICR_INT_ASSERTED) == 0)
return FILTER_STRAY;
- /*
- * Mask interrupts until the taskqueue is finished running. This is
- * cheap, just assume that it is needed. This also works around the
- * MSI message reordering errata on certain systems.
- */
em_disable_intr(adapter);
- taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
+ taskqueue_enqueue(adapter->tq, &adapter->que_task);
/* Link status change */
if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
@@ -1835,30 +1401,64 @@ em_irq_fast(void *arg)
return FILTER_HANDLED;
}
+/* Combined RX/TX handler, used by Legacy and MSI */
+static void
+em_handle_que(void *context, int pending)
+{
+ struct adapter *adapter = context;
+ struct ifnet *ifp = adapter->ifp;
+ struct tx_ring *txr = adapter->tx_rings;
+ struct rx_ring *rxr = adapter->rx_rings;
+ u32 loop = EM_MAX_LOOP;
+ bool more_rx, more_tx;
+
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ EM_TX_LOCK(txr);
+ do {
+ more_rx = em_rxeof(rxr, adapter->rx_process_limit);
+ more_tx = em_txeof(txr);
+ } while (loop-- && (more_rx || more_tx));
+
+#if __FreeBSD_version >= 800000
+ if (!drbr_empty(ifp, txr->br))
+ em_mq_start_locked(ifp, txr, NULL);
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ em_start_locked(ifp, txr);
+#endif
+ if (more_rx || more_tx)
+ taskqueue_enqueue(adapter->tq, &adapter->que_task);
+
+ EM_TX_UNLOCK(txr);
+ }
+
+ em_enable_intr(adapter);
+ return;
+}
+
+
/*********************************************************************
*
* MSIX Interrupt Service Routines
*
**********************************************************************/
-#define EM_MSIX_TX 0x00040000
-#define EM_MSIX_RX 0x00010000
-#define EM_MSIX_LINK 0x00100000
-
static void
em_msix_tx(void *arg)
{
- struct adapter *adapter = arg;
- struct ifnet *ifp = adapter->ifp;
+ struct tx_ring *txr = arg;
+ struct adapter *adapter = txr->adapter;
+ bool more;
- ++adapter->tx_irq;
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- EM_TX_LOCK(adapter);
- em_txeof(adapter);
- EM_TX_UNLOCK(adapter);
- taskqueue_enqueue(adapter->tq, &adapter->tx_task);
- }
- /* Reenable this interrupt */
- E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_TX);
+ ++txr->tx_irq;
+ EM_TX_LOCK(txr);
+ more = em_txeof(txr);
+ EM_TX_UNLOCK(txr);
+ if (more)
+ taskqueue_enqueue(txr->tq, &txr->tx_task);
+ else
+ /* Reenable this interrupt */
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS, txr->ims);
return;
}
@@ -1871,15 +1471,17 @@ em_msix_tx(void *arg)
static void
em_msix_rx(void *arg)
{
- struct adapter *adapter = arg;
- struct ifnet *ifp = adapter->ifp;
+ struct rx_ring *rxr = arg;
+ struct adapter *adapter = rxr->adapter;
+ bool more;
- ++adapter->rx_irq;
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (em_rxeof(adapter, adapter->rx_process_limit) != 0))
- taskqueue_enqueue(adapter->tq, &adapter->rx_task);
- /* Reenable this interrupt */
- E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_RX);
+ ++rxr->rx_irq;
+ more = em_rxeof(rxr, adapter->rx_process_limit);
+ if (more)
+ taskqueue_enqueue(rxr->tq, &rxr->rx_task);
+ else
+ /* Reenable this interrupt */
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS, rxr->ims);
return;
}
@@ -1888,7 +1490,6 @@ em_msix_rx(void *arg)
* MSIX Link Fast Interrupt Service routine
*
**********************************************************************/
-
static void
em_msix_link(void *arg)
{
@@ -1901,45 +1502,71 @@ em_msix_link(void *arg)
if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
adapter->hw.mac.get_link_status = 1;
taskqueue_enqueue(taskqueue_fast, &adapter->link_task);
- }
- E1000_WRITE_REG(&adapter->hw, E1000_IMS,
- EM_MSIX_LINK | E1000_IMS_LSC);
+ } else
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS,
+ EM_MSIX_LINK | E1000_IMS_LSC);
return;
}
static void
em_handle_rx(void *context, int pending)
{
- struct adapter *adapter = context;
- struct ifnet *ifp = adapter->ifp;
-
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (em_rxeof(adapter, adapter->rx_process_limit) != 0))
- taskqueue_enqueue(adapter->tq, &adapter->rx_task);
+ struct rx_ring *rxr = context;
+ struct adapter *adapter = rxr->adapter;
+ u32 loop = EM_MAX_LOOP;
+ bool more;
+ do {
+ more = em_rxeof(rxr, adapter->rx_process_limit);
+ } while (loop-- && more);
+ /* Reenable this interrupt */
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS, rxr->ims);
}
static void
em_handle_tx(void *context, int pending)
{
- struct adapter *adapter = context;
+ struct tx_ring *txr = context;
+ struct adapter *adapter = txr->adapter;
struct ifnet *ifp = adapter->ifp;
+ u32 loop = EM_MAX_LOOP;
+ bool more;
+
+ if (!EM_TX_TRYLOCK(txr))
+ return;
+ do {
+ more = em_txeof(txr);
+ } while (loop-- && more);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if (!EM_TX_TRYLOCK(adapter))
- return;
- em_txeof(adapter);
#if __FreeBSD_version >= 800000
- if (!drbr_empty(ifp, adapter->br))
- em_mq_start_locked(ifp, NULL);
+ if (!drbr_empty(ifp, txr->br))
+ em_mq_start_locked(ifp, txr, NULL);
#else
- if (!IFQ_DRV_IS_EMPTY(&ifp->snd))
- em_start_locked(ifp);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ em_start_locked(ifp, txr);
#endif
- EM_TX_UNLOCK(adapter);
- }
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS, txr->ims);
+ EM_TX_UNLOCK(txr);
+}
+
+static void
+em_handle_link(void *context, int pending)
+{
+ struct adapter *adapter = context;
+ struct ifnet *ifp = adapter->ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ return;
+
+ EM_CORE_LOCK(adapter);
+ callout_stop(&adapter->timer);
+ em_update_link_status(adapter);
+ callout_reset(&adapter->timer, hz, em_local_timer, adapter);
+ E1000_WRITE_REG(&adapter->hw, E1000_IMS,
+ EM_MSIX_LINK | E1000_IMS_LSC);
+ EM_CORE_UNLOCK(adapter);
}
-#endif /* EM_FAST_IRQ */
+
/*********************************************************************
*
@@ -1972,8 +1599,6 @@ em_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
if ((adapter->hw.phy.media_type == e1000_media_type_fiber) ||
(adapter->hw.phy.media_type == e1000_media_type_internal_serdes)) {
- if (adapter->hw.mac.type == e1000_82545)
- fiber_type = IFM_1000_LX;
ifmr->ifm_active |= fiber_type | IFM_FDX;
} else {
switch (adapter->link_speed) {
@@ -2065,8 +1690,9 @@ em_media_change(struct ifnet *ifp)
**********************************************************************/
static int
-em_xmit(struct adapter *adapter, struct mbuf **m_headp)
+em_xmit(struct tx_ring *txr, struct mbuf **m_headp)
{
+ struct adapter *adapter = txr->adapter;
bus_dma_segment_t segs[EM_MAX_SCATTER];
bus_dmamap_t map;
struct em_buffer *tx_buffer, *tx_buffer_mapped;
@@ -2075,31 +1701,17 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
u32 txd_upper, txd_lower, txd_used, txd_saved;
int nsegs, i, j, first, last = 0;
int error, do_tso, tso_desc = 0;
-#if __FreeBSD_version < 700000
- struct m_tag *mtag;
-#endif
+
m_head = *m_headp;
txd_upper = txd_lower = txd_used = txd_saved = 0;
-
-#if __FreeBSD_version >= 700000
do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0);
-#else
- do_tso = 0;
-#endif
/*
* Force a cleanup if number of TX descriptors
* available hits the threshold
*/
- if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) {
- em_txeof(adapter);
- /* Now do we at least have a minimal? */
- if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD) {
- adapter->no_tx_desc_avail1++;
- return (ENOBUFS);
- }
- }
-
+ if (txr->tx_avail <= EM_TX_CLEANUP_THRESHOLD)
+ em_txeof(txr);
/*
* TSO workaround:
@@ -2121,12 +1733,12 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
* of the EOP which is the only one that
* now gets a DONE bit writeback.
*/
- first = adapter->next_avail_tx_desc;
- tx_buffer = &adapter->tx_buffer_area[first];
+ first = txr->next_avail_desc;
+ tx_buffer = &txr->tx_buffers[first];
tx_buffer_mapped = tx_buffer;
map = tx_buffer->map;
- error = bus_dmamap_load_mbuf_sg(adapter->txtag, map,
+ error = bus_dmamap_load_mbuf_sg(txr->txtag, map,
*m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
/*
@@ -2151,7 +1763,7 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
*m_headp = m;
/* Try it again */
- error = bus_dmamap_load_mbuf_sg(adapter->txtag, map,
+ error = bus_dmamap_load_mbuf_sg(txr->txtag, map,
*m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
if (error) {
@@ -2171,15 +1783,15 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
* it follows a TSO burst, then we need to add a
* sentinel descriptor to prevent premature writeback.
*/
- if ((do_tso == 0) && (adapter->tx_tso == TRUE)) {
+ if ((do_tso == 0) && (txr->tx_tso == TRUE)) {
if (nsegs == 1)
tso_desc = TRUE;
- adapter->tx_tso = FALSE;
+ txr->tx_tso = FALSE;
}
- if (nsegs > (adapter->num_tx_desc_avail - 2)) {
- adapter->no_tx_desc_avail2++;
- bus_dmamap_unload(adapter->txtag, map);
+ if (nsegs > (txr->tx_avail - 2)) {
+ txr->no_desc_avail++;
+ bus_dmamap_unload(txr->txtag, map);
return (ENOBUFS);
}
m_head = *m_headp;
@@ -2187,7 +1799,7 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
/* Do hardware assists */
#if __FreeBSD_version >= 700000
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
- error = em_tso_setup(adapter, m_head, &txd_upper, &txd_lower);
+ error = em_tso_setup(txr, m_head, &txd_upper, &txd_lower);
if (error != TRUE)
return (ENXIO); /* something foobar */
/* we need to make a final sentinel transmit desc */
@@ -2195,123 +1807,70 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
} else
#endif
if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD)
- em_transmit_checksum_setup(adapter, m_head,
+ em_transmit_checksum_setup(txr, m_head,
&txd_upper, &txd_lower);
- i = adapter->next_avail_tx_desc;
- if (adapter->pcix_82544)
- txd_saved = i;
+ i = txr->next_avail_desc;
/* Set up our transmit descriptors */
for (j = 0; j < nsegs; j++) {
bus_size_t seg_len;
bus_addr_t seg_addr;
- /* If adapter is 82544 and on PCIX bus */
- if(adapter->pcix_82544) {
- DESC_ARRAY desc_array;
- u32 array_elements, counter;
- /*
- * Check the Address and Length combination and
- * split the data accordingly
- */
- array_elements = em_fill_descriptors(segs[j].ds_addr,
- segs[j].ds_len, &desc_array);
- for (counter = 0; counter < array_elements; counter++) {
- if (txd_used == adapter->num_tx_desc_avail) {
- adapter->next_avail_tx_desc = txd_saved;
- adapter->no_tx_desc_avail2++;
- bus_dmamap_unload(adapter->txtag, map);
- return (ENOBUFS);
- }
- tx_buffer = &adapter->tx_buffer_area[i];
- ctxd = &adapter->tx_desc_base[i];
- ctxd->buffer_addr = htole64(
- desc_array.descriptor[counter].address);
- ctxd->lower.data = htole32(
- (adapter->txd_cmd | txd_lower | (u16)
- desc_array.descriptor[counter].length));
- ctxd->upper.data =
- htole32((txd_upper));
- last = i;
- if (++i == adapter->num_tx_desc)
- i = 0;
- tx_buffer->m_head = NULL;
- tx_buffer->next_eop = -1;
- txd_used++;
- }
+
+ tx_buffer = &txr->tx_buffers[i];
+ ctxd = &txr->tx_base[i];
+ seg_addr = segs[j].ds_addr;
+ seg_len = segs[j].ds_len;
+ /*
+ ** TSO Workaround:
+ ** If this is the last descriptor, we want to
+ ** split it so we have a small final sentinel
+ */
+ if (tso_desc && (j == (nsegs -1)) && (seg_len > 8)) {
+ seg_len -= 4;
+ ctxd->buffer_addr = htole64(seg_addr);
+ ctxd->lower.data = htole32(
+ adapter->txd_cmd | txd_lower | seg_len);
+ ctxd->upper.data =
+ htole32(txd_upper);
+ if (++i == adapter->num_tx_desc)
+ i = 0;
+ /* Now make the sentinel */
+ ++txd_used; /* using an extra txd */
+ ctxd = &txr->tx_base[i];
+ tx_buffer = &txr->tx_buffers[i];
+ ctxd->buffer_addr =
+ htole64(seg_addr + seg_len);
+ ctxd->lower.data = htole32(
+ adapter->txd_cmd | txd_lower | 4);
+ ctxd->upper.data =
+ htole32(txd_upper);
+ last = i;
+ if (++i == adapter->num_tx_desc)
+ i = 0;
} else {
- tx_buffer = &adapter->tx_buffer_area[i];
- ctxd = &adapter->tx_desc_base[i];
- seg_addr = segs[j].ds_addr;
- seg_len = segs[j].ds_len;
- /*
- ** TSO Workaround:
- ** If this is the last descriptor, we want to
- ** split it so we have a small final sentinel
- */
- if (tso_desc && (j == (nsegs -1)) && (seg_len > 8)) {
- seg_len -= 4;
- ctxd->buffer_addr = htole64(seg_addr);
- ctxd->lower.data = htole32(
- adapter->txd_cmd | txd_lower | seg_len);
- ctxd->upper.data =
- htole32(txd_upper);
- if (++i == adapter->num_tx_desc)
- i = 0;
- /* Now make the sentinel */
- ++txd_used; /* using an extra txd */
- ctxd = &adapter->tx_desc_base[i];
- tx_buffer = &adapter->tx_buffer_area[i];
- ctxd->buffer_addr =
- htole64(seg_addr + seg_len);
- ctxd->lower.data = htole32(
- adapter->txd_cmd | txd_lower | 4);
- ctxd->upper.data =
- htole32(txd_upper);
- last = i;
- if (++i == adapter->num_tx_desc)
- i = 0;
- } else {
- ctxd->buffer_addr = htole64(seg_addr);
- ctxd->lower.data = htole32(
- adapter->txd_cmd | txd_lower | seg_len);
- ctxd->upper.data =
- htole32(txd_upper);
- last = i;
- if (++i == adapter->num_tx_desc)
- i = 0;
- }
- tx_buffer->m_head = NULL;
- tx_buffer->next_eop = -1;
+ ctxd->buffer_addr = htole64(seg_addr);
+ ctxd->lower.data = htole32(
+ adapter->txd_cmd | txd_lower | seg_len);
+ ctxd->upper.data =
+ htole32(txd_upper);
+ last = i;
+ if (++i == adapter->num_tx_desc)
+ i = 0;
}
+ tx_buffer->m_head = NULL;
+ tx_buffer->next_eop = -1;
}
- adapter->next_avail_tx_desc = i;
- if (adapter->pcix_82544)
- adapter->num_tx_desc_avail -= txd_used;
- else {
- adapter->num_tx_desc_avail -= nsegs;
- if (tso_desc) /* TSO used an extra for sentinel */
- adapter->num_tx_desc_avail -= txd_used;
- }
+ txr->next_avail_desc = i;
+ txr->tx_avail -= nsegs;
+ if (tso_desc) /* TSO used an extra for sentinel */
+ txr->tx_avail -= txd_used;
- /*
- ** Handle VLAN tag, this is the
- ** biggest difference between
- ** 6.x and 7
- */
-#if __FreeBSD_version < 700000
- /* Find out if we are in vlan mode. */
- mtag = VLAN_OUTPUT_TAG(ifp, m_head);
- if (mtag != NULL) {
- ctxd->upper.fields.special =
- htole16(VLAN_TAG_VALUE(mtag));
-#else /* FreeBSD 7 */
if (m_head->m_flags & M_VLANTAG) {
/* Set the vlan id. */
ctxd->upper.fields.special =
htole16(m_head->m_pkthdr.ether_vtag);
-#endif
/* Tell hardware to add tag */
ctxd->lower.data |= htole32(E1000_TXD_CMD_VLE);
}
@@ -2319,7 +1878,7 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
tx_buffer->m_head = m_head;
tx_buffer_mapped->map = tx_buffer->map;
tx_buffer->map = map;
- bus_dmamap_sync(adapter->txtag, map, BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(txr->txtag, map, BUS_DMASYNC_PREWRITE);
/*
* Last Descriptor of Packet
@@ -2332,146 +1891,21 @@ em_xmit(struct adapter *adapter, struct mbuf **m_headp)
* Keep track in the first buffer which
* descriptor will be written back
*/
- tx_buffer = &adapter->tx_buffer_area[first];
+ tx_buffer = &txr->tx_buffers[first];
tx_buffer->next_eop = last;
/*
* Advance the Transmit Descriptor Tail (TDT), this tells the E1000
* that this frame is available to transmit.
*/
- bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
- if (adapter->hw.mac.type == e1000_82547 &&
- adapter->link_duplex == HALF_DUPLEX)
- em_82547_move_tail(adapter);
- else {
- E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), i);
- if (adapter->hw.mac.type == e1000_82547)
- em_82547_update_fifo_head(adapter,
- m_head->m_pkthdr.len);
- }
-
- return (0);
-}
-
-/*********************************************************************
- *
- * 82547 workaround to avoid controller hang in half-duplex environment.
- * The workaround is to avoid queuing a large packet that would span
- * the internal Tx FIFO ring boundary. We need to reset the FIFO pointers
- * in this case. We do that only when FIFO is quiescent.
- *
- **********************************************************************/
-static void
-em_82547_move_tail(void *arg)
-{
- struct adapter *adapter = arg;
- struct e1000_tx_desc *tx_desc;
- u16 hw_tdt, sw_tdt, length = 0;
- bool eop = 0;
-
- EM_TX_LOCK_ASSERT(adapter);
-
- hw_tdt = E1000_READ_REG(&adapter->hw, E1000_TDT(0));
- sw_tdt = adapter->next_avail_tx_desc;
-
- while (hw_tdt != sw_tdt) {
- tx_desc = &adapter->tx_desc_base[hw_tdt];
- length += tx_desc->lower.flags.length;
- eop = tx_desc->lower.data & E1000_TXD_CMD_EOP;
- if (++hw_tdt == adapter->num_tx_desc)
- hw_tdt = 0;
-
- if (eop) {
- if (em_82547_fifo_workaround(adapter, length)) {
- adapter->tx_fifo_wrk_cnt++;
- callout_reset(&adapter->tx_fifo_timer, 1,
- em_82547_move_tail, adapter);
- break;
- }
- E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), hw_tdt);
- em_82547_update_fifo_head(adapter, length);
- length = 0;
- }
- }
-}
-
-static int
-em_82547_fifo_workaround(struct adapter *adapter, int len)
-{
- int fifo_space, fifo_pkt_len;
-
- fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR);
-
- if (adapter->link_duplex == HALF_DUPLEX) {
- fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head;
-
- if (fifo_pkt_len >= (EM_82547_PKT_THRESH + fifo_space)) {
- if (em_82547_tx_fifo_reset(adapter))
- return (0);
- else
- return (1);
- }
- }
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(txr->me), i);
return (0);
}
static void
-em_82547_update_fifo_head(struct adapter *adapter, int len)
-{
- int fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR);
-
- /* tx_fifo_head is always 16 byte aligned */
- adapter->tx_fifo_head += fifo_pkt_len;
- if (adapter->tx_fifo_head >= adapter->tx_fifo_size) {
- adapter->tx_fifo_head -= adapter->tx_fifo_size;
- }
-}
-
-
-static int
-em_82547_tx_fifo_reset(struct adapter *adapter)
-{
- u32 tctl;
-
- if ((E1000_READ_REG(&adapter->hw, E1000_TDT(0)) ==
- E1000_READ_REG(&adapter->hw, E1000_TDH(0))) &&
- (E1000_READ_REG(&adapter->hw, E1000_TDFT) ==
- E1000_READ_REG(&adapter->hw, E1000_TDFH)) &&
- (E1000_READ_REG(&adapter->hw, E1000_TDFTS) ==
- E1000_READ_REG(&adapter->hw, E1000_TDFHS)) &&
- (E1000_READ_REG(&adapter->hw, E1000_TDFPC) == 0)) {
- /* Disable TX unit */
- tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL);
- E1000_WRITE_REG(&adapter->hw, E1000_TCTL,
- tctl & ~E1000_TCTL_EN);
-
- /* Reset FIFO pointers */
- E1000_WRITE_REG(&adapter->hw, E1000_TDFT,
- adapter->tx_head_addr);
- E1000_WRITE_REG(&adapter->hw, E1000_TDFH,
- adapter->tx_head_addr);
- E1000_WRITE_REG(&adapter->hw, E1000_TDFTS,
- adapter->tx_head_addr);
- E1000_WRITE_REG(&adapter->hw, E1000_TDFHS,
- adapter->tx_head_addr);
-
- /* Re-enable TX unit */
- E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
- E1000_WRITE_FLUSH(&adapter->hw);
-
- adapter->tx_fifo_head = 0;
- adapter->tx_fifo_reset_cnt++;
-
- return (TRUE);
- }
- else {
- return (FALSE);
- }
-}
-
-static void
em_set_promisc(struct adapter *adapter)
{
struct ifnet *ifp = adapter->ifp;
@@ -2541,7 +1975,11 @@ em_set_multi(struct adapter *adapter)
if (mta == NULL)
panic("em_set_multi memory failure\n");
+#if __FreeBSD_version < 800000
+ IF_ADDR_LOCK(ifp);
+#else
if_maddr_rlock(ifp);
+#endif
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
@@ -2553,8 +1991,11 @@ em_set_multi(struct adapter *adapter)
&mta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN);
mcnt++;
}
+#if __FreeBSD_version < 800000
+ IF_ADDR_UNLOCK(ifp);
+#else
if_maddr_runlock(ifp);
-
+#endif
if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) {
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
reg_rctl |= E1000_RCTL_MPE;
@@ -2587,11 +2028,10 @@ em_local_timer(void *arg)
{
struct adapter *adapter = arg;
struct ifnet *ifp = adapter->ifp;
+ struct tx_ring *txr = adapter->tx_rings;
EM_CORE_LOCK_ASSERT(adapter);
- taskqueue_enqueue(adapter->tq,
- &adapter->rxtx_task);
em_update_link_status(adapter);
em_update_stats_counters(adapter);
@@ -2602,18 +2042,31 @@ em_local_timer(void *arg)
if (em_display_debug_stats && ifp->if_drv_flags & IFF_DRV_RUNNING)
em_print_hw_stats(adapter);
- em_smartspeed(adapter);
-
/*
- * Each second we check the watchdog to
- * protect against hardware hangs.
- */
- em_watchdog(adapter);
+ ** Check for time since any descriptor was cleaned
+ */
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ EM_TX_LOCK(txr);
+ if (txr->watchdog_check == FALSE) {
+ EM_TX_UNLOCK(txr);
+ continue;
+ }
+ if ((ticks - txr->watchdog_time) > EM_WATCHDOG)
+ goto hung;
+ EM_TX_UNLOCK(txr);
+ }
callout_reset(&adapter->timer, hz, em_local_timer, adapter);
-
+ return;
+hung:
+ device_printf(adapter->dev, "Watchdog timeout -- resetting\n");
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ adapter->watchdog_events++;
+ EM_TX_UNLOCK(txr);
+ em_init_locked(adapter);
}
+
static void
em_update_link_status(struct adapter *adapter)
{
@@ -2677,7 +2130,8 @@ em_update_link_status(struct adapter *adapter)
device_printf(dev, "Link is Down\n");
adapter->link_active = 0;
/* Link down, disable watchdog */
- adapter->watchdog_timer = FALSE;
+ // JFV change later
+ //adapter->watchdog_check = FALSE;
if_link_state_change(ifp, LINK_STATE_DOWN);
}
}
@@ -2696,22 +2150,30 @@ em_stop(void *arg)
{
struct adapter *adapter = arg;
struct ifnet *ifp = adapter->ifp;
+ struct tx_ring *txr = adapter->tx_rings;
EM_CORE_LOCK_ASSERT(adapter);
- EM_TX_LOCK_ASSERT(adapter);
INIT_DEBUGOUT("em_stop: begin");
em_disable_intr(adapter);
callout_stop(&adapter->timer);
- callout_stop(&adapter->tx_fifo_timer);
/* Tell the stack that the interface is no longer active */
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ /* Unarm watchdog timer. */
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ EM_TX_LOCK(txr);
+ txr->watchdog_check = FALSE;
+ EM_TX_UNLOCK(txr);
+ }
+
e1000_reset_hw(&adapter->hw);
- if (adapter->hw.mac.type >= e1000_82544)
- E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0);
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0);
+
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
}
@@ -2757,7 +2219,7 @@ static int
em_allocate_pci_resources(struct adapter *adapter)
{
device_t dev = adapter->dev;
- int val, rid, error = E1000_SUCCESS;
+ int rid;
rid = PCIR_BAR(0);
adapter->memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
@@ -2772,58 +2234,17 @@ em_allocate_pci_resources(struct adapter *adapter)
rman_get_bushandle(adapter->memory);
adapter->hw.hw_addr = (u8 *)&adapter->osdep.mem_bus_space_handle;
- /* Only older adapters use IO mapping */
- if ((adapter->hw.mac.type > e1000_82543) &&
- (adapter->hw.mac.type < e1000_82571)) {
- /* Figure our where our IO BAR is ? */
- for (rid = PCIR_BAR(0); rid < PCIR_CIS;) {
- val = pci_read_config(dev, rid, 4);
- if (EM_BAR_TYPE(val) == EM_BAR_TYPE_IO) {
- adapter->io_rid = rid;
- break;
- }
- rid += 4;
- /* check for 64bit BAR */
- if (EM_BAR_MEM_TYPE(val) == EM_BAR_MEM_TYPE_64BIT)
- rid += 4;
- }
- if (rid >= PCIR_CIS) {
- device_printf(dev, "Unable to locate IO BAR\n");
- return (ENXIO);
- }
- adapter->ioport = bus_alloc_resource_any(dev,
- SYS_RES_IOPORT, &adapter->io_rid, RF_ACTIVE);
- if (adapter->ioport == NULL) {
- device_printf(dev, "Unable to allocate bus resource: "
- "ioport\n");
- return (ENXIO);
- }
- adapter->hw.io_base = 0;
- adapter->osdep.io_bus_space_tag =
- rman_get_bustag(adapter->ioport);
- adapter->osdep.io_bus_space_handle =
- rman_get_bushandle(adapter->ioport);
- }
-
- /*
- ** Init the resource arrays
- ** used by MSIX setup
- */
- for (int i = 0; i < 3; i++) {
- adapter->rid[i] = i + 1; /* MSI/X RID starts at 1 */
- adapter->tag[i] = NULL;
- adapter->res[i] = NULL;
- }
+ /* Default to a single queue */
+ adapter->num_queues = 1;
/*
* Setup MSI/X or MSI if PCI Express
*/
- if (em_enable_msi)
- adapter->msi = em_setup_msix(adapter);
+ adapter->msix = em_setup_msix(adapter);
adapter->hw.back = &adapter->osdep;
- return (error);
+ return (0);
}
/*********************************************************************
@@ -2835,63 +2256,40 @@ int
em_allocate_legacy(struct adapter *adapter)
{
device_t dev = adapter->dev;
- int error;
+ int error, rid = 0;
/* Manually turn off all interrupts */
E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff);
- /* Legacy RID is 0 */
- if (adapter->msi == 0)
- adapter->rid[0] = 0;
-
+ if (adapter->msix == 1) /* using MSI */
+ rid = 1;
/* We allocate a single interrupt resource */
- adapter->res[0] = bus_alloc_resource_any(dev,
- SYS_RES_IRQ, &adapter->rid[0], RF_SHAREABLE | RF_ACTIVE);
- if (adapter->res[0] == NULL) {
+ adapter->res = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
+ if (adapter->res == NULL) {
device_printf(dev, "Unable to allocate bus resource: "
"interrupt\n");
return (ENXIO);
}
-#ifdef EM_LEGACY_IRQ
- /* We do Legacy setup */
- if ((error = bus_setup_intr(dev, adapter->res[0],
-#if __FreeBSD_version > 700000
- INTR_TYPE_NET | INTR_MPSAFE, NULL, em_intr, adapter,
-#else /* 6.X */
- INTR_TYPE_NET | INTR_MPSAFE, em_intr, adapter,
-#endif
- &adapter->tag[0])) != 0) {
- device_printf(dev, "Failed to register interrupt handler");
- return (error);
- }
-
-#else /* FAST_IRQ */
/*
- * Try allocating a fast interrupt and the associated deferred
- * processing contexts.
+ * Allocate a fast interrupt and the associated
+ * deferred processing contexts.
*/
- TASK_INIT(&adapter->rxtx_task, 0, em_handle_rxtx, adapter);
+ TASK_INIT(&adapter->que_task, 0, em_handle_que, adapter);
TASK_INIT(&adapter->link_task, 0, em_handle_link, adapter);
adapter->tq = taskqueue_create_fast("em_taskq", M_NOWAIT,
taskqueue_thread_enqueue, &adapter->tq);
taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq",
device_get_nameunit(adapter->dev));
-#if __FreeBSD_version < 700000
- if ((error = bus_setup_intr(dev, adapter->res[0],
- INTR_TYPE_NET | INTR_FAST, em_irq_fast, adapter,
-#else
- if ((error = bus_setup_intr(dev, adapter->res[0],
- INTR_TYPE_NET, em_irq_fast, NULL, adapter,
-#endif
- &adapter->tag[0])) != 0) {
+ if ((error = bus_setup_intr(dev, adapter->res, INTR_TYPE_NET,
+ em_irq_fast, NULL, adapter, &adapter->tag)) != 0) {
device_printf(dev, "Failed to register fast interrupt "
"handler: %d\n", error);
taskqueue_free(adapter->tq);
adapter->tq = NULL;
return (error);
}
-#endif /* EM_LEGACY_IRQ */
return (0);
}
@@ -2906,79 +2304,108 @@ em_allocate_legacy(struct adapter *adapter)
int
em_allocate_msix(struct adapter *adapter)
{
- device_t dev = adapter->dev;
- int error;
+ device_t dev = adapter->dev;
+ struct tx_ring *txr = adapter->tx_rings;
+ struct rx_ring *rxr = adapter->rx_rings;
+ int error, rid, vector = 0;
+
/* Make sure all interrupts are disabled */
E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff);
- /* First get the resources */
- for (int i = 0; i < adapter->msi; i++) {
- adapter->res[i] = bus_alloc_resource_any(dev,
- SYS_RES_IRQ, &adapter->rid[i], RF_ACTIVE);
- if (adapter->res[i] == NULL) {
+ /* First set up ring resources */
+ for (int i = 0; i < adapter->num_queues; i++, txr++, rxr++) {
+
+ /* RX ring */
+ rid = vector + 1;
+
+ rxr->res = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (rxr->res == NULL) {
device_printf(dev,
"Unable to allocate bus resource: "
- "MSIX Interrupt\n");
+ "RX MSIX Interrupt %d\n", i);
return (ENXIO);
}
- }
-
- /*
- * Now allocate deferred processing contexts.
- */
- TASK_INIT(&adapter->rx_task, 0, em_handle_rx, adapter);
- TASK_INIT(&adapter->tx_task, 0, em_handle_tx, adapter);
- /*
- * Handle compatibility for msi case for deferral due to
- * trylock failure
- */
- TASK_INIT(&adapter->rxtx_task, 0, em_handle_tx, adapter);
- TASK_INIT(&adapter->link_task, 0, em_handle_link, adapter);
- adapter->tq = taskqueue_create_fast("em_taskq", M_NOWAIT,
- taskqueue_thread_enqueue, &adapter->tq);
- taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq",
- device_get_nameunit(adapter->dev));
-
- /*
- * And setup the interrupt handlers
- */
-
- /* First slot to RX */
- if ((error = bus_setup_intr(dev, adapter->res[0],
-#if __FreeBSD_version > 700000
- INTR_TYPE_NET | INTR_MPSAFE, NULL, em_msix_rx, adapter,
-#else /* 6.X */
- INTR_TYPE_NET | INTR_MPSAFE, em_msix_rx, adapter,
-#endif
- &adapter->tag[0])) != 0) {
- device_printf(dev, "Failed to register RX handler");
- return (error);
- }
+ if ((error = bus_setup_intr(dev, rxr->res,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, em_msix_rx,
+ rxr, &rxr->tag)) != 0) {
+ device_printf(dev, "Failed to register RX handler");
+ return (error);
+ }
+ rxr->msix = vector++; /* NOTE increment vector for TX */
+ TASK_INIT(&rxr->rx_task, 0, em_handle_rx, rxr);
+ rxr->tq = taskqueue_create_fast("em_rxq", M_NOWAIT,
+ taskqueue_thread_enqueue, &rxr->tq);
+ taskqueue_start_threads(&rxr->tq, 1, PI_NET, "%s rxq",
+ device_get_nameunit(adapter->dev));
+ /*
+ ** Set the bit to enable interrupt
+ ** in E1000_IMS -- bits 20 and 21
+ ** are for RX0 and RX1, note this has
+ ** NOTHING to do with the MSIX vector
+ */
+ rxr->ims = 1 << (20 + i);
+ adapter->ivars |= (8 | rxr->msix) << (i * 4);
- /* Next TX */
- if ((error = bus_setup_intr(dev, adapter->res[1],
-#if __FreeBSD_version > 700000
- INTR_TYPE_NET | INTR_MPSAFE, NULL, em_msix_tx, adapter,
-#else /* 6.X */
- INTR_TYPE_NET | INTR_MPSAFE, em_msix_tx, adapter,
-#endif
- &adapter->tag[1])) != 0) {
- device_printf(dev, "Failed to register TX handler");
- return (error);
+ /* TX ring */
+ rid = vector + 1;
+ txr->res = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (txr->res == NULL) {
+ device_printf(dev,
+ "Unable to allocate bus resource: "
+ "TX MSIX Interrupt %d\n", i);
+ return (ENXIO);
+ }
+ if ((error = bus_setup_intr(dev, txr->res,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, em_msix_tx,
+ txr, &txr->tag)) != 0) {
+ device_printf(dev, "Failed to register TX handler");
+ return (error);
+ }
+ txr->msix = vector++; /* Increment vector for next pass */
+ TASK_INIT(&txr->tx_task, 0, em_handle_tx, txr);
+ txr->tq = taskqueue_create_fast("em_txq", M_NOWAIT,
+ taskqueue_thread_enqueue, &txr->tq);
+ taskqueue_start_threads(&txr->tq, 1, PI_NET, "%s txq",
+ device_get_nameunit(adapter->dev));
+ /*
+ ** Set the bit to enable interrupt
+ ** in E1000_IMS -- bits 22 and 23
+ ** are for TX0 and TX1, note this has
+ ** NOTHING to do with the MSIX vector
+ */
+ txr->ims = 1 << (22 + i);
+ adapter->ivars |= (8 | txr->msix) << (8 + (i * 4));
}
- /* And Link */
- if ((error = bus_setup_intr(dev, adapter->res[2],
-#if __FreeBSD_version > 700000
- INTR_TYPE_NET | INTR_MPSAFE, NULL, em_msix_link, adapter,
-#else /* 6.X */
- INTR_TYPE_NET | INTR_MPSAFE, em_msix_link, adapter,
-#endif
- &adapter->tag[2])) != 0) {
- device_printf(dev, "Failed to register TX handler");
+ /* Link interrupt */
+ ++rid;
+ adapter->res = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (!adapter->res) {
+ device_printf(dev,"Unable to allocate "
+ "bus resource: Link interrupt [%d]\n", rid);
+ return (ENXIO);
+ }
+ /* Set the link handler function */
+ error = bus_setup_intr(dev, adapter->res,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL,
+ em_msix_link, adapter, &adapter->tag);
+ if (error) {
+ adapter->res = NULL;
+ device_printf(dev, "Failed to register LINK handler");
return (error);
}
+ adapter->linkvec = vector;
+ adapter->ivars |= (8 | vector) << 16;
+ adapter->ivars |= 0x80000000;
+ TASK_INIT(&adapter->link_task, 0, em_handle_link, adapter);
+ adapter->tq = taskqueue_create_fast("em_link", M_NOWAIT,
+ taskqueue_thread_enqueue, &adapter->tq);
+ taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s linkq",
+ device_get_nameunit(adapter->dev));
return (0);
}
@@ -2987,36 +2414,56 @@ em_allocate_msix(struct adapter *adapter)
static void
em_free_pci_resources(struct adapter *adapter)
{
- device_t dev = adapter->dev;
+ device_t dev = adapter->dev;
+ struct tx_ring *txr;
+ struct rx_ring *rxr;
+ int rid;
- /* Make sure the for loop below runs once */
- if (adapter->msi == 0)
- adapter->msi = 1;
/*
- * First release all the interrupt resources:
- * notice that since these are just kept
- * in an array we can do the same logic
- * whether its MSIX or just legacy.
- */
- for (int i = 0; i < adapter->msi; i++) {
- if (adapter->tag[i] != NULL) {
- bus_teardown_intr(dev, adapter->res[i],
- adapter->tag[i]);
- adapter->tag[i] = NULL;
+ ** Release all the queue interrupt resources:
+ */
+ for (int i = 0; i < adapter->num_queues; i++) {
+ txr = &adapter->tx_rings[i];
+ rxr = &adapter->rx_rings[i];
+ rid = txr->msix +1;
+ if (txr->tag != NULL) {
+ bus_teardown_intr(dev, txr->res, txr->tag);
+ txr->tag = NULL;
}
- if (adapter->res[i] != NULL) {
+ if (txr->res != NULL)
bus_release_resource(dev, SYS_RES_IRQ,
- adapter->rid[i], adapter->res[i]);
+ rid, txr->res);
+ rid = rxr->msix +1;
+ if (rxr->tag != NULL) {
+ bus_teardown_intr(dev, rxr->res, rxr->tag);
+ rxr->tag = NULL;
}
+ if (rxr->res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rid, rxr->res);
}
- if (adapter->msi)
+ if (adapter->linkvec) /* we are doing MSIX */
+ rid = adapter->linkvec + 1;
+ else
+ (adapter->msix != 0) ? (rid = 1):(rid = 0);
+
+ if (adapter->tag != NULL) {
+ bus_teardown_intr(dev, adapter->res, adapter->tag);
+ adapter->tag = NULL;
+ }
+
+ if (adapter->res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, rid, adapter->res);
+
+
+ if (adapter->msix)
pci_release_msi(dev);
- if (adapter->msix != NULL)
+ if (adapter->msix_mem != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
- PCIR_BAR(EM_MSIX_BAR), adapter->msix);
+ PCIR_BAR(EM_MSIX_BAR), adapter->msix_mem);
if (adapter->memory != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
@@ -3025,10 +2472,6 @@ em_free_pci_resources(struct adapter *adapter)
if (adapter->flash != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
EM_FLASH, adapter->flash);
-
- if (adapter->ioport != NULL)
- bus_release_resource(dev, SYS_RES_IOPORT,
- adapter->io_rid, adapter->ioport);
}
/*
@@ -3040,82 +2483,81 @@ em_setup_msix(struct adapter *adapter)
device_t dev = adapter->dev;
int val = 0;
- if (adapter->hw.mac.type < e1000_82571)
- return (0);
/* Setup MSI/X for Hartwell */
- if (adapter->hw.mac.type == e1000_82574) {
+ if ((adapter->hw.mac.type == e1000_82574) &&
+ (em_enable_msix == TRUE)) {
/* Map the MSIX BAR */
int rid = PCIR_BAR(EM_MSIX_BAR);
- adapter->msix = bus_alloc_resource_any(dev,
+ adapter->msix_mem = bus_alloc_resource_any(dev,
SYS_RES_MEMORY, &rid, RF_ACTIVE);
- if (!adapter->msix) {
+ if (!adapter->msix_mem) {
/* May not be enabled */
device_printf(adapter->dev,
"Unable to map MSIX table \n");
goto msi;
}
val = pci_msix_count(dev);
- /*
- ** 82574 can be configured for 5 but
- ** we limit use to 3.
- */
- if (val > 3) val = 3;
- if ((val) && pci_alloc_msix(dev, &val) == 0) {
- device_printf(adapter->dev,"Using MSIX interrupts\n");
- return (val);
+ if (val != 5) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ PCIR_BAR(EM_MSIX_BAR), adapter->msix_mem);
+ adapter->msix_mem = NULL;
+ device_printf(adapter->dev,
+ "MSIX vectors wrong, using MSI \n");
+ goto msi;
}
+ if (em_msix_queues == 2) {
+ val = 5;
+ adapter->num_queues = 2;
+ } else {
+ val = 3;
+ adapter->num_queues = 1;
+ }
+ if (pci_alloc_msix(dev, &val) == 0) {
+ device_printf(adapter->dev,
+ "Using MSIX interrupts "
+ "with %d vectors\n", val);
+ }
+
+ return (val);
}
msi:
val = pci_msi_count(dev);
if (val == 1 && pci_alloc_msi(dev, &val) == 0) {
- adapter->msi = 1;
+ adapter->msix = 1;
device_printf(adapter->dev,"Using MSI interrupt\n");
return (val);
}
+ /* Should only happen due to manual invention */
+ device_printf(adapter->dev,"Setup MSIX failure\n");
return (0);
}
+
/*********************************************************************
*
* Initialize the hardware to a configuration
* as specified by the adapter structure.
*
**********************************************************************/
-static int
-em_hardware_init(struct adapter *adapter)
+static void
+em_reset(struct adapter *adapter)
{
- device_t dev = adapter->dev;
- u16 rx_buffer_size;
-
- INIT_DEBUGOUT("em_hardware_init: begin");
-
- /* Issue a global reset */
- e1000_reset_hw(&adapter->hw);
-
- /* Get control from any management/hw control */
- if (((adapter->hw.mac.type == e1000_82573) ||
- (adapter->hw.mac.type == e1000_82583) ||
- (adapter->hw.mac.type == e1000_ich8lan) ||
- (adapter->hw.mac.type == e1000_ich10lan) ||
- (adapter->hw.mac.type == e1000_ich9lan)) &&
- e1000_check_mng_mode(&adapter->hw))
- em_get_hw_control(adapter);
+ device_t dev = adapter->dev;
+ struct e1000_hw *hw = &adapter->hw;
+ u16 rx_buffer_size;
- /* When hardware is reset, fifo_head is also reset */
- adapter->tx_fifo_head = 0;
+ INIT_DEBUGOUT("em_reset: begin");
/* Set up smart power down as default off on newer adapters. */
- if (!em_smart_pwr_down && (adapter->hw.mac.type == e1000_82571 ||
- adapter->hw.mac.type == e1000_82572)) {
+ if (!em_smart_pwr_down && (hw->mac.type == e1000_82571 ||
+ hw->mac.type == e1000_82572)) {
u16 phy_tmp = 0;
/* Speed up time to link by disabling smart power down. */
- e1000_read_phy_reg(&adapter->hw,
- IGP02E1000_PHY_POWER_MGMT, &phy_tmp);
+ e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_tmp);
phy_tmp &= ~IGP02E1000_PM_SPD;
- e1000_write_phy_reg(&adapter->hw,
- IGP02E1000_PHY_POWER_MGMT, phy_tmp);
+ e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_tmp);
}
/*
@@ -3132,34 +2574,42 @@ em_hardware_init(struct adapter *adapter)
* by 1500.
* - The pause time is fairly large at 1000 x 512ns = 512 usec.
*/
- rx_buffer_size = ((E1000_READ_REG(&adapter->hw, E1000_PBA) &
- 0xffff) << 10 );
+ rx_buffer_size = ((E1000_READ_REG(hw, E1000_PBA) & 0xffff) << 10 );
- adapter->hw.fc.high_water = rx_buffer_size -
+ hw->fc.high_water = rx_buffer_size -
roundup2(adapter->max_frame_size, 1024);
- adapter->hw.fc.low_water = adapter->hw.fc.high_water - 1500;
+ hw->fc.low_water = hw->fc.high_water - 1500;
- if (adapter->hw.mac.type == e1000_80003es2lan)
- adapter->hw.fc.pause_time = 0xFFFF;
+ if (hw->mac.type == e1000_80003es2lan)
+ hw->fc.pause_time = 0xFFFF;
else
- adapter->hw.fc.pause_time = EM_FC_PAUSE_TIME;
- adapter->hw.fc.send_xon = TRUE;
+ hw->fc.pause_time = EM_FC_PAUSE_TIME;
+
+ hw->fc.send_xon = TRUE;
/* Set Flow control, use the tunable location if sane */
if ((em_fc_setting >= 0) || (em_fc_setting < 4))
- adapter->hw.fc.requested_mode = em_fc_setting;
- else
- adapter->hw.fc.requested_mode = e1000_fc_none;
+ hw->fc.requested_mode = em_fc_setting;
+ else
+ hw->fc.requested_mode = e1000_fc_none;
+
+ /* Override - workaround for PCHLAN issue */
+ if (hw->mac.type == e1000_pchlan)
+ hw->fc.requested_mode = e1000_fc_rx_pause;
+ /* Issue a global reset */
+ e1000_reset_hw(hw);
+ E1000_WRITE_REG(hw, E1000_WUC, 0);
- if (e1000_init_hw(&adapter->hw) < 0) {
+ if (e1000_init_hw(hw) < 0) {
device_printf(dev, "Hardware Initialization Failed\n");
- return (EIO);
+ return;
}
- e1000_check_for_link(&adapter->hw);
-
- return (0);
+ E1000_WRITE_REG(hw, E1000_VET, ETHERTYPE_VLAN);
+ e1000_get_phy_info(hw);
+ e1000_check_for_link(hw);
+ return;
}
/*********************************************************************
@@ -3196,31 +2646,14 @@ em_setup_interface(device_t dev, struct adapter *adapter)
/* Multiqueue tx functions */
ifp->if_transmit = em_mq_start;
ifp->if_qflush = em_qflush;
- adapter->br = buf_ring_alloc(4096, M_DEVBUF, M_WAITOK, &adapter->tx_mtx);
#endif
- if (adapter->hw.mac.type >= e1000_82543) {
- int version_cap;
-#if __FreeBSD_version < 700000
- version_cap = IFCAP_HWCSUM;
-#else
- version_cap = IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM;
-#endif
- ifp->if_capabilities |= version_cap;
- ifp->if_capenable |= version_cap;
- }
-#if __FreeBSD_version >= 700000
- /* Identify TSO capable adapters */
- if ((adapter->hw.mac.type > e1000_82544) &&
- (adapter->hw.mac.type != e1000_82547))
- ifp->if_capabilities |= IFCAP_TSO4;
- /*
- * By default only enable on PCI-E, this
- * can be overriden by ifconfig.
- */
- if (adapter->hw.mac.type >= e1000_82571)
- ifp->if_capenable |= IFCAP_TSO4;
-#endif
+ ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM;
+ ifp->if_capenable |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM;
+
+ /* Enable TSO by default, can disable with ifconfig */
+ ifp->if_capabilities |= IFCAP_TSO4;
+ ifp->if_capenable |= IFCAP_TSO4;
/*
* Tell the upper layer(s) we support long frames.
@@ -3233,6 +2666,12 @@ em_setup_interface(device_t dev, struct adapter *adapter)
ifp->if_capabilities |= IFCAP_POLLING;
#endif
+ /* Enable All WOL methods by default */
+ if (adapter->wol) {
+ ifp->if_capabilities |= IFCAP_WOL;
+ ifp->if_capenable |= IFCAP_WOL;
+ }
+
/*
* Specify the media types supported by this adapter and register
* callbacks to update media and link information
@@ -3243,8 +2682,6 @@ em_setup_interface(device_t dev, struct adapter *adapter)
(adapter->hw.phy.media_type == e1000_media_type_internal_serdes)) {
u_char fiber_type = IFM_1000_SX; /* default type */
- if (adapter->hw.mac.type == e1000_82545)
- fiber_type = IFM_1000_LX;
ifmedia_add(&adapter->media, IFM_ETHER | fiber_type | IFM_FDX,
0, NULL);
ifmedia_add(&adapter->media, IFM_ETHER | fiber_type, 0, NULL);
@@ -3268,67 +2705,6 @@ em_setup_interface(device_t dev, struct adapter *adapter)
}
-/*********************************************************************
- *
- * Workaround for SmartSpeed on 82541 and 82547 controllers
- *
- **********************************************************************/
-static void
-em_smartspeed(struct adapter *adapter)
-{
- u16 phy_tmp;
-
- if (adapter->link_active || (adapter->hw.phy.type != e1000_phy_igp) ||
- adapter->hw.mac.autoneg == 0 ||
- (adapter->hw.phy.autoneg_advertised & ADVERTISE_1000_FULL) == 0)
- return;
-
- if (adapter->smartspeed == 0) {
- /* If Master/Slave config fault is asserted twice,
- * we assume back-to-back */
- e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp);
- if (!(phy_tmp & SR_1000T_MS_CONFIG_FAULT))
- return;
- e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp);
- if (phy_tmp & SR_1000T_MS_CONFIG_FAULT) {
- e1000_read_phy_reg(&adapter->hw,
- PHY_1000T_CTRL, &phy_tmp);
- if(phy_tmp & CR_1000T_MS_ENABLE) {
- phy_tmp &= ~CR_1000T_MS_ENABLE;
- e1000_write_phy_reg(&adapter->hw,
- PHY_1000T_CTRL, phy_tmp);
- adapter->smartspeed++;
- if(adapter->hw.mac.autoneg &&
- !e1000_phy_setup_autoneg(&adapter->hw) &&
- !e1000_read_phy_reg(&adapter->hw,
- PHY_CONTROL, &phy_tmp)) {
- phy_tmp |= (MII_CR_AUTO_NEG_EN |
- MII_CR_RESTART_AUTO_NEG);
- e1000_write_phy_reg(&adapter->hw,
- PHY_CONTROL, phy_tmp);
- }
- }
- }
- return;
- } else if(adapter->smartspeed == EM_SMARTSPEED_DOWNSHIFT) {
- /* If still no link, perhaps using 2/3 pair cable */
- e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp);
- phy_tmp |= CR_1000T_MS_ENABLE;
- e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_tmp);
- if(adapter->hw.mac.autoneg &&
- !e1000_phy_setup_autoneg(&adapter->hw) &&
- !e1000_read_phy_reg(&adapter->hw, PHY_CONTROL, &phy_tmp)) {
- phy_tmp |= (MII_CR_AUTO_NEG_EN |
- MII_CR_RESTART_AUTO_NEG);
- e1000_write_phy_reg(&adapter->hw, PHY_CONTROL, phy_tmp);
- }
- }
- /* Restart process after EM_SMARTSPEED_MAX iterations */
- if(adapter->smartspeed++ == EM_SMARTSPEED_MAX)
- adapter->smartspeed = 0;
-}
-
-
/*
* Manage DMA'able memory.
*/
@@ -3346,11 +2722,7 @@ em_dma_malloc(struct adapter *adapter, bus_size_t size,
{
int error;
-#if __FreeBSD_version >= 700000
error = bus_dma_tag_create(bus_get_dma_tag(adapter->dev), /* parent */
-#else
- error = bus_dma_tag_create(NULL, /* parent */
-#endif
EM_DBA_ALIGN, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
@@ -3421,97 +2793,243 @@ em_dma_free(struct adapter *adapter, struct em_dma_alloc *dma)
/*********************************************************************
*
+ * Allocate memory for the transmit and receive rings, and then
+ * the descriptors associated with each, called only once at attach.
+ *
+ **********************************************************************/
+static int
+em_allocate_queues(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ struct tx_ring *txr = NULL;
+ struct rx_ring *rxr = NULL;
+ int rsize, tsize, error = E1000_SUCCESS;
+ int txconf = 0, rxconf = 0;
+
+
+ /* Allocate the TX ring struct memory */
+ if (!(adapter->tx_rings =
+ (struct tx_ring *) malloc(sizeof(struct tx_ring) *
+ adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate TX ring memory\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ /* Now allocate the RX */
+ if (!(adapter->rx_rings =
+ (struct rx_ring *) malloc(sizeof(struct rx_ring) *
+ adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate RX ring memory\n");
+ error = ENOMEM;
+ goto rx_fail;
+ }
+
+ tsize = roundup2(adapter->num_tx_desc *
+ sizeof(struct e1000_tx_desc), EM_DBA_ALIGN);
+ /*
+ * Now set up the TX queues, txconf is needed to handle the
+ * possibility that things fail midcourse and we need to
+ * undo memory gracefully
+ */
+ for (int i = 0; i < adapter->num_queues; i++, txconf++) {
+ /* Set up some basics */
+ txr = &adapter->tx_rings[i];
+ txr->adapter = adapter;
+ txr->me = i;
+
+ /* Initialize the TX lock */
+ snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)",
+ device_get_nameunit(dev), txr->me);
+ mtx_init(&txr->tx_mtx, txr->mtx_name, NULL, MTX_DEF);
+
+ if (em_dma_malloc(adapter, tsize,
+ &txr->txdma, BUS_DMA_NOWAIT)) {
+ device_printf(dev,
+ "Unable to allocate TX Descriptor memory\n");
+ error = ENOMEM;
+ goto err_tx_desc;
+ }
+ txr->tx_base = (struct e1000_tx_desc *)txr->txdma.dma_vaddr;
+ bzero((void *)txr->tx_base, tsize);
+
+ if (em_allocate_transmit_buffers(txr)) {
+ device_printf(dev,
+ "Critical Failure setting up transmit buffers\n");
+ error = ENOMEM;
+ goto err_tx_desc;
+ }
+#if __FreeBSD_version >= 800000
+ /* Allocate a buf ring */
+ txr->br = buf_ring_alloc(4096, M_DEVBUF,
+ M_WAITOK, &txr->tx_mtx);
+#endif
+ }
+
+ /*
+ * Next the RX queues...
+ */
+ rsize = roundup2(adapter->num_rx_desc *
+ sizeof(struct e1000_rx_desc), EM_DBA_ALIGN);
+ for (int i = 0; i < adapter->num_queues; i++, rxconf++) {
+ rxr = &adapter->rx_rings[i];
+ rxr->adapter = adapter;
+ rxr->me = i;
+
+ /* Initialize the RX lock */
+ snprintf(rxr->mtx_name, sizeof(rxr->mtx_name), "%s:rx(%d)",
+ device_get_nameunit(dev), txr->me);
+ mtx_init(&rxr->rx_mtx, rxr->mtx_name, NULL, MTX_DEF);
+
+ if (em_dma_malloc(adapter, rsize,
+ &rxr->rxdma, BUS_DMA_NOWAIT)) {
+ device_printf(dev,
+ "Unable to allocate RxDescriptor memory\n");
+ error = ENOMEM;
+ goto err_rx_desc;
+ }
+ rxr->rx_base = (struct e1000_rx_desc *)rxr->rxdma.dma_vaddr;
+ bzero((void *)rxr->rx_base, rsize);
+
+ /* Allocate receive buffers for the ring*/
+ if (em_allocate_receive_buffers(rxr)) {
+ device_printf(dev,
+ "Critical Failure setting up receive buffers\n");
+ error = ENOMEM;
+ goto err_rx_desc;
+ }
+ }
+
+ return (0);
+
+err_rx_desc:
+ for (rxr = adapter->rx_rings; rxconf > 0; rxr++, rxconf--)
+ em_dma_free(adapter, &rxr->rxdma);
+err_tx_desc:
+ for (txr = adapter->tx_rings; txconf > 0; txr++, txconf--)
+ em_dma_free(adapter, &txr->txdma);
+ free(adapter->rx_rings, M_DEVBUF);
+rx_fail:
+ buf_ring_free(txr->br, M_DEVBUF);
+ free(adapter->tx_rings, M_DEVBUF);
+fail:
+ return (error);
+}
+
+
+/*********************************************************************
+ *
* Allocate memory for tx_buffer structures. The tx_buffer stores all
- * the information needed to transmit a packet on the wire.
+ * the information needed to transmit a packet on the wire. This is
+ * called only once at attach, setup is done every reset.
*
**********************************************************************/
static int
-em_allocate_transmit_structures(struct adapter *adapter)
+em_allocate_transmit_buffers(struct tx_ring *txr)
{
+ struct adapter *adapter = txr->adapter;
device_t dev = adapter->dev;
- struct em_buffer *tx_buffer;
- int error;
+ struct em_buffer *txbuf;
+ int error, i;
/*
- * Create DMA tags for tx descriptors
+ * Setup DMA descriptor areas.
*/
-#if __FreeBSD_version >= 700000
- if ((error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
-#else
- if ((error = bus_dma_tag_create(NULL, /* parent */
-#endif
- 1, 0, /* alignment, bounds */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- EM_TSO_SIZE, /* maxsize */
- EM_MAX_SCATTER, /* nsegments */
- EM_TSO_SEG_SIZE, /* maxsegsize */
- 0, /* flags */
- NULL, /* lockfunc */
- NULL, /* lockarg */
- &adapter->txtag)) != 0) {
- device_printf(dev, "Unable to allocate TX DMA tag\n");
+ if ((error = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ EM_TSO_SIZE, /* maxsize */
+ EM_MAX_SCATTER, /* nsegments */
+ PAGE_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &txr->txtag))) {
+ device_printf(dev,"Unable to allocate TX DMA tag\n");
goto fail;
}
- adapter->tx_buffer_area = malloc(sizeof(struct em_buffer) *
- adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO);
- if (adapter->tx_buffer_area == NULL) {
+ if (!(txr->tx_buffers =
+ (struct em_buffer *) malloc(sizeof(struct em_buffer) *
+ adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO))) {
device_printf(dev, "Unable to allocate tx_buffer memory\n");
error = ENOMEM;
goto fail;
}
- /* Create the descriptor buffer dma maps */
- for (int i = 0; i < adapter->num_tx_desc; i++) {
- tx_buffer = &adapter->tx_buffer_area[i];
- error = bus_dmamap_create(adapter->txtag, 0, &tx_buffer->map);
+ /* Create the descriptor buffer dma maps */
+ txbuf = txr->tx_buffers;
+ for (i = 0; i < adapter->num_tx_desc; i++, txbuf++) {
+ error = bus_dmamap_create(txr->txtag, 0, &txbuf->map);
if (error != 0) {
device_printf(dev, "Unable to create TX DMA map\n");
goto fail;
}
- tx_buffer->next_eop = -1;
}
- return (0);
+ return 0;
fail:
+ /* We free all, it handles case where we are in the middle */
em_free_transmit_structures(adapter);
return (error);
}
/*********************************************************************
*
- * (Re)Initialize transmit structures.
+ * Initialize a transmit ring.
*
**********************************************************************/
static void
-em_setup_transmit_structures(struct adapter *adapter)
+em_setup_transmit_ring(struct tx_ring *txr)
{
- struct em_buffer *tx_buffer;
+ struct adapter *adapter = txr->adapter;
+ struct em_buffer *txbuf;
+ int i;
- /* Clear the old ring contents */
- bzero(adapter->tx_desc_base,
- (sizeof(struct e1000_tx_desc)) * adapter->num_tx_desc);
+ /* Clear the old descriptor contents */
+ EM_TX_LOCK(txr);
+ bzero((void *)txr->tx_base,
+ (sizeof(struct e1000_tx_desc)) * adapter->num_tx_desc);
+ /* Reset indices */
+ txr->next_avail_desc = 0;
+ txr->next_to_clean = 0;
- /* Free any existing TX buffers */
- for (int i = 0; i < adapter->num_tx_desc; i++, tx_buffer++) {
- tx_buffer = &adapter->tx_buffer_area[i];
- bus_dmamap_sync(adapter->txtag, tx_buffer->map,
- BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(adapter->txtag, tx_buffer->map);
- m_freem(tx_buffer->m_head);
- tx_buffer->m_head = NULL;
- tx_buffer->next_eop = -1;
- }
+ /* Free any existing tx buffers. */
+ txbuf = txr->tx_buffers;
+ for (i = 0; i < adapter->num_tx_desc; i++, txbuf++) {
+ if (txbuf->m_head != NULL) {
+ bus_dmamap_sync(txr->txtag, txbuf->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txr->txtag, txbuf->map);
+ m_freem(txbuf->m_head);
+ txbuf->m_head = NULL;
+ }
+ /* clear the watch index */
+ txbuf->next_eop = -1;
+ }
- /* Reset state */
- adapter->next_avail_tx_desc = 0;
- adapter->next_tx_to_clean = 0;
- adapter->num_tx_desc_avail = adapter->num_tx_desc;
+ /* Set number of descriptors available */
+ txr->tx_avail = adapter->num_tx_desc;
- bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ EM_TX_UNLOCK(txr);
+}
+
+/*********************************************************************
+ *
+ * Initialize all transmit rings.
+ *
+ **********************************************************************/
+static void
+em_setup_transmit_structures(struct adapter *adapter)
+{
+ struct tx_ring *txr = adapter->tx_rings;
+
+ for (int i = 0; i < adapter->num_queues; i++, txr++)
+ em_setup_transmit_ring(txr);
return;
}
@@ -3524,25 +3042,31 @@ em_setup_transmit_structures(struct adapter *adapter)
static void
em_initialize_transmit_unit(struct adapter *adapter)
{
+ struct tx_ring *txr = adapter->tx_rings;
+ struct e1000_hw *hw = &adapter->hw;
u32 tctl, tarc, tipg = 0;
- u64 bus_addr;
INIT_DEBUGOUT("em_initialize_transmit_unit: begin");
- /* Setup the Base and Length of the Tx Descriptor Ring */
- bus_addr = adapter->txdma.dma_paddr;
- E1000_WRITE_REG(&adapter->hw, E1000_TDLEN(0),
- adapter->num_tx_desc * sizeof(struct e1000_tx_desc));
- E1000_WRITE_REG(&adapter->hw, E1000_TDBAH(0),
- (u32)(bus_addr >> 32));
- E1000_WRITE_REG(&adapter->hw, E1000_TDBAL(0),
- (u32)bus_addr);
- /* Setup the HW Tx Head and Tail descriptor pointers */
- E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), 0);
- E1000_WRITE_REG(&adapter->hw, E1000_TDH(0), 0);
- HW_DEBUGOUT2("Base = %x, Length = %x\n",
- E1000_READ_REG(&adapter->hw, E1000_TDBAL(0)),
- E1000_READ_REG(&adapter->hw, E1000_TDLEN(0)));
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ u64 bus_addr = txr->txdma.dma_paddr;
+ /* Base and Len of TX Ring */
+ E1000_WRITE_REG(hw, E1000_TDLEN(i),
+ adapter->num_tx_desc * sizeof(struct e1000_tx_desc));
+ E1000_WRITE_REG(hw, E1000_TDBAH(i),
+ (u32)(bus_addr >> 32));
+ E1000_WRITE_REG(hw, E1000_TDBAL(i),
+ (u32)bus_addr);
+ /* Init the HEAD/TAIL indices */
+ E1000_WRITE_REG(hw, E1000_TDT(i), 0);
+ E1000_WRITE_REG(hw, E1000_TDH(i), 0);
+
+ HW_DEBUGOUT2("Base = %x, Length = %x\n",
+ E1000_READ_REG(&adapter->hw, E1000_TDBAL(i)),
+ E1000_READ_REG(&adapter->hw, E1000_TDLEN(i)));
+
+ txr->watchdog_check = FALSE;
+ }
/* Set the default values for the Tx Inter Packet Gap timer */
switch (adapter->hw.mac.type) {
@@ -3569,6 +3093,7 @@ em_initialize_transmit_unit(struct adapter *adapter)
E1000_WRITE_REG(&adapter->hw, E1000_TIPG, tipg);
E1000_WRITE_REG(&adapter->hw, E1000_TIDV, adapter->tx_int_delay.value);
+
if(adapter->hw.mac.type >= e1000_82540)
E1000_WRITE_REG(&adapter->hw, E1000_TADV,
adapter->tx_abs_int_delay.value);
@@ -3587,6 +3112,10 @@ em_initialize_transmit_unit(struct adapter *adapter)
E1000_WRITE_REG(&adapter->hw, E1000_TARC(1), tarc);
}
+ adapter->txd_cmd = E1000_TXD_CMD_IFCS;
+ if (adapter->tx_int_delay.value > 0)
+ adapter->txd_cmd |= E1000_TXD_CMD_IDE;
+
/* Program the Transmit Control Register */
tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL);
tctl &= ~E1000_TCTL_CT;
@@ -3599,59 +3128,84 @@ em_initialize_transmit_unit(struct adapter *adapter)
/* This write will effectively turn on the transmit unit. */
E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
- /* Setup Transmit Descriptor Base Settings */
- adapter->txd_cmd = E1000_TXD_CMD_IFCS;
-
- if (adapter->tx_int_delay.value > 0)
- adapter->txd_cmd |= E1000_TXD_CMD_IDE;
}
+
/*********************************************************************
*
- * Free all transmit related data structures.
+ * Free all transmit rings.
*
**********************************************************************/
static void
em_free_transmit_structures(struct adapter *adapter)
{
- struct em_buffer *tx_buffer;
+ struct tx_ring *txr = adapter->tx_rings;
- INIT_DEBUGOUT("free_transmit_structures: begin");
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ EM_TX_LOCK(txr);
+ em_free_transmit_buffers(txr);
+ em_dma_free(adapter, &txr->txdma);
+ EM_TX_UNLOCK(txr);
+ EM_TX_LOCK_DESTROY(txr);
+ }
- if (adapter->tx_buffer_area != NULL) {
- for (int i = 0; i < adapter->num_tx_desc; i++) {
- tx_buffer = &adapter->tx_buffer_area[i];
- if (tx_buffer->m_head != NULL) {
- bus_dmamap_sync(adapter->txtag, tx_buffer->map,
- BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(adapter->txtag,
- tx_buffer->map);
- m_freem(tx_buffer->m_head);
- tx_buffer->m_head = NULL;
- } else if (tx_buffer->map != NULL)
- bus_dmamap_unload(adapter->txtag,
- tx_buffer->map);
- if (tx_buffer->map != NULL) {
- bus_dmamap_destroy(adapter->txtag,
- tx_buffer->map);
- tx_buffer->map = NULL;
+ free(adapter->tx_rings, M_DEVBUF);
+}
+
+/*********************************************************************
+ *
+ * Free transmit ring related data structures.
+ *
+ **********************************************************************/
+static void
+em_free_transmit_buffers(struct tx_ring *txr)
+{
+ struct adapter *adapter = txr->adapter;
+ struct em_buffer *txbuf;
+
+ INIT_DEBUGOUT("free_transmit_ring: begin");
+
+ if (txr->tx_buffers == NULL)
+ return;
+
+ for (int i = 0; i < adapter->num_tx_desc; i++) {
+ txbuf = &txr->tx_buffers[i];
+ if (txbuf->m_head != NULL) {
+ bus_dmamap_sync(txr->txtag, txbuf->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txr->txtag,
+ txbuf->map);
+ m_freem(txbuf->m_head);
+ txbuf->m_head = NULL;
+ if (txbuf->map != NULL) {
+ bus_dmamap_destroy(txr->txtag,
+ txbuf->map);
+ txbuf->map = NULL;
}
+ } else if (txbuf->map != NULL) {
+ bus_dmamap_unload(txr->txtag,
+ txbuf->map);
+ bus_dmamap_destroy(txr->txtag,
+ txbuf->map);
+ txbuf->map = NULL;
}
}
- if (adapter->tx_buffer_area != NULL) {
- free(adapter->tx_buffer_area, M_DEVBUF);
- adapter->tx_buffer_area = NULL;
- }
- if (adapter->txtag != NULL) {
- bus_dma_tag_destroy(adapter->txtag);
- adapter->txtag = NULL;
- }
#if __FreeBSD_version >= 800000
- if (adapter->br != NULL)
- buf_ring_free(adapter->br, M_DEVBUF);
+ if (txr->br != NULL)
+ buf_ring_free(txr->br, M_DEVBUF);
#endif
+ if (txr->tx_buffers != NULL) {
+ free(txr->tx_buffers, M_DEVBUF);
+ txr->tx_buffers = NULL;
+ }
+ if (txr->txtag != NULL) {
+ bus_dma_tag_destroy(txr->txtag);
+ txr->txtag = NULL;
+ }
+ return;
}
+
/*********************************************************************
*
* The offload context needs to be set when we transfer the first
@@ -3663,22 +3217,23 @@ em_free_transmit_structures(struct adapter *adapter)
* big performance win. -jfv
**********************************************************************/
static void
-em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
+em_transmit_checksum_setup(struct tx_ring *txr, struct mbuf *mp,
u32 *txd_upper, u32 *txd_lower)
{
- struct e1000_context_desc *TXD = NULL;
+ struct adapter *adapter = txr->adapter;
+ struct e1000_context_desc *TXD = NULL;
struct em_buffer *tx_buffer;
struct ether_vlan_header *eh;
struct ip *ip = NULL;
struct ip6_hdr *ip6;
- int curr_txd, ehdrlen;
+ int cur, ehdrlen;
u32 cmd, hdr_len, ip_hlen;
u16 etype;
u8 ipproto;
cmd = hdr_len = ipproto = 0;
- curr_txd = adapter->next_avail_tx_desc;
+ cur = txr->next_avail_desc;
/*
* Determine where frame payload starts.
@@ -3711,7 +3266,7 @@ em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
* Offset of place to put the checksum.
*/
TXD = (struct e1000_context_desc *)
- &adapter->tx_desc_base[curr_txd];
+ &txr->tx_base[cur];
TXD->lower_setup.ip_fields.ipcss = ehdrlen;
TXD->lower_setup.ip_fields.ipcse =
htole16(ehdrlen + ip_hlen);
@@ -3753,16 +3308,16 @@ em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
*txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
*txd_upper |= E1000_TXD_POPTS_TXSM << 8;
/* no need for context if already set */
- if (adapter->last_hw_offload == CSUM_TCP)
+ if (txr->last_hw_offload == CSUM_TCP)
return;
- adapter->last_hw_offload = CSUM_TCP;
+ txr->last_hw_offload = CSUM_TCP;
/*
* Start offset for payload checksum calculation.
* End offset for payload checksum calculation.
* Offset of place to put the checksum.
*/
TXD = (struct e1000_context_desc *)
- &adapter->tx_desc_base[curr_txd];
+ &txr->tx_base[cur];
TXD->upper_setup.tcp_fields.tucss = hdr_len;
TXD->upper_setup.tcp_fields.tucse = htole16(0);
TXD->upper_setup.tcp_fields.tucso =
@@ -3776,16 +3331,16 @@ em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
*txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
*txd_upper |= E1000_TXD_POPTS_TXSM << 8;
/* no need for context if already set */
- if (adapter->last_hw_offload == CSUM_UDP)
+ if (txr->last_hw_offload == CSUM_UDP)
return;
- adapter->last_hw_offload = CSUM_UDP;
+ txr->last_hw_offload = CSUM_UDP;
/*
* Start offset for header checksum calculation.
* End offset for header checksum calculation.
* Offset of place to put the checksum.
*/
TXD = (struct e1000_context_desc *)
- &adapter->tx_desc_base[curr_txd];
+ &txr->tx_base[cur];
TXD->upper_setup.tcp_fields.tucss = hdr_len;
TXD->upper_setup.tcp_fields.tucse = htole16(0);
TXD->upper_setup.tcp_fields.tucso =
@@ -3800,35 +3355,35 @@ em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
TXD->tcp_seg_setup.data = htole32(0);
TXD->cmd_and_length =
htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT | cmd);
- tx_buffer = &adapter->tx_buffer_area[curr_txd];
+ tx_buffer = &txr->tx_buffers[cur];
tx_buffer->m_head = NULL;
tx_buffer->next_eop = -1;
- if (++curr_txd == adapter->num_tx_desc)
- curr_txd = 0;
+ if (++cur == adapter->num_tx_desc)
+ cur = 0;
- adapter->num_tx_desc_avail--;
- adapter->next_avail_tx_desc = curr_txd;
+ txr->tx_avail--;
+ txr->next_avail_desc = cur;
}
-#if __FreeBSD_version >= 700000
/**********************************************************************
*
* Setup work for hardware segmentation offload (TSO)
*
**********************************************************************/
static bool
-em_tso_setup(struct adapter *adapter, struct mbuf *mp, u32 *txd_upper,
+em_tso_setup(struct tx_ring *txr, struct mbuf *mp, u32 *txd_upper,
u32 *txd_lower)
{
- struct e1000_context_desc *TXD;
- struct em_buffer *tx_buffer;
- struct ether_vlan_header *eh;
- struct ip *ip;
- struct ip6_hdr *ip6;
- struct tcphdr *th;
- int curr_txd, ehdrlen, hdr_len, ip_hlen, isip6;
+ struct adapter *adapter = txr->adapter;
+ struct e1000_context_desc *TXD;
+ struct em_buffer *tx_buffer;
+ struct ether_vlan_header *eh;
+ struct ip *ip;
+ struct ip6_hdr *ip6;
+ struct tcphdr *th;
+ int cur, ehdrlen, hdr_len, ip_hlen, isip6;
u16 etype;
/*
@@ -3908,9 +3463,9 @@ em_tso_setup(struct adapter *adapter, struct mbuf *mp, u32 *txd_upper,
*txd_upper = ((isip6 ? 0 : E1000_TXD_POPTS_IXSM) |
E1000_TXD_POPTS_TXSM) << 8;
- curr_txd = adapter->next_avail_tx_desc;
- tx_buffer = &adapter->tx_buffer_area[curr_txd];
- TXD = (struct e1000_context_desc *) &adapter->tx_desc_base[curr_txd];
+ cur = txr->next_avail_desc;
+ tx_buffer = &txr->tx_buffers[cur];
+ TXD = (struct e1000_context_desc *) &txr->tx_base[cur];
/* IPv6 doesn't have a header checksum. */
if (!isip6) {
@@ -3945,24 +3500,23 @@ em_tso_setup(struct adapter *adapter, struct mbuf *mp, u32 *txd_upper,
TXD->cmd_and_length = htole32(adapter->txd_cmd |
E1000_TXD_CMD_DEXT | /* Extended descr */
E1000_TXD_CMD_TSE | /* TSE context */
- (isip6 ? 0 : E1000_TXD_CMD_IP) | /* Do IP csum */
+ (isip6 ? 0 : E1000_TXD_CMD_IP) |
E1000_TXD_CMD_TCP | /* Do TCP checksum */
(mp->m_pkthdr.len - (hdr_len))); /* Total len */
tx_buffer->m_head = NULL;
tx_buffer->next_eop = -1;
- if (++curr_txd == adapter->num_tx_desc)
- curr_txd = 0;
+ if (++cur == adapter->num_tx_desc)
+ cur = 0;
- adapter->num_tx_desc_avail--;
- adapter->next_avail_tx_desc = curr_txd;
- adapter->tx_tso = TRUE;
+ txr->tx_avail--;
+ txr->next_avail_desc = cur;
+ txr->tx_tso = TRUE;
return TRUE;
}
-#endif /* __FreeBSD_version >= 700000 */
/**********************************************************************
*
@@ -3971,26 +3525,26 @@ em_tso_setup(struct adapter *adapter, struct mbuf *mp, u32 *txd_upper,
* tx_buffer is put back on the free queue.
*
**********************************************************************/
-static void
-em_txeof(struct adapter *adapter)
+static bool
+em_txeof(struct tx_ring *txr)
{
+ struct adapter *adapter = txr->adapter;
int first, last, done, num_avail;
- u32 cleaned = 0;
struct em_buffer *tx_buffer;
struct e1000_tx_desc *tx_desc, *eop_desc;
struct ifnet *ifp = adapter->ifp;
- EM_TX_LOCK_ASSERT(adapter);
+ EM_TX_LOCK_ASSERT(txr);
- if (adapter->num_tx_desc_avail == adapter->num_tx_desc)
- return;
+ if (txr->tx_avail == adapter->num_tx_desc)
+ return (FALSE);
- num_avail = adapter->num_tx_desc_avail;
- first = adapter->next_tx_to_clean;
- tx_desc = &adapter->tx_desc_base[first];
- tx_buffer = &adapter->tx_buffer_area[first];
+ num_avail = txr->tx_avail;
+ first = txr->next_to_clean;
+ tx_desc = &txr->tx_base[first];
+ tx_buffer = &txr->tx_buffers[first];
last = tx_buffer->next_eop;
- eop_desc = &adapter->tx_desc_base[last];
+ eop_desc = &txr->tx_base[last];
/*
* What this does is get the index of the
@@ -4002,7 +3556,7 @@ em_txeof(struct adapter *adapter)
last = 0;
done = last;
- bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_POSTREAD);
while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) {
@@ -4011,137 +3565,130 @@ em_txeof(struct adapter *adapter)
tx_desc->upper.data = 0;
tx_desc->lower.data = 0;
tx_desc->buffer_addr = 0;
- ++num_avail; ++cleaned;
+ ++num_avail;
if (tx_buffer->m_head) {
ifp->if_opackets++;
- bus_dmamap_sync(adapter->txtag,
+ bus_dmamap_sync(txr->txtag,
tx_buffer->map,
BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(adapter->txtag,
+ bus_dmamap_unload(txr->txtag,
tx_buffer->map);
m_freem(tx_buffer->m_head);
tx_buffer->m_head = NULL;
}
tx_buffer->next_eop = -1;
+ txr->watchdog_time = ticks;
if (++first == adapter->num_tx_desc)
first = 0;
- tx_buffer = &adapter->tx_buffer_area[first];
- tx_desc = &adapter->tx_desc_base[first];
+ tx_buffer = &txr->tx_buffers[first];
+ tx_desc = &txr->tx_base[first];
}
/* See if we can continue to the next packet */
last = tx_buffer->next_eop;
if (last != -1) {
- eop_desc = &adapter->tx_desc_base[last];
+ eop_desc = &txr->tx_base[last];
/* Get new done point */
if (++last == adapter->num_tx_desc) last = 0;
done = last;
} else
break;
}
- bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
- adapter->next_tx_to_clean = first;
+ txr->next_to_clean = first;
/*
* If we have enough room, clear IFF_DRV_OACTIVE to
* tell the stack that it is OK to send packets.
- * If there are no pending descriptors, clear the timeout.
+ * If there are no pending descriptors, clear the watchdog.
*/
if (num_avail > EM_TX_CLEANUP_THRESHOLD) {
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
if (num_avail == adapter->num_tx_desc) {
- adapter->watchdog_timer = 0;
- adapter->num_tx_desc_avail = num_avail;
- return;
+ txr->watchdog_check = FALSE;
+ txr->tx_avail = num_avail;
+ return (FALSE);
}
}
- /* If any descriptors cleaned, reset the watchdog */
- if (cleaned)
- adapter->watchdog_timer = EM_TX_TIMEOUT;
- adapter->num_tx_desc_avail = num_avail;
- return;
+ txr->tx_avail = num_avail;
+ return (TRUE);
}
-/*********************************************************************
- *
- * When Link is lost sometimes there is work still in the TX ring
- * which will result in a watchdog, rather than allow that do an
- * attempted cleanup and then reinit here. Note that this has been
- * seens mostly with fiber adapters.
- *
- **********************************************************************/
-static void
-em_tx_purge(struct adapter *adapter)
-{
- if ((!adapter->link_active) && (adapter->watchdog_timer)) {
- EM_TX_LOCK(adapter);
- em_txeof(adapter);
- EM_TX_UNLOCK(adapter);
- if (adapter->watchdog_timer) { /* Still not clean? */
- adapter->watchdog_timer = 0;
- em_init_locked(adapter);
- }
- }
-}
/*********************************************************************
*
- * Get a buffer from system mbuf buffer pool.
+ * Refresh RX descriptor mbufs from system mbuf buffer pool.
*
**********************************************************************/
-static int
-em_get_buf(struct adapter *adapter, int i)
+static void
+em_refresh_mbufs(struct rx_ring *rxr, int limit)
{
+ struct adapter *adapter = rxr->adapter;
struct mbuf *m;
bus_dma_segment_t segs[1];
bus_dmamap_t map;
- struct em_buffer *rx_buffer;
- int error, nsegs;
+ struct em_buffer *rxbuf;
+ int i, error, nsegs, cleaned;
- m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
- if (m == NULL) {
- adapter->mbuf_cluster_failed++;
- return (ENOBUFS);
- }
- m->m_len = m->m_pkthdr.len = MCLBYTES;
+ i = rxr->next_to_refresh;
+ cleaned = -1;
+ while (i != limit) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ goto update;
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
- if (adapter->max_frame_size <= (MCLBYTES - ETHER_ALIGN))
- m_adj(m, ETHER_ALIGN);
+ if (adapter->max_frame_size <= (MCLBYTES - ETHER_ALIGN))
+ m_adj(m, ETHER_ALIGN);
- /*
- * Using memory from the mbuf cluster pool, invoke the
- * bus_dma machinery to arrange the memory mapping.
- */
- error = bus_dmamap_load_mbuf_sg(adapter->rxtag,
- adapter->rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
- if (error != 0) {
- m_free(m);
- return (error);
- }
-
- /* If nsegs is wrong then the stack is corrupt. */
- KASSERT(nsegs == 1, ("Too many segments returned!"));
+ /*
+ * Using memory from the mbuf cluster pool, invoke the
+ * bus_dma machinery to arrange the memory mapping.
+ */
+ error = bus_dmamap_load_mbuf_sg(rxr->rxtag, rxr->rx_sparemap,
+ m, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_free(m);
+ goto update;
+ }
- rx_buffer = &adapter->rx_buffer_area[i];
- if (rx_buffer->m_head != NULL)
- bus_dmamap_unload(adapter->rxtag, rx_buffer->map);
+ /* If nsegs is wrong then the stack is corrupt. */
+ KASSERT(nsegs == 1, ("Too many segments returned!"));
+
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->m_head != NULL)
+ bus_dmamap_unload(rxr->rxtag, rxbuf->map);
+
+ map = rxbuf->map;
+ rxbuf->map = rxr->rx_sparemap;
+ rxr->rx_sparemap = map;
+ bus_dmamap_sync(rxr->rxtag,
+ rxbuf->map, BUS_DMASYNC_PREREAD);
+ rxbuf->m_head = m;
+ rxr->rx_base[i].buffer_addr = htole64(segs[0].ds_addr);
- map = rx_buffer->map;
- rx_buffer->map = adapter->rx_sparemap;
- adapter->rx_sparemap = map;
- bus_dmamap_sync(adapter->rxtag, rx_buffer->map, BUS_DMASYNC_PREREAD);
- rx_buffer->m_head = m;
+ cleaned = i;
+ /* Calculate next index */
+ if (++i == adapter->num_rx_desc)
+ i = 0;
+ /* This is the work marker for refresh */
+ rxr->next_to_refresh = i;
+ }
+update:
+ if (cleaned != -1) /* Update tail index */
+ E1000_WRITE_REG(&adapter->hw,
+ E1000_RDT(rxr->me), cleaned);
- adapter->rx_desc_base[i].buffer_addr = htole64(segs[0].ds_addr);
- return (0);
+ return;
}
+
/*********************************************************************
*
* Allocate memory for rx_buffer structures. Since we use one
@@ -4151,24 +3698,21 @@ em_get_buf(struct adapter *adapter, int i)
*
**********************************************************************/
static int
-em_allocate_receive_structures(struct adapter *adapter)
+em_allocate_receive_buffers(struct rx_ring *rxr)
{
- device_t dev = adapter->dev;
- struct em_buffer *rx_buffer;
- int i, error;
+ struct adapter *adapter = rxr->adapter;
+ device_t dev = adapter->dev;
+ struct em_buffer *rxbuf;
+ int error;
- adapter->rx_buffer_area = malloc(sizeof(struct em_buffer) *
+ rxr->rx_buffers = malloc(sizeof(struct em_buffer) *
adapter->num_rx_desc, M_DEVBUF, M_NOWAIT | M_ZERO);
- if (adapter->rx_buffer_area == NULL) {
+ if (rxr->rx_buffers == NULL) {
device_printf(dev, "Unable to allocate rx_buffer memory\n");
return (ENOMEM);
}
-#if __FreeBSD_version >= 700000
error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
-#else
- error = bus_dma_tag_create(NULL, /* parent */
-#endif
1, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
@@ -4179,7 +3723,7 @@ em_allocate_receive_structures(struct adapter *adapter)
0, /* flags */
NULL, /* lockfunc */
NULL, /* lockarg */
- &adapter->rxtag);
+ &rxr->rxtag);
if (error) {
device_printf(dev, "%s: bus_dma_tag_create failed %d\n",
__func__, error);
@@ -4187,18 +3731,19 @@ em_allocate_receive_structures(struct adapter *adapter)
}
/* Create the spare map (used by getbuf) */
- error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT,
- &adapter->rx_sparemap);
+ error = bus_dmamap_create(rxr->rxtag, BUS_DMA_NOWAIT,
+ &rxr->rx_sparemap);
if (error) {
device_printf(dev, "%s: bus_dmamap_create failed: %d\n",
__func__, error);
goto fail;
}
- rx_buffer = adapter->rx_buffer_area;
- for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
- error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT,
- &rx_buffer->map);
+ rxbuf = rxr->rx_buffers;
+ for (int i = 0; i < adapter->num_rx_desc; i++, rxbuf++) {
+ rxbuf = &rxr->rx_buffers[i];
+ error = bus_dmamap_create(rxr->rxtag, BUS_DMA_NOWAIT,
+ &rxbuf->map);
if (error) {
device_printf(dev, "%s: bus_dmamap_create failed: %d\n",
__func__, error);
@@ -4213,50 +3758,184 @@ fail:
return (error);
}
+
/*********************************************************************
*
- * (Re)initialize receive structures.
+ * Initialize a receive ring and its buffers.
*
**********************************************************************/
static int
-em_setup_receive_structures(struct adapter *adapter)
+em_setup_receive_ring(struct rx_ring *rxr)
{
- struct em_buffer *rx_buffer;
- int i, error;
+ struct adapter *adapter = rxr->adapter;
+ struct em_buffer *rxbuf;
+ bus_dma_segment_t seg[1];
+ int rsize, nsegs, error;
- /* Reset descriptor ring */
- bzero(adapter->rx_desc_base,
- (sizeof(struct e1000_rx_desc)) * adapter->num_rx_desc);
- /* Free current RX buffers. */
- rx_buffer = adapter->rx_buffer_area;
- for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
- if (rx_buffer->m_head != NULL) {
- bus_dmamap_sync(adapter->rxtag, rx_buffer->map,
+ /* Clear the ring contents */
+ EM_RX_LOCK(rxr);
+ rsize = roundup2(adapter->num_rx_desc *
+ sizeof(struct e1000_rx_desc), EM_DBA_ALIGN);
+ bzero((void *)rxr->rx_base, rsize);
+
+ /*
+ ** Free current RX buffer structs and their mbufs
+ */
+ for (int i = 0; i < adapter->num_rx_desc; i++) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->m_head != NULL) {
+ bus_dmamap_sync(rxr->rxtag, rxbuf->map,
BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(adapter->rxtag, rx_buffer->map);
- m_freem(rx_buffer->m_head);
- rx_buffer->m_head = NULL;
+ bus_dmamap_unload(rxr->rxtag, rxbuf->map);
+ m_freem(rxbuf->m_head);
}
- }
+ }
+
+ /* Now replenish the mbufs */
+ for (int j = 0; j != adapter->num_rx_desc; ++j) {
+
+ rxbuf = &rxr->rx_buffers[j];
+ rxbuf->m_head = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (rxbuf->m_head == NULL)
+ panic("RX ring hdr initialization failed!\n");
+ rxbuf->m_head->m_len = MCLBYTES;
+ rxbuf->m_head->m_flags &= ~M_HASFCS; /* we strip it */
+ rxbuf->m_head->m_pkthdr.len = MCLBYTES;
+
+ /* Get the memory mapping */
+ error = bus_dmamap_load_mbuf_sg(rxr->rxtag,
+ rxbuf->map, rxbuf->m_head, seg,
+ &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0)
+ panic("RX ring dma initialization failed!\n");
+ bus_dmamap_sync(rxr->rxtag,
+ rxbuf->map, BUS_DMASYNC_PREREAD);
- /* Allocate new ones. */
- for (i = 0; i < adapter->num_rx_desc; i++) {
- error = em_get_buf(adapter, i);
- if (error)
- return (error);
+ /* Update descriptor */
+ rxr->rx_base[j].buffer_addr = htole64(seg[0].ds_addr);
}
- /* Setup our descriptor pointers */
- adapter->next_rx_desc_to_check = 0;
- bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
+
+ /* Setup our descriptor indices */
+ rxr->next_to_check = 0;
+ rxr->next_to_refresh = 0;
+
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ EM_RX_UNLOCK(rxr);
return (0);
}
/*********************************************************************
*
+ * Initialize all receive rings.
+ *
+ **********************************************************************/
+static int
+em_setup_receive_structures(struct adapter *adapter)
+{
+ struct rx_ring *rxr = adapter->rx_rings;
+ int j;
+
+ for (j = 0; j < adapter->num_queues; j++, rxr++)
+ if (em_setup_receive_ring(rxr))
+ goto fail;
+
+ return (0);
+fail:
+ /*
+ * Free RX buffers allocated so far, we will only handle
+ * the rings that completed, the failing case will have
+ * cleaned up for itself. 'j' failed, so its the terminus.
+ */
+ for (int i = 0; i < j; ++i) {
+ rxr = &adapter->rx_rings[i];
+ for (int n = 0; n < adapter->num_rx_desc; n++) {
+ struct em_buffer *rxbuf;
+ rxbuf = &rxr->rx_buffers[n];
+ if (rxbuf->m_head != NULL) {
+ bus_dmamap_sync(rxr->rxtag, rxbuf->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->rxtag, rxbuf->map);
+ m_freem(rxbuf->m_head);
+ rxbuf->m_head = NULL;
+ }
+ }
+ }
+
+ return (ENOBUFS);
+}
+
+/*********************************************************************
+ *
+ * Free all receive rings.
+ *
+ **********************************************************************/
+static void
+em_free_receive_structures(struct adapter *adapter)
+{
+ struct rx_ring *rxr = adapter->rx_rings;
+
+ for (int i = 0; i < adapter->num_queues; i++, rxr++) {
+ em_free_receive_buffers(rxr);
+ /* Free the ring memory as well */
+ em_dma_free(adapter, &rxr->rxdma);
+ EM_RX_LOCK_DESTROY(rxr);
+ }
+
+ free(adapter->rx_rings, M_DEVBUF);
+}
+
+
+/*********************************************************************
+ *
+ * Free receive ring data structures
+ *
+ **********************************************************************/
+static void
+em_free_receive_buffers(struct rx_ring *rxr)
+{
+ struct adapter *adapter = rxr->adapter;
+ struct em_buffer *rxbuf = NULL;
+
+ INIT_DEBUGOUT("free_receive_buffers: begin");
+
+ if (rxr->rx_sparemap) {
+ bus_dmamap_destroy(rxr->rxtag, rxr->rx_sparemap);
+ rxr->rx_sparemap = NULL;
+ }
+
+ if (rxr->rx_buffers != NULL) {
+ for (int i = 0; i < adapter->num_rx_desc; i++) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->map != NULL) {
+ bus_dmamap_sync(rxr->rxtag, rxbuf->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->rxtag, rxbuf->map);
+ bus_dmamap_destroy(rxr->rxtag, rxbuf->map);
+ }
+ if (rxbuf->m_head != NULL) {
+ m_freem(rxbuf->m_head);
+ rxbuf->m_head = NULL;
+ }
+ }
+ free(rxr->rx_buffers, M_DEVBUF);
+ rxr->rx_buffers = NULL;
+ }
+
+ if (rxr->rxtag != NULL) {
+ bus_dma_tag_destroy(rxr->rxtag);
+ rxr->rxtag = NULL;
+ }
+
+ return;
+}
+
+
+/*********************************************************************
+ *
* Enable receive unit.
*
**********************************************************************/
@@ -4266,28 +3945,28 @@ em_setup_receive_structures(struct adapter *adapter)
static void
em_initialize_receive_unit(struct adapter *adapter)
{
+ struct rx_ring *rxr = adapter->rx_rings;
struct ifnet *ifp = adapter->ifp;
+ struct e1000_hw *hw = &adapter->hw;
u64 bus_addr;
u32 rctl, rxcsum;
- INIT_DEBUGOUT("em_initialize_receive_unit: begin");
+ INIT_DEBUGOUT("em_initialize_receive_units: begin");
/*
* Make sure receives are disabled while setting
* up the descriptor ring
*/
- rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
- E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
+ rctl = E1000_READ_REG(hw, E1000_RCTL);
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
- if (adapter->hw.mac.type >= e1000_82540) {
- E1000_WRITE_REG(&adapter->hw, E1000_RADV,
- adapter->rx_abs_int_delay.value);
- /*
- * Set the interrupt throttling rate. Value is calculated
- * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns)
- */
- E1000_WRITE_REG(&adapter->hw, E1000_ITR, DEFAULT_ITR);
- }
+ E1000_WRITE_REG(&adapter->hw, E1000_RADV,
+ adapter->rx_abs_int_delay.value);
+ /*
+ * Set the interrupt throttling rate. Value is calculated
+ * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns)
+ */
+ E1000_WRITE_REG(hw, E1000_ITR, DEFAULT_ITR);
/*
** When using MSIX interrupts we need to throttle
@@ -4295,67 +3974,17 @@ em_initialize_receive_unit(struct adapter *adapter)
*/
if (adapter->msix)
for (int i = 0; i < 4; i++)
- E1000_WRITE_REG(&adapter->hw,
- E1000_EITR_82574(i), DEFAULT_ITR);
+ E1000_WRITE_REG(hw, E1000_EITR_82574(i),
+ DEFAULT_ITR);
/* Disable accelerated ackknowledge */
if (adapter->hw.mac.type == e1000_82574)
- E1000_WRITE_REG(&adapter->hw,
- E1000_RFCTL, E1000_RFCTL_ACK_DIS);
-
- /* Setup the Base and Length of the Rx Descriptor Ring */
- bus_addr = adapter->rxdma.dma_paddr;
- E1000_WRITE_REG(&adapter->hw, E1000_RDLEN(0),
- adapter->num_rx_desc * sizeof(struct e1000_rx_desc));
- E1000_WRITE_REG(&adapter->hw, E1000_RDBAH(0),
- (u32)(bus_addr >> 32));
- E1000_WRITE_REG(&adapter->hw, E1000_RDBAL(0),
- (u32)bus_addr);
-
- /* Setup the Receive Control Register */
- rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
- rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO |
- E1000_RCTL_RDMTS_HALF |
- (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
-
- /* Make sure VLAN Filters are off */
- rctl &= ~E1000_RCTL_VFE;
-
- if (e1000_tbi_sbp_enabled_82543(&adapter->hw))
- rctl |= E1000_RCTL_SBP;
- else
- rctl &= ~E1000_RCTL_SBP;
+ E1000_WRITE_REG(hw, E1000_RFCTL, E1000_RFCTL_ACK_DIS);
- switch (adapter->rx_buffer_len) {
- default:
- case 2048:
- rctl |= E1000_RCTL_SZ_2048;
- break;
- case 4096:
- rctl |= E1000_RCTL_SZ_4096 |
- E1000_RCTL_BSEX | E1000_RCTL_LPE;
- break;
- case 8192:
- rctl |= E1000_RCTL_SZ_8192 |
- E1000_RCTL_BSEX | E1000_RCTL_LPE;
- break;
- case 16384:
- rctl |= E1000_RCTL_SZ_16384 |
- E1000_RCTL_BSEX | E1000_RCTL_LPE;
- break;
- }
-
- if (ifp->if_mtu > ETHERMTU)
- rctl |= E1000_RCTL_LPE;
- else
- rctl &= ~E1000_RCTL_LPE;
-
- /* Enable 82543 Receive Checksum Offload for TCP and UDP */
- if ((adapter->hw.mac.type >= e1000_82543) &&
- (ifp->if_capenable & IFCAP_RXCSUM)) {
- rxcsum = E1000_READ_REG(&adapter->hw, E1000_RXCSUM);
+ if (ifp->if_capenable & IFCAP_RXCSUM) {
+ rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
rxcsum |= (E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
- E1000_WRITE_REG(&adapter->hw, E1000_RXCSUM, rxcsum);
+ E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
}
/*
@@ -4365,73 +3994,43 @@ em_initialize_receive_unit(struct adapter *adapter)
** values in RDTR is a known source of problems on other
** platforms another solution is being sought.
*/
- if (adapter->hw.mac.type == e1000_82573)
- E1000_WRITE_REG(&adapter->hw, E1000_RDTR, 0x20);
-
- /* Enable Receives */
- E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl);
-
- /*
- * Setup the HW Rx Head and
- * Tail Descriptor Pointers
- */
- E1000_WRITE_REG(&adapter->hw, E1000_RDH(0), 0);
- E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), adapter->num_rx_desc - 1);
+ if (hw->mac.type == e1000_82573)
+ E1000_WRITE_REG(hw, E1000_RDTR, 0x20);
- return;
-}
-
-/*********************************************************************
- *
- * Free receive related data structures.
- *
- **********************************************************************/
-static void
-em_free_receive_structures(struct adapter *adapter)
-{
- struct em_buffer *rx_buffer;
- int i;
-
- INIT_DEBUGOUT("free_receive_structures: begin");
-
- if (adapter->rx_sparemap) {
- bus_dmamap_destroy(adapter->rxtag, adapter->rx_sparemap);
- adapter->rx_sparemap = NULL;
+ for (int i = 0; i < adapter->num_queues; i++, rxr++) {
+ /* Setup the Base and Length of the Rx Descriptor Ring */
+ bus_addr = rxr->rxdma.dma_paddr;
+ E1000_WRITE_REG(hw, E1000_RDLEN(i),
+ adapter->num_rx_desc * sizeof(struct e1000_rx_desc));
+ E1000_WRITE_REG(hw, E1000_RDBAH(i), (u32)(bus_addr >> 32));
+ E1000_WRITE_REG(hw, E1000_RDBAL(i), (u32)bus_addr);
+ /* Setup the Head and Tail Descriptor Pointers */
+ E1000_WRITE_REG(hw, E1000_RDH(i), 0);
+ E1000_WRITE_REG(hw, E1000_RDT(i), adapter->num_rx_desc - 1);
}
- /* Cleanup any existing buffers */
- if (adapter->rx_buffer_area != NULL) {
- rx_buffer = adapter->rx_buffer_area;
- for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
- if (rx_buffer->m_head != NULL) {
- bus_dmamap_sync(adapter->rxtag, rx_buffer->map,
- BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(adapter->rxtag,
- rx_buffer->map);
- m_freem(rx_buffer->m_head);
- rx_buffer->m_head = NULL;
- } else if (rx_buffer->map != NULL)
- bus_dmamap_unload(adapter->rxtag,
- rx_buffer->map);
- if (rx_buffer->map != NULL) {
- bus_dmamap_destroy(adapter->rxtag,
- rx_buffer->map);
- rx_buffer->map = NULL;
- }
- }
- }
+ /* Setup the Receive Control Register */
+ rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
+ rctl |= E1000_RCTL_EN | E1000_RCTL_BAM |
+ E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+ (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
- if (adapter->rx_buffer_area != NULL) {
- free(adapter->rx_buffer_area, M_DEVBUF);
- adapter->rx_buffer_area = NULL;
- }
+ /* Make sure VLAN Filters are off */
+ rctl &= ~E1000_RCTL_VFE;
+ rctl &= ~E1000_RCTL_SBP;
+ rctl |= E1000_RCTL_SZ_2048;
+ if (ifp->if_mtu > ETHERMTU)
+ rctl |= E1000_RCTL_LPE;
+ else
+ rctl &= ~E1000_RCTL_LPE;
- if (adapter->rxtag != NULL) {
- bus_dma_tag_destroy(adapter->rxtag);
- adapter->rxtag = NULL;
- }
+ /* Write out the settings */
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl);
+
+ return;
}
+
/*********************************************************************
*
* This routine executes in interrupt context. It replenishes
@@ -4444,184 +4043,127 @@ em_free_receive_structures(struct adapter *adapter)
* For polling we also now return the number of cleaned packets
*********************************************************************/
static int
-em_rxeof(struct adapter *adapter, int count)
+em_rxeof(struct rx_ring *rxr, int count)
{
- struct ifnet *ifp = adapter->ifp;
- struct mbuf *mp;
- u8 status, accept_frame = 0, eop = 0;
- u16 len, desc_len, prev_len_adj;
- int i, rx_sent = 0;
- struct e1000_rx_desc *current_desc;
-
- EM_RX_LOCK(adapter);
- i = adapter->next_rx_desc_to_check;
- current_desc = &adapter->rx_desc_base[i];
- bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
- BUS_DMASYNC_POSTREAD);
+ struct adapter *adapter = rxr->adapter;
+ struct ifnet *ifp = adapter->ifp;;
+ struct mbuf *mp, *sendmp;
+ u8 status;
+ u16 len;
+ int i, processed, rxdone = 0;
+ bool eop;
+ struct e1000_rx_desc *cur;
- if (!((current_desc->status) & E1000_RXD_STAT_DD)) {
- EM_RX_UNLOCK(adapter);
- return (rx_sent);
- }
+ EM_RX_LOCK(rxr);
- while ((current_desc->status & E1000_RXD_STAT_DD) &&
- (count != 0) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- struct mbuf *m = NULL;
+ for (i = rxr->next_to_check, processed = 0; count != 0;) {
- mp = adapter->rx_buffer_area[i].m_head;
- /*
- * Can't defer bus_dmamap_sync(9) because TBI_ACCEPT
- * needs to access the last received byte in the mbuf.
- */
- bus_dmamap_sync(adapter->rxtag, adapter->rx_buffer_area[i].map,
- BUS_DMASYNC_POSTREAD);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
- accept_frame = 1;
- prev_len_adj = 0;
- desc_len = le16toh(current_desc->length);
- status = current_desc->status;
- if (status & E1000_RXD_STAT_EOP) {
- count--;
- eop = 1;
- if (desc_len < ETHER_CRC_LEN) {
- len = 0;
- prev_len_adj = ETHER_CRC_LEN - desc_len;
- } else
- len = desc_len - ETHER_CRC_LEN;
- } else {
- eop = 0;
- len = desc_len;
- }
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- if (current_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
- u8 last_byte;
- u32 pkt_len = desc_len;
+ cur = &rxr->rx_base[i];
+ status = cur->status;
+ mp = sendmp = NULL;
- if (adapter->fmp != NULL)
- pkt_len += adapter->fmp->m_pkthdr.len;
+ if ((status & E1000_RXD_STAT_DD) == 0)
+ break;
- last_byte = *(mtod(mp, caddr_t) + desc_len - 1);
- if (TBI_ACCEPT(&adapter->hw, status,
- current_desc->errors, pkt_len, last_byte,
- adapter->min_frame_size, adapter->max_frame_size)) {
- e1000_tbi_adjust_stats_82543(&adapter->hw,
- &adapter->stats, pkt_len,
- adapter->hw.mac.addr,
- adapter->max_frame_size);
- if (len > 0)
- len--;
- } else
- accept_frame = 0;
- }
+ len = le16toh(cur->length);
+ eop = (status & E1000_RXD_STAT_EOP) != 0;
+ count--;
- if (accept_frame) {
- if (em_get_buf(adapter, i) != 0) {
- ifp->if_iqdrops++;
- goto discard;
- }
+ if ((cur->errors & E1000_RXD_ERR_FRAME_ERR_MASK) == 0) {
/* Assign correct length to the current fragment */
+ mp = rxr->rx_buffers[i].m_head;
mp->m_len = len;
- if (adapter->fmp == NULL) {
+ if (rxr->fmp == NULL) {
mp->m_pkthdr.len = len;
- adapter->fmp = mp; /* Store the first mbuf */
- adapter->lmp = mp;
+ rxr->fmp = mp; /* Store the first mbuf */
+ rxr->lmp = mp;
} else {
/* Chain mbuf's together */
mp->m_flags &= ~M_PKTHDR;
- /*
- * Adjust length of previous mbuf in chain if
- * we received less than 4 bytes in the last
- * descriptor.
- */
- if (prev_len_adj > 0) {
- adapter->lmp->m_len -= prev_len_adj;
- adapter->fmp->m_pkthdr.len -=
- prev_len_adj;
- }
- adapter->lmp->m_next = mp;
- adapter->lmp = adapter->lmp->m_next;
- adapter->fmp->m_pkthdr.len += len;
+ rxr->lmp->m_next = mp;
+ rxr->lmp = rxr->lmp->m_next;
+ rxr->fmp->m_pkthdr.len += len;
}
if (eop) {
- adapter->fmp->m_pkthdr.rcvif = ifp;
+ rxr->fmp->m_pkthdr.rcvif = ifp;
ifp->if_ipackets++;
- em_receive_checksum(adapter, current_desc,
- adapter->fmp);
+ em_receive_checksum(cur, rxr->fmp);
#ifndef __NO_STRICT_ALIGNMENT
if (adapter->max_frame_size >
(MCLBYTES - ETHER_ALIGN) &&
- em_fixup_rx(adapter) != 0)
+ em_fixup_rx(rxr) != 0)
goto skip;
#endif
if (status & E1000_RXD_STAT_VP) {
-#if __FreeBSD_version < 700000
- VLAN_INPUT_TAG_NEW(ifp, adapter->fmp,
- (le16toh(current_desc->special) &
- E1000_RXD_SPC_VLAN_MASK));
-#else
- adapter->fmp->m_pkthdr.ether_vtag =
- (le16toh(current_desc->special) &
+ rxr->fmp->m_pkthdr.ether_vtag =
+ (le16toh(cur->special) &
E1000_RXD_SPC_VLAN_MASK);
- adapter->fmp->m_flags |= M_VLANTAG;
-#endif
+ rxr->fmp->m_flags |= M_VLANTAG;
}
#ifndef __NO_STRICT_ALIGNMENT
skip:
#endif
- m = adapter->fmp;
- adapter->fmp = NULL;
- adapter->lmp = NULL;
+ sendmp = rxr->fmp;
+ rxr->fmp = NULL;
+ rxr->lmp = NULL;
}
} else {
ifp->if_ierrors++;
-discard:
/* Reuse loaded DMA map and just update mbuf chain */
- mp = adapter->rx_buffer_area[i].m_head;
+ mp = rxr->rx_buffers[i].m_head;
mp->m_len = mp->m_pkthdr.len = MCLBYTES;
mp->m_data = mp->m_ext.ext_buf;
mp->m_next = NULL;
if (adapter->max_frame_size <=
(MCLBYTES - ETHER_ALIGN))
m_adj(mp, ETHER_ALIGN);
- if (adapter->fmp != NULL) {
- m_freem(adapter->fmp);
- adapter->fmp = NULL;
- adapter->lmp = NULL;
+ if (rxr->fmp != NULL) {
+ m_freem(rxr->fmp);
+ rxr->fmp = NULL;
+ rxr->lmp = NULL;
}
- m = NULL;
+ sendmp = NULL;
}
/* Zero out the receive descriptors status. */
- current_desc->status = 0;
- bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
- BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ cur->status = 0;
+ ++rxdone; /* cumulative for POLL */
+ ++processed;
/* Advance our pointers to the next descriptor. */
if (++i == adapter->num_rx_desc)
i = 0;
- /* Call into the stack */
- if (m != NULL) {
- adapter->next_rx_desc_to_check = i;
- EM_RX_UNLOCK(adapter);
- (*ifp->if_input)(ifp, m);
- EM_RX_LOCK(adapter);
- rx_sent++;
- i = adapter->next_rx_desc_to_check;
+
+ /* Send to the stack */
+ if (sendmp != NULL)
+ (*ifp->if_input)(ifp, sendmp);
+
+ /* Only refresh mbufs every 8 descriptors */
+ if (processed == 8) {
+ em_refresh_mbufs(rxr, i);
+ processed = 0;
}
- current_desc = &adapter->rx_desc_base[i];
}
- adapter->next_rx_desc_to_check = i;
- /* Advance the E1000's Receive Queue #0 "Tail Pointer". */
- if (--i < 0)
- i = adapter->num_rx_desc - 1;
- E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), i);
- EM_RX_UNLOCK(adapter);
- return (rx_sent);
+ /* Catch any remaining refresh work */
+ if (processed != 0) {
+ em_refresh_mbufs(rxr, i);
+ processed = 0;
+ }
+
+ rxr->next_to_check = i;
+
+ EM_RX_UNLOCK(rxr);
+ return (rxdone);
}
#ifndef __NO_STRICT_ALIGNMENT
@@ -4640,13 +4182,14 @@ discard:
* not used at all on architectures with strict alignment.
*/
static int
-em_fixup_rx(struct adapter *adapter)
+em_fixup_rx(struct rx_ring *rxr)
{
+ struct adapter *adapter = rxr->adapter;
struct mbuf *m, *n;
int error;
error = 0;
- m = adapter->fmp;
+ m = rxr->fmp;
if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) {
bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
m->m_data += ETHER_HDR_LEN;
@@ -4659,11 +4202,11 @@ em_fixup_rx(struct adapter *adapter)
n->m_len = ETHER_HDR_LEN;
M_MOVE_PKTHDR(n, m);
n->m_next = m;
- adapter->fmp = n;
+ rxr->fmp = n;
} else {
adapter->dropped_pkts++;
- m_freem(adapter->fmp);
- adapter->fmp = NULL;
+ m_freem(rxr->fmp);
+ rxr->fmp = NULL;
error = ENOMEM;
}
}
@@ -4680,13 +4223,10 @@ em_fixup_rx(struct adapter *adapter)
*
*********************************************************************/
static void
-em_receive_checksum(struct adapter *adapter,
- struct e1000_rx_desc *rx_desc, struct mbuf *mp)
+em_receive_checksum(struct e1000_rx_desc *rx_desc, struct mbuf *mp)
{
- /* 82543 or newer only */
- if ((adapter->hw.mac.type < e1000_82543) ||
- /* Ignore Checksum bit is set */
- (rx_desc->status & E1000_RXD_STAT_IXSM)) {
+ /* Ignore Checksum bit is set */
+ if (rx_desc->status & E1000_RXD_STAT_IXSM) {
mp->m_pkthdr.csum_flags = 0;
return;
}
@@ -4713,7 +4253,6 @@ em_receive_checksum(struct adapter *adapter,
}
}
-#if __FreeBSD_version >= 700029
/*
* This routine is run via an vlan
* config EVENT
@@ -4800,7 +4339,6 @@ em_setup_vlan_hw_support(struct adapter *adapter)
E1000_WRITE_REG(&adapter->hw, E1000_RLPML,
adapter->max_frame_size + VLAN_TAG_SIZE);
}
-#endif
static void
em_enable_intr(struct adapter *adapter)
@@ -4843,15 +4381,12 @@ em_init_manageability(struct adapter *adapter)
manc &= ~(E1000_MANC_ARP_EN);
/* enable receiving management packets to the host */
- if (adapter->hw.mac.type >= e1000_82571) {
- manc |= E1000_MANC_EN_MNG2HOST;
+ manc |= E1000_MANC_EN_MNG2HOST;
#define E1000_MNG2HOST_PORT_623 (1 << 5)
#define E1000_MNG2HOST_PORT_664 (1 << 6)
- manc2h |= E1000_MNG2HOST_PORT_623;
- manc2h |= E1000_MNG2HOST_PORT_664;
- E1000_WRITE_REG(&adapter->hw, E1000_MANC2H, manc2h);
- }
-
+ manc2h |= E1000_MNG2HOST_PORT_623;
+ manc2h |= E1000_MNG2HOST_PORT_664;
+ E1000_WRITE_REG(&adapter->hw, E1000_MANC2H, manc2h);
E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc);
}
}
@@ -4868,81 +4403,61 @@ em_release_manageability(struct adapter *adapter)
/* re-enable hardware interception of ARP */
manc |= E1000_MANC_ARP_EN;
-
- if (adapter->hw.mac.type >= e1000_82571)
- manc &= ~E1000_MANC_EN_MNG2HOST;
+ manc &= ~E1000_MANC_EN_MNG2HOST;
E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc);
}
}
/*
- * em_get_hw_control sets {CTRL_EXT|FWSM}:DRV_LOAD bit.
- * For ASF and Pass Through versions of f/w this means that
- * the driver is loaded. For AMT version (only with 82573)
- * of the f/w this means that the network i/f is open.
- *
+ * em_get_hw_control sets the {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means
+ * that the driver is loaded. For AMT version type f/w
+ * this means that the network i/f is open.
*/
static void
em_get_hw_control(struct adapter *adapter)
{
u32 ctrl_ext, swsm;
- /* Let firmware know the driver has taken over */
- switch (adapter->hw.mac.type) {
- case e1000_82573:
+ if (adapter->hw.mac.type == e1000_82573) {
swsm = E1000_READ_REG(&adapter->hw, E1000_SWSM);
E1000_WRITE_REG(&adapter->hw, E1000_SWSM,
swsm | E1000_SWSM_DRV_LOAD);
- break;
- case e1000_82571:
- case e1000_82572:
- case e1000_80003es2lan:
- case e1000_ich8lan:
- case e1000_ich9lan:
- case e1000_ich10lan:
- ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
- E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
- ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
- break;
- default:
- break;
+ return;
}
+ /* else */
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
+ ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
+ return;
}
/*
* em_release_hw_control resets {CTRL_EXT|FWSM}:DRV_LOAD bit.
- * For ASF and Pass Through versions of f/w this means that the
- * driver is no longer loaded. For AMT version (only with 82573) i
- * of the f/w this means that the network i/f is closed.
- *
+ * For ASF and Pass Through versions of f/w this means that
+ * the driver is no longer loaded. For AMT versions of the
+ * f/w this means that the network i/f is closed.
*/
static void
em_release_hw_control(struct adapter *adapter)
{
u32 ctrl_ext, swsm;
- /* Let firmware taken over control of h/w */
- switch (adapter->hw.mac.type) {
- case e1000_82573:
+ if (!adapter->has_manage)
+ return;
+
+ if (adapter->hw.mac.type == e1000_82573) {
swsm = E1000_READ_REG(&adapter->hw, E1000_SWSM);
E1000_WRITE_REG(&adapter->hw, E1000_SWSM,
swsm & ~E1000_SWSM_DRV_LOAD);
- break;
- case e1000_82571:
- case e1000_82572:
- case e1000_80003es2lan:
- case e1000_ich8lan:
- case e1000_ich9lan:
- case e1000_ich10lan:
- ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
- E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
- ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
- break;
- default:
- break;
-
+ return;
}
+ /* else */
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
+ ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
+ return;
}
static int
@@ -4958,83 +4473,248 @@ em_is_valid_ether_addr(u8 *addr)
}
/*
+** Parse the interface capabilities with regard
+** to both system management and wake-on-lan for
+** later use.
+*/
+static void
+em_get_wakeup(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+ u16 eeprom_data = 0, device_id, apme_mask;
+
+ adapter->has_manage = e1000_enable_mng_pass_thru(&adapter->hw);
+ apme_mask = EM_EEPROM_APME;
+
+ switch (adapter->hw.mac.type) {
+ case e1000_82573:
+ case e1000_82583:
+ adapter->has_amt = TRUE;
+ /* Falls thru */
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
+ if (adapter->hw.bus.func == 1) {
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+ break;
+ } else
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ break;
+ case e1000_ich8lan:
+ case e1000_ich9lan:
+ case e1000_ich10lan:
+ case e1000_pchlan:
+ apme_mask = E1000_WUC_APME;
+ adapter->has_amt = TRUE;
+ eeprom_data = E1000_READ_REG(&adapter->hw, E1000_WUC);
+ break;
+ default:
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ break;
+ }
+ if (eeprom_data & apme_mask)
+ adapter->wol = (E1000_WUFC_MAG | E1000_WUFC_MC);
+ /*
+ * We have the eeprom settings, now apply the special cases
+ * where the eeprom may be wrong or the board won't support
+ * wake on lan on a particular port
+ */
+ device_id = pci_get_device(dev);
+ switch (device_id) {
+ case E1000_DEV_ID_82571EB_FIBER:
+ /* Wake events only supported on port A for dual fiber
+ * regardless of eeprom setting */
+ if (E1000_READ_REG(&adapter->hw, E1000_STATUS) &
+ E1000_STATUS_FUNC_1)
+ adapter->wol = 0;
+ break;
+ case E1000_DEV_ID_82571EB_QUAD_COPPER:
+ case E1000_DEV_ID_82571EB_QUAD_FIBER:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER_LP:
+ /* if quad port adapter, disable WoL on all but port A */
+ if (global_quad_port_a != 0)
+ adapter->wol = 0;
+ /* Reset for multiple quad port adapters */
+ if (++global_quad_port_a == 4)
+ global_quad_port_a = 0;
+ break;
+ }
+ return;
+}
+
+
+/*
* Enable PCI Wake On Lan capability
*/
-void
+static void
em_enable_wakeup(device_t dev)
{
- u16 cap, status;
- u8 id;
+ struct adapter *adapter = device_get_softc(dev);
+ struct ifnet *ifp = adapter->ifp;
+ u32 pmc, ctrl, ctrl_ext, rctl;
+ u16 status;
- /* First find the capabilities pointer*/
- cap = pci_read_config(dev, PCIR_CAP_PTR, 2);
- /* Read the PM Capabilities */
- id = pci_read_config(dev, cap, 1);
- if (id != PCIY_PMG) /* Something wrong */
+ if ((pci_find_extcap(dev, PCIY_PMG, &pmc) != 0))
return;
- /* OK, we have the power capabilities, so
- now get the status register */
- cap += PCIR_POWER_STATUS;
- status = pci_read_config(dev, cap, 2);
- status |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
- pci_write_config(dev, cap, status, 2);
+
+ /* Advertise the wakeup capability */
+ ctrl = E1000_READ_REG(&adapter->hw, E1000_CTRL);
+ ctrl |= (E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN3);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL, ctrl);
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
+
+ if ((adapter->hw.mac.type == e1000_ich8lan) ||
+ (adapter->hw.mac.type == e1000_pchlan) ||
+ (adapter->hw.mac.type == e1000_ich9lan) ||
+ (adapter->hw.mac.type == e1000_ich10lan)) {
+ e1000_disable_gig_wol_ich8lan(&adapter->hw);
+ e1000_hv_phy_powerdown_workaround_ich8lan(&adapter->hw);
+ }
+
+ /* Keep the laser running on Fiber adapters */
+ if (adapter->hw.phy.media_type == e1000_media_type_fiber ||
+ adapter->hw.phy.media_type == e1000_media_type_internal_serdes) {
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_SDP3_DATA;
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, ctrl_ext);
+ }
+
+ /*
+ ** Determine type of Wakeup: note that wol
+ ** is set with all bits on by default.
+ */
+ if ((ifp->if_capenable & IFCAP_WOL_MAGIC) == 0)
+ adapter->wol &= ~E1000_WUFC_MAG;
+
+ if ((ifp->if_capenable & IFCAP_WOL_MCAST) == 0)
+ adapter->wol &= ~E1000_WUFC_MC;
+ else {
+ rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ rctl |= E1000_RCTL_MPE;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl);
+ }
+
+ if (adapter->hw.mac.type == e1000_pchlan) {
+ if (em_enable_phy_wakeup(adapter))
+ return;
+ } else {
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
+ E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol);
+ }
+
+ if (adapter->hw.phy.type == e1000_phy_igp_3)
+ e1000_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw);
+
+ /* Request PME */
+ status = pci_read_config(dev, pmc + PCIR_POWER_STATUS, 2);
+ status &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
+ if (ifp->if_capenable & IFCAP_WOL)
+ status |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
+ pci_write_config(dev, pmc + PCIR_POWER_STATUS, status, 2);
+
return;
}
-
-/*********************************************************************
-* 82544 Coexistence issue workaround.
-* There are 2 issues.
-* 1. Transmit Hang issue.
-* To detect this issue, following equation can be used...
-* SIZE[3:0] + ADDR[2:0] = SUM[3:0].
-* If SUM[3:0] is in between 1 to 4, we will have this issue.
-*
-* 2. DAC issue.
-* To detect this issue, following equation can be used...
-* SIZE[3:0] + ADDR[2:0] = SUM[3:0].
-* If SUM[3:0] is in between 9 to c, we will have this issue.
-*
-*
-* WORKAROUND:
-* Make sure we do not have ending address
-* as 1,2,3,4(Hang) or 9,a,b,c (DAC)
-*
-*************************************************************************/
-static u32
-em_fill_descriptors (bus_addr_t address, u32 length,
- PDESC_ARRAY desc_array)
+/*
+** WOL in the newer chipset interfaces (pchlan)
+** require thing to be copied into the phy
+*/
+static int
+em_enable_phy_wakeup(struct adapter *adapter)
{
- u32 safe_terminator;
+ struct e1000_hw *hw = &adapter->hw;
+ u32 mreg, ret = 0;
+ u16 preg;
+
+ /* copy MAC RARs to PHY RARs */
+ for (int i = 0; i < adapter->hw.mac.rar_entry_count; i++) {
+ mreg = E1000_READ_REG(hw, E1000_RAL(i));
+ e1000_write_phy_reg(hw, BM_RAR_L(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_RAR_M(i),
+ (u16)((mreg >> 16) & 0xFFFF));
+ mreg = E1000_READ_REG(hw, E1000_RAH(i));
+ e1000_write_phy_reg(hw, BM_RAR_H(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_RAR_CTRL(i),
+ (u16)((mreg >> 16) & 0xFFFF));
+ }
+
+ /* copy MAC MTA to PHY MTA */
+ for (int i = 0; i < adapter->hw.mac.mta_reg_count; i++) {
+ mreg = E1000_READ_REG_ARRAY(hw, E1000_MTA, i);
+ e1000_write_phy_reg(hw, BM_MTA(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_MTA(i) + 1,
+ (u16)((mreg >> 16) & 0xFFFF));
+ }
+
+ /* configure PHY Rx Control register */
+ e1000_read_phy_reg(&adapter->hw, BM_RCTL, &preg);
+ mreg = E1000_READ_REG(hw, E1000_RCTL);
+ if (mreg & E1000_RCTL_UPE)
+ preg |= BM_RCTL_UPE;
+ if (mreg & E1000_RCTL_MPE)
+ preg |= BM_RCTL_MPE;
+ preg &= ~(BM_RCTL_MO_MASK);
+ if (mreg & E1000_RCTL_MO_3)
+ preg |= (((mreg & E1000_RCTL_MO_3) >> E1000_RCTL_MO_SHIFT)
+ << BM_RCTL_MO_SHIFT);
+ if (mreg & E1000_RCTL_BAM)
+ preg |= BM_RCTL_BAM;
+ if (mreg & E1000_RCTL_PMCF)
+ preg |= BM_RCTL_PMCF;
+ mreg = E1000_READ_REG(hw, E1000_CTRL);
+ if (mreg & E1000_CTRL_RFCE)
+ preg |= BM_RCTL_RFCE;
+ e1000_write_phy_reg(&adapter->hw, BM_RCTL, preg);
+
+ /* enable PHY wakeup in MAC register */
+ E1000_WRITE_REG(hw, E1000_WUC,
+ E1000_WUC_PHY_WAKE | E1000_WUC_PME_EN);
+ E1000_WRITE_REG(hw, E1000_WUFC, adapter->wol);
- /* Since issue is sensitive to length and address.*/
- /* Let us first check the address...*/
- if (length <= 4) {
- desc_array->descriptor[0].address = address;
- desc_array->descriptor[0].length = length;
- desc_array->elements = 1;
- return (desc_array->elements);
+ /* configure and enable PHY wakeup in PHY registers */
+ e1000_write_phy_reg(&adapter->hw, BM_WUFC, adapter->wol);
+ e1000_write_phy_reg(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
+
+ /* activate PHY wakeup */
+ ret = hw->phy.ops.acquire(hw);
+ if (ret) {
+ printf("Could not acquire PHY\n");
+ return ret;
}
- safe_terminator = (u32)((((u32)address & 0x7) +
- (length & 0xF)) & 0xF);
- /* if it does not fall between 0x1 to 0x4 and 0x9 to 0xC then return */
- if (safe_terminator == 0 ||
- (safe_terminator > 4 &&
- safe_terminator < 9) ||
- (safe_terminator > 0xC &&
- safe_terminator <= 0xF)) {
- desc_array->descriptor[0].address = address;
- desc_array->descriptor[0].length = length;
- desc_array->elements = 1;
- return (desc_array->elements);
+ e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
+ ret = e1000_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &preg);
+ if (ret) {
+ printf("Could not read PHY page 769\n");
+ goto out;
}
+ preg |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
+ ret = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, preg);
+ if (ret)
+ printf("Could not set PHY Host Wakeup bit\n");
+out:
+ hw->phy.ops.release(hw);
- desc_array->descriptor[0].address = address;
- desc_array->descriptor[0].length = length - 4;
- desc_array->descriptor[1].address = address + (length - 4);
- desc_array->descriptor[1].length = 4;
- desc_array->elements = 2;
- return (desc_array->elements);
+ return ret;
+}
+
+static void
+em_led_func(void *arg, int onoff)
+{
+ struct adapter *adapter = arg;
+
+ EM_CORE_LOCK(adapter);
+ if (onoff) {
+ e1000_setup_led(&adapter->hw);
+ e1000_led_on(&adapter->hw);
+ } else {
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
+ }
+ EM_CORE_UNLOCK(adapter);
}
/**********************************************************************
@@ -5146,6 +4826,8 @@ em_print_debug_info(struct adapter *adapter)
{
device_t dev = adapter->dev;
u8 *hw_addr = adapter->hw.hw_addr;
+ struct rx_ring *rxr = adapter->rx_rings;
+ struct tx_ring *txr = adapter->tx_rings;
device_printf(dev, "Adapter hardware address = %p \n", hw_addr);
device_printf(dev, "CTRL = 0x%x RCTL = 0x%x \n",
@@ -5163,29 +4845,33 @@ em_print_debug_info(struct adapter *adapter)
device_printf(dev, "rx_int_delay = %d, rx_abs_int_delay = %d\n",
E1000_READ_REG(&adapter->hw, E1000_RDTR),
E1000_READ_REG(&adapter->hw, E1000_RADV));
- device_printf(dev, "fifo workaround = %lld, fifo_reset_count = %lld\n",
- (long long)adapter->tx_fifo_wrk_cnt,
- (long long)adapter->tx_fifo_reset_cnt);
- device_printf(dev, "hw tdh = %d, hw tdt = %d\n",
- E1000_READ_REG(&adapter->hw, E1000_TDH(0)),
- E1000_READ_REG(&adapter->hw, E1000_TDT(0)));
- device_printf(dev, "hw rdh = %d, hw rdt = %d\n",
- E1000_READ_REG(&adapter->hw, E1000_RDH(0)),
- E1000_READ_REG(&adapter->hw, E1000_RDT(0)));
- device_printf(dev, "Num Tx descriptors avail = %d\n",
- adapter->num_tx_desc_avail);
- device_printf(dev, "Tx Descriptors not avail1 = %ld\n",
- adapter->no_tx_desc_avail1);
- device_printf(dev, "Tx Descriptors not avail2 = %ld\n",
- adapter->no_tx_desc_avail2);
+
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ device_printf(dev, "Queue(%d) tdh = %d, tdt = %d\n", i,
+ E1000_READ_REG(&adapter->hw, E1000_TDH(i)),
+ E1000_READ_REG(&adapter->hw, E1000_TDT(i)));
+ device_printf(dev, "TX(%d) no descriptors avail event = %ld\n",
+ txr->me, txr->no_desc_avail);
+ device_printf(dev, "TX(%d) MSIX IRQ Handled = %ld\n",
+ txr->me, txr->tx_irq);
+ device_printf(dev, "Num Tx descriptors avail = %d\n",
+ txr->tx_avail);
+ device_printf(dev, "Tx Descriptors not avail1 = %ld\n",
+ txr->no_desc_avail);
+ }
+ for (int i = 0; i < adapter->num_queues; i++, rxr++) {
+ device_printf(dev, "RX(%d) MSIX IRQ Handled = %ld\n",
+ rxr->me, rxr->rx_irq);
+ device_printf(dev, "hw rdh = %d, hw rdt = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_RDH(i)),
+ E1000_READ_REG(&adapter->hw, E1000_RDT(i)));
+ }
device_printf(dev, "Std mbuf failed = %ld\n",
adapter->mbuf_alloc_failed);
device_printf(dev, "Std mbuf cluster failed = %ld\n",
adapter->mbuf_cluster_failed);
device_printf(dev, "Driver dropped packets = %ld\n",
adapter->dropped_pkts);
- device_printf(dev, "Driver tx dma failure in encap = %ld\n",
- adapter->no_tx_dma_setup);
}
static void
@@ -5218,12 +4904,8 @@ em_print_hw_stats(struct adapter *adapter)
(long long)adapter->stats.algnerrc);
device_printf(dev, "Collision/Carrier extension errors = %lld\n",
(long long)adapter->stats.cexterr);
- device_printf(dev, "RX overruns = %ld\n", adapter->rx_overruns);
device_printf(dev, "watchdog timeouts = %ld\n",
adapter->watchdog_events);
- device_printf(dev, "RX MSIX IRQ = %ld TX MSIX IRQ = %ld"
- " LINK MSIX IRQ = %ld\n", adapter->rx_irq,
- adapter->tx_irq , adapter->link_irq);
device_printf(dev, "XON Rcvd = %lld\n",
(long long)adapter->stats.xonrxc);
device_printf(dev, "XON Xmtd = %lld\n",
@@ -5327,9 +5009,7 @@ em_sysctl_int_delay(SYSCTL_HANDLER_ARGS)
struct em_int_delay_info *info;
struct adapter *adapter;
u32 regval;
- int error;
- int usecs;
- int ticks;
+ int error, usecs, ticks;
info = (struct em_int_delay_info *)arg1;
usecs = info->value;
@@ -5378,7 +5058,6 @@ em_add_int_delay_sysctl(struct adapter *adapter, const char *name,
info, 0, em_sysctl_int_delay, "I", description);
}
-#ifndef EM_LEGACY_IRQ
static void
em_add_rx_process_limit(struct adapter *adapter, const char *name,
const char *description, int *limit, int value)
@@ -5388,6 +5067,5 @@ em_add_rx_process_limit(struct adapter *adapter, const char *name,
SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)),
OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description);
}
-#endif
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index 7487a89e0d0b..96d217b420d3 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -52,9 +52,8 @@
* (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
*/
#define EM_MIN_TXD 80
-#define EM_MAX_TXD_82543 256
#define EM_MAX_TXD 4096
-#define EM_DEFAULT_TXD EM_MAX_TXD_82543
+#define EM_DEFAULT_TXD 1024
/*
* EM_RXD - Maximum number of receive Descriptors
@@ -70,9 +69,8 @@
* (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
*/
#define EM_MIN_RXD 80
-#define EM_MAX_RXD_82543 256
#define EM_MAX_RXD 4096
-#define EM_DEFAULT_RXD EM_MAX_RXD_82543
+#define EM_DEFAULT_RXD 1024
/*
* EM_TIDV - Transmit Interrupt Delay Value
@@ -135,16 +133,15 @@
#define EM_RADV 64
/*
- * This parameter controls the duration of transmit watchdog timer.
+ * This parameter controls the max duration of transmit watchdog.
*/
-#define EM_TX_TIMEOUT 5
+#define EM_WATCHDOG (10 * hz)
/*
* This parameter controls when the driver calls the routine to reclaim
* transmit descriptors.
*/
#define EM_TX_CLEANUP_THRESHOLD (adapter->num_tx_desc / 8)
-#define EM_TX_OP_THRESHOLD (adapter->num_tx_desc / 32)
/*
* This parameter controls whether or not autonegotation is enabled.
@@ -182,18 +179,14 @@
#define EM_DEFAULT_PBA 0x00000030
#define EM_SMARTSPEED_DOWNSHIFT 3
#define EM_SMARTSPEED_MAX 15
-#define EM_MAX_INTR 10
+#define EM_MAX_LOOP 10
#define MAX_NUM_MULTICAST_ADDRESSES 128
#define PCI_ANY_ID (~0U)
#define ETHER_ALIGN 2
#define EM_FC_PAUSE_TIME 0x0680
#define EM_EEPROM_APME 0x400;
-
-/* Code compatilbility between 6 and 7 */
-#ifndef ETHER_BPF_MTAP
-#define ETHER_BPF_MTAP BPF_MTAP
-#endif
+#define EM_82544_APME 0x0004;
/*
* TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be
@@ -208,7 +201,6 @@
#define EM_BAR_TYPE(v) ((v) & EM_BAR_TYPE_MASK)
#define EM_BAR_TYPE_MASK 0x00000001
#define EM_BAR_TYPE_MMEM 0x00000000
-#define EM_BAR_TYPE_IO 0x00000001
#define EM_BAR_TYPE_FLASH 0x0014
#define EM_BAR_MEM_TYPE(v) ((v) & EM_BAR_MEM_TYPE_MASK)
#define EM_BAR_MEM_TYPE_MASK 0x00000006
@@ -236,6 +228,7 @@
#define EM_TSO_SIZE (65535 + sizeof(struct ether_vlan_header))
#define EM_TSO_SEG_SIZE 4096 /* Max dma segment size */
#define EM_MSIX_MASK 0x01F00000 /* For 82574 use */
+#define EM_MSIX_LINK 0x01000000 /* For 82574 use */
#define ETH_ZLEN 60
#define ETH_ADDR_LEN 6
#define CSUM_OFFLOAD 7 /* Offload bits in mbuf flag */
@@ -248,18 +241,6 @@
*/
#define EM_EIAC 0x000DC
-/* Used in for 82547 10Mb Half workaround */
-#define EM_PBA_BYTES_SHIFT 0xA
-#define EM_TX_HEAD_ADDR_SHIFT 7
-#define EM_PBA_TX_MASK 0xFFFF0000
-#define EM_FIFO_HDR 0x10
-#define EM_82547_PKT_THRESH 0x3e0
-
-/* Precision Time Sync (IEEE 1588) defines */
-#define ETHERTYPE_IEEE1588 0x88F7
-#define PICOSECS_PER_TICK 20833
-#define TSYNC_PORT 319 /* UDP port for the protocol */
-
/*
* Bus dma allocation structure used by
* e1000_dma_malloc and e1000_dma_free.
@@ -281,59 +262,134 @@ struct em_int_delay_info {
int value; /* Current value in usecs */
};
+/*
+ * The transmit ring, one per tx queue
+ */
+struct tx_ring {
+ struct adapter *adapter;
+ struct mtx tx_mtx;
+ char mtx_name[16];
+ u32 me;
+ u32 msix;
+ u32 ims;
+ bool watchdog_check;
+ int watchdog_time;
+ struct em_dma_alloc txdma;
+ struct e1000_tx_desc *tx_base;
+ struct task tx_task;
+ struct taskqueue *tq;
+ u32 next_avail_desc;
+ u32 next_to_clean;
+ struct em_buffer *tx_buffers;
+ volatile u16 tx_avail;
+ u32 tx_tso; /* last tx was tso */
+ u16 last_hw_offload;
+#if __FreeBSD_version >= 800000
+ struct buf_ring *br;
+#endif
+ /* Interrupt resources */
+ bus_dma_tag_t txtag;
+ void *tag;
+ struct resource *res;
+ unsigned long tx_irq;
+ unsigned long no_desc_avail;
+};
+
+/*
+ * The Receive ring, one per rx queue
+ */
+struct rx_ring {
+ struct adapter *adapter;
+ u32 me;
+ u32 msix;
+ u32 ims;
+ struct mtx rx_mtx;
+ char mtx_name[16];
+ u32 payload;
+ struct task rx_task;
+ struct taskqueue *tq;
+ struct e1000_rx_desc *rx_base;
+ struct em_dma_alloc rxdma;
+ u32 next_to_refresh;
+ u32 next_to_check;
+ struct em_buffer *rx_buffers;
+ struct mbuf *fmp;
+ struct mbuf *lmp;
+
+ /* Interrupt resources */
+ void *tag;
+ struct resource *res;
+ bus_dma_tag_t rxtag;
+ bus_dmamap_t rx_sparemap;
+
+ /* Soft stats */
+ unsigned long rx_irq;
+ unsigned long rx_packets;
+ unsigned long rx_bytes;
+};
+
+
/* Our adapter structure */
struct adapter {
struct ifnet *ifp;
-#if __FreeBSD_version >= 800000
- struct buf_ring *br;
-#endif
struct e1000_hw hw;
/* FreeBSD operating-system-specific structures. */
struct e1000_osdep osdep;
struct device *dev;
+ struct cdev *led_dev;
struct resource *memory;
struct resource *flash;
- struct resource *msix;
-
- struct resource *ioport;
- int io_rid;
+ struct resource *msix_mem;
- /* 82574 may use 3 int vectors */
- struct resource *res[3];
- void *tag[3];
- int rid[3];
+ struct resource *res;
+ void *tag;
+ u32 linkvec;
+ u32 ivars;
struct ifmedia media;
struct callout timer;
- struct callout tx_fifo_timer;
- int watchdog_timer;
- int msi;
+ int msix;
int if_flags;
int max_frame_size;
int min_frame_size;
struct mtx core_mtx;
- struct mtx tx_mtx;
- struct mtx rx_mtx;
int em_insert_vlan_header;
+ u32 ims;
+ bool in_detach;
/* Task for FAST handling */
struct task link_task;
- struct task rxtx_task;
- struct task rx_task;
- struct task tx_task;
+ struct task que_task;
struct taskqueue *tq; /* private task queue */
-#if __FreeBSD_version >= 700029
eventhandler_tag vlan_attach;
eventhandler_tag vlan_detach;
- u32 num_vlans;
-#endif
+
+ u16 num_vlans;
+ u16 num_queues;
+
+ /*
+ * Transmit rings:
+ * Allocated at run time, an array of rings.
+ */
+ struct tx_ring *tx_rings;
+ int num_tx_desc;
+ u32 txd_cmd;
+
+ /*
+ * Receive rings:
+ * Allocated at run time, an array of rings.
+ */
+ struct rx_ring *rx_rings;
+ int num_rx_desc;
+ u32 rx_process_limit;
/* Management and WOL features */
- int wol;
- int has_manage;
+ u32 wol;
+ bool has_manage;
+ bool has_amt;
/* Info about the board itself */
uint8_t link_active;
@@ -345,89 +401,26 @@ struct adapter {
struct em_int_delay_info rx_int_delay;
struct em_int_delay_info rx_abs_int_delay;
- /*
- * Transmit definitions
- *
- * We have an array of num_tx_desc descriptors (handled
- * by the controller) paired with an array of tx_buffers
- * (at tx_buffer_area).
- * The index of the next available descriptor is next_avail_tx_desc.
- * The number of remaining tx_desc is num_tx_desc_avail.
- */
- struct em_dma_alloc txdma; /* bus_dma glue for tx desc */
- struct e1000_tx_desc *tx_desc_base;
- uint32_t next_avail_tx_desc;
- uint32_t next_tx_to_clean;
- volatile uint16_t num_tx_desc_avail;
- uint16_t num_tx_desc;
- uint16_t last_hw_offload;
- uint32_t txd_cmd;
- struct em_buffer *tx_buffer_area;
- bus_dma_tag_t txtag; /* dma tag for tx */
- uint32_t tx_tso; /* last tx was tso */
-
- /*
- * Receive definitions
- *
- * we have an array of num_rx_desc rx_desc (handled by the
- * controller), and paired with an array of rx_buffers
- * (at rx_buffer_area).
- * The next pair to check on receive is at offset next_rx_desc_to_check
- */
- struct em_dma_alloc rxdma; /* bus_dma glue for rx desc */
- struct e1000_rx_desc *rx_desc_base;
- uint32_t next_rx_desc_to_check;
- uint32_t rx_buffer_len;
- uint16_t num_rx_desc;
- int rx_process_limit;
- struct em_buffer *rx_buffer_area;
- bus_dma_tag_t rxtag;
- bus_dmamap_t rx_sparemap;
-
- /*
- * First/last mbuf pointers, for
- * collecting multisegment RX packets.
- */
- struct mbuf *fmp;
- struct mbuf *lmp;
-
/* Misc stats maintained by the driver */
unsigned long dropped_pkts;
unsigned long mbuf_alloc_failed;
unsigned long mbuf_cluster_failed;
- unsigned long no_tx_desc_avail1;
- unsigned long no_tx_desc_avail2;
unsigned long no_tx_map_avail;
unsigned long no_tx_dma_setup;
- unsigned long watchdog_events;
unsigned long rx_overruns;
- unsigned long rx_irq;
- unsigned long tx_irq;
+ unsigned long watchdog_events;
unsigned long link_irq;
- /* 82547 workaround */
- uint32_t tx_fifo_size;
- uint32_t tx_fifo_head;
- uint32_t tx_fifo_head_addr;
- uint64_t tx_fifo_reset_cnt;
- uint64_t tx_fifo_wrk_cnt;
- uint32_t tx_head_addr;
-
- /* For 82544 PCIX Workaround */
- boolean_t pcix_82544;
- boolean_t in_detach;
-
-
struct e1000_hw_stats stats;
};
-/* ******************************************************************************
+/********************************************************************************
* vendor_info_array
*
* This array contains the list of Subvendor/Subdevice IDs on which the driver
* should load.
*
- * ******************************************************************************/
+ ********************************************************************************/
typedef struct _em_vendor_info_t {
unsigned int vendor_id;
unsigned int device_id;
@@ -442,19 +435,6 @@ struct em_buffer {
bus_dmamap_t map; /* bus_dma map for packet */
};
-/* For 82544 PCIX Workaround */
-typedef struct _ADDRESS_LENGTH_PAIR
-{
- uint64_t address;
- uint32_t length;
-} ADDRESS_LENGTH_PAIR, *PADDRESS_LENGTH_PAIR;
-
-typedef struct _DESCRIPTOR_PAIR
-{
- ADDRESS_LENGTH_PAIR descriptor[4];
- uint32_t elements;
-} DESC_ARRAY, *PDESC_ARRAY;
-
#define EM_CORE_LOCK_INIT(_sc, _name) \
mtx_init(&(_sc)->core_mtx, _name, "EM Core Lock", MTX_DEF)
#define EM_TX_LOCK_INIT(_sc, _name) \
diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c
index 8b3290a3f77d..5fdd52c163b6 100644
--- a/sys/dev/e1000/if_igb.c
+++ b/sys/dev/e1000/if_igb.c
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,7 @@
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_device_polling.h"
#include "opt_inet.h"
+#include "opt_altq.h"
#endif
#include <sys/param.h>
@@ -62,10 +63,6 @@
#include <machine/bus.h>
#include <machine/resource.h>
-#ifdef IGB_IEEE1588
-#include <sys/ieee1588.h>
-#endif
-
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
@@ -86,6 +83,7 @@
#include <netinet/udp.h>
#include <machine/in_cksum.h>
+#include <dev/led/led.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
@@ -101,7 +99,7 @@ int igb_display_debug_stats = 0;
/*********************************************************************
* Driver version:
*********************************************************************/
-char igb_driver_version[] = "version - 1.7.3";
+char igb_driver_version[] = "version - 1.9.3";
/*********************************************************************
@@ -123,12 +121,19 @@ static igb_vendor_info_t igb_vendor_info_array[] =
PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576_NS, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82576_NS_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576_SERDES_QUAD,
PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, E1000_DEV_ID_82576_QUAD_COPPER,
PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82580_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82580_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82580_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82580_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82580_COPPER_DUAL,
+ PCI_ANY_ID, PCI_ANY_ID, 0},
/* required last entry */
{ 0, 0, 0, 0, 0}
};
@@ -159,7 +164,6 @@ static int igb_mq_start_locked(struct ifnet *,
static void igb_qflush(struct ifnet *);
#endif
static int igb_ioctl(struct ifnet *, u_long, caddr_t);
-static void igb_watchdog(struct adapter *);
static void igb_init(void *);
static void igb_init_locked(struct adapter *);
static void igb_stop(void *);
@@ -172,7 +176,7 @@ static int igb_allocate_legacy(struct adapter *);
static int igb_setup_msix(struct adapter *);
static void igb_free_pci_resources(struct adapter *);
static void igb_local_timer(void *);
-static int igb_hardware_init(struct adapter *);
+static void igb_reset(struct adapter *);
static void igb_setup_interface(device_t, struct adapter *);
static int igb_allocate_queues(struct adapter *);
static void igb_configure_queues(struct adapter *);
@@ -190,13 +194,19 @@ static int igb_setup_receive_ring(struct rx_ring *);
static void igb_initialize_receive_units(struct adapter *);
static void igb_free_receive_structures(struct adapter *);
static void igb_free_receive_buffers(struct rx_ring *);
+static void igb_free_receive_ring(struct rx_ring *);
static void igb_enable_intr(struct adapter *);
static void igb_disable_intr(struct adapter *);
static void igb_update_stats_counters(struct adapter *);
static bool igb_txeof(struct tx_ring *);
-static bool igb_rxeof(struct rx_ring *, int);
-static void igb_rx_checksum(u32, struct mbuf *, bool);
+
+static __inline void igb_rx_discard(struct rx_ring *, int);
+static __inline void igb_rx_input(struct rx_ring *,
+ struct ifnet *, struct mbuf *, u32);
+
+static bool igb_rxeof(struct igb_queue *, int);
+static void igb_rx_checksum(u32, struct mbuf *, u32);
static int igb_tx_ctx_setup(struct tx_ring *, struct mbuf *);
static bool igb_tso_setup(struct tx_ring *, struct mbuf *, u32 *);
static void igb_set_promisc(struct adapter *);
@@ -204,7 +214,7 @@ static void igb_disable_promisc(struct adapter *);
static void igb_set_multi(struct adapter *);
static void igb_print_hw_stats(struct adapter *);
static void igb_update_link_status(struct adapter *);
-static int igb_get_buf(struct rx_ring *, int, u8);
+static void igb_refresh_mbufs(struct rx_ring *, int);
static void igb_register_vlan(void *, struct ifnet *, u16);
static void igb_unregister_vlan(void *, struct ifnet *, u16);
@@ -225,21 +235,22 @@ static void igb_release_manageability(struct adapter *);
static void igb_get_hw_control(struct adapter *);
static void igb_release_hw_control(struct adapter *);
static void igb_enable_wakeup(device_t);
+static void igb_led_func(void *, int);
static int igb_irq_fast(void *);
static void igb_add_rx_process_limit(struct adapter *, const char *,
const char *, int *, int);
static void igb_handle_rxtx(void *context, int pending);
-static void igb_handle_tx(void *context, int pending);
-static void igb_handle_rx(void *context, int pending);
+static void igb_handle_que(void *context, int pending);
+static void igb_handle_link(void *context, int pending);
/* These are MSIX only irq handlers */
-static void igb_msix_rx(void *);
-static void igb_msix_tx(void *);
+static void igb_msix_que(void *);
static void igb_msix_link(void *);
-/* Adaptive Interrupt Moderation */
-static void igb_update_aim(struct rx_ring *);
+#ifdef DEVICE_POLLING
+static poll_handler_t igb_poll;
+#endif /* POLLING */
/*********************************************************************
* FreeBSD Device Interface Entry Points
@@ -276,31 +287,35 @@ TUNABLE_INT("hw.igb.rxd", &igb_rxd);
TUNABLE_INT("hw.igb.txd", &igb_txd);
/*
-** These parameters are used in Adaptive
-** Interrupt Moderation. The value is set
-** into EITR and controls the interrupt
-** frequency. A variable static scheme can
-** be created by changing the assigned value
-** of igb_ave_latency to the desired value,
-** and then set igb_enable_aim to FALSE.
-** This will result in all EITR registers
-** getting set to that value statically.
+** AIM: Adaptive Interrupt Moderation
+** which means that the interrupt rate
+** is varied over time based on the
+** traffic for that interrupt vector
*/
static int igb_enable_aim = TRUE;
TUNABLE_INT("hw.igb.enable_aim", &igb_enable_aim);
-static int igb_low_latency = IGB_LOW_LATENCY;
-TUNABLE_INT("hw.igb.low_latency", &igb_low_latency);
-static int igb_ave_latency = IGB_AVE_LATENCY;
-TUNABLE_INT("hw.igb.ave_latency", &igb_ave_latency);
-static int igb_bulk_latency = IGB_BULK_LATENCY;
-TUNABLE_INT("hw.igb.bulk_latency", &igb_bulk_latency);
-
+
+/*
+ * MSIX should be the default for best performance,
+ * but this allows it to be forced off for testing.
+ */
+static int igb_enable_msix = 1;
+TUNABLE_INT("hw.igb.enable_msix", &igb_enable_msix);
+
+/*
+ * Header split has seemed to be beneficial in
+ * many circumstances tested, however there have
+ * been some stability issues, so the default is
+ * off.
+ */
+static bool igb_header_split = FALSE;
+TUNABLE_INT("hw.igb.hdr_split", &igb_header_split);
+
/*
-** This will autoconfigure based on the number
-** of CPUs if set to 0. Only a matched pair of
-** TX and RX rings are allowed.
+** This will autoconfigure based on
+** the number of CPUs if left at 0.
*/
-static int igb_num_queues = 1;
+static int igb_num_queues = 0;
TUNABLE_INT("hw.igb.num_queues", &igb_num_queues);
/* How many packets rxeof tries to clean at a time */
@@ -415,21 +430,6 @@ igb_attach(device_t dev)
OID_AUTO, "enable_aim", CTLTYPE_INT|CTLFLAG_RW,
&igb_enable_aim, 1, "Interrupt Moderation");
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
- OID_AUTO, "low_latency", CTLTYPE_INT|CTLFLAG_RW,
- &igb_low_latency, 1, "Low Latency");
-
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
- OID_AUTO, "ave_latency", CTLTYPE_INT|CTLFLAG_RW,
- &igb_ave_latency, 1, "Average Latency");
-
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
- OID_AUTO, "bulk_latency", CTLTYPE_INT|CTLFLAG_RW,
- &igb_bulk_latency, 1, "Bulk Latency");
-
callout_init_mtx(&adapter->timer, &adapter->core_mtx, 0);
/* Determine hardware and mac info */
@@ -540,17 +540,10 @@ igb_attach(device_t dev)
goto err_late;
}
- /* Now Initialize the hardware */
- if (igb_hardware_init(adapter)) {
- device_printf(dev, "Unable to initialize the hardware\n");
- error = EIO;
- goto err_late;
- }
-
/*
** Configure Interrupts
*/
- if (adapter->msix > 1) /* MSIX */
+ if ((adapter->msix > 1) && (igb_enable_msix))
error = igb_allocate_msix(adapter);
else /* MSI or Legacy */
error = igb_allocate_legacy(adapter);
@@ -560,21 +553,8 @@ igb_attach(device_t dev)
/* Setup OS specific network interface */
igb_setup_interface(dev, adapter);
-#ifdef IGB_IEEE1588
- /*
- ** Setup the timer: IEEE 1588 support
- */
- adapter->cycles.read = igb_read_clock;
- adapter->cycles.mask = (u64)-1;
- adapter->cycles.mult = 1;
- adapter->cycles.shift = IGB_TSYNC_SHIFT;
- E1000_WRITE_REG(&adapter->hw, E1000_TIMINCA, (1<<24) |
- IGB_TSYNC_CYCLE_TIME * IGB_TSYNC_SHIFT);
- E1000_WRITE_REG(&adapter->hw, E1000_SYSTIML, 0x00000000);
- E1000_WRITE_REG(&adapter->hw, E1000_SYSTIMH, 0xFF800000);
-
- // JFV - this is not complete yet
-#endif
+ /* Now get a good starting state */
+ igb_reset(adapter);
/* Initialize statistics */
igb_update_stats_counters(adapter);
@@ -607,6 +587,9 @@ igb_attach(device_t dev)
/* Tell the stack that the interface is not active */
adapter->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ adapter->led_dev = led_create(igb_led_func, adapter,
+ device_get_nameunit(dev));
+
INIT_DEBUGOUT("igb_attach: end");
return (0);
@@ -646,6 +629,14 @@ igb_detach(device_t dev)
return (EBUSY);
}
+ if (adapter->led_dev != NULL)
+ led_destroy(adapter->led_dev);
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING)
+ ether_poll_deregister(ifp);
+#endif
+
IGB_CORE_LOCK(adapter);
adapter->in_detach = 1;
igb_stop(adapter);
@@ -787,8 +778,8 @@ igb_start_locked(struct tx_ring *txr, struct ifnet *ifp)
/* Send a copy of the frame to the BPF listener */
ETHER_BPF_MTAP(ifp, m_head);
- /* Set timeout in case hardware has problems transmitting. */
- txr->watchdog_timer = IGB_TX_TIMEOUT;
+ /* Set watchdog on */
+ txr->watchdog_check = TRUE;
}
}
@@ -826,6 +817,9 @@ igb_mq_start(struct ifnet *ifp, struct mbuf *m)
/* Which queue to use */
if ((m->m_flags & M_FLOWID) != 0)
i = m->m_pkthdr.flowid % adapter->num_queues;
+ else
+ i = curcpu % adapter->num_queues;
+
txr = &adapter->tx_rings[i];
if (IGB_TX_TRYLOCK(txr)) {
@@ -842,59 +836,48 @@ igb_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m)
{
struct adapter *adapter = txr->adapter;
struct mbuf *next;
- int err = 0;
+ int err = 0, enq;
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
- err = drbr_enqueue(ifp, txr->br, m);
+ IGB_TX_LOCK_ASSERT(txr);
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING || adapter->link_active == 0) {
+ if (m != NULL)
+ err = drbr_enqueue(ifp, txr->br, m);
return (err);
}
- if (m == NULL) /* Called by tasklet */
- goto process;
-
- /* If nothing queued go right to xmit */
- if (!drbr_needs_enqueue(ifp, txr->br)) {
- if ((err = igb_xmit(txr, &m)) != 0) {
- if (m != NULL)
- err = drbr_enqueue(ifp, txr->br, m);
+ enq = 0;
+ if (m == NULL) {
+ next = drbr_dequeue(ifp, txr->br);
+ } else if (drbr_needs_enqueue(ifp, txr->br)) {
+ if ((err = drbr_enqueue(ifp, txr->br, m)) != 0)
return (err);
- } else {
- /* Success, update stats */
- drbr_stats_update(ifp, m->m_pkthdr.len, m->m_flags);
- /* Send a copy of the frame to the BPF listener */
- ETHER_BPF_MTAP(ifp, m);
- /* Set the watchdog */
- txr->watchdog_timer = IGB_TX_TIMEOUT;
- }
-
- } else if ((err = drbr_enqueue(ifp, txr->br, m)) != 0)
- return (err);
-
-process:
- if (drbr_empty(ifp, txr->br))
- return (err);
-
- /* Process the queue */
- while (TRUE) {
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
- break;
next = drbr_dequeue(ifp, txr->br);
- if (next == NULL)
- break;
+ } else
+ next = m;
+ /* Process the queue */
+ while (next != NULL) {
if ((err = igb_xmit(txr, &next)) != 0) {
if (next != NULL)
err = drbr_enqueue(ifp, txr->br, next);
break;
}
+ enq++;
drbr_stats_update(ifp, next->m_pkthdr.len, next->m_flags);
ETHER_BPF_MTAP(ifp, next);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
+ if (txr->tx_avail <= IGB_TX_OP_THRESHOLD) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ next = drbr_dequeue(ifp, txr->br);
+ }
+ if (enq > 0) {
/* Set the watchdog */
- txr->watchdog_timer = IGB_TX_TIMEOUT;
+ txr->watchdog_check = TRUE;
}
-
- if (txr->tx_avail <= IGB_TX_OP_THRESHOLD)
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
-
return (err);
}
@@ -1011,6 +994,9 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
IGB_CORE_LOCK(adapter);
igb_disable_intr(adapter);
igb_set_multi(adapter);
+#ifdef DEVICE_POLLING
+ if (!(ifp->if_capenable & IFCAP_POLLING))
+#endif
igb_enable_intr(adapter);
IGB_CORE_UNLOCK(adapter);
}
@@ -1037,6 +1023,26 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFCAP (Set Capabilities)");
reinit = 0;
mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+#ifdef DEVICE_POLLING
+ if (mask & IFCAP_POLLING) {
+ if (ifr->ifr_reqcap & IFCAP_POLLING) {
+ error = ether_poll_register(igb_poll, ifp);
+ if (error)
+ return (error);
+ IGB_CORE_LOCK(adapter);
+ igb_disable_intr(adapter);
+ ifp->if_capenable |= IFCAP_POLLING;
+ IGB_CORE_UNLOCK(adapter);
+ } else {
+ error = ether_poll_deregister(ifp);
+ /* Enable interrupt even in error case */
+ IGB_CORE_LOCK(adapter);
+ igb_enable_intr(adapter);
+ ifp->if_capenable &= ~IFCAP_POLLING;
+ IGB_CORE_UNLOCK(adapter);
+ }
+ }
+#endif
if (mask & IFCAP_HWCSUM) {
ifp->if_capenable ^= IFCAP_HWCSUM;
reinit = 1;
@@ -1059,15 +1065,6 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
break;
}
-#ifdef IGB_IEEE1588
- /*
- ** IOCTL support for Precision Time (IEEE 1588) Support
- */
- case SIOCSHWTSTAMP:
- error = igb_hwtstamp_ioctl(adapter, ifp);
- break;
-#endif
-
default:
error = ether_ioctl(ifp, command, data);
break;
@@ -1076,80 +1073,6 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
return (error);
}
-/*********************************************************************
- * Watchdog timer:
- *
- * This routine is called from the local timer every second.
- * As long as transmit descriptors are being cleaned the value
- * is non-zero and we do nothing. Reaching 0 indicates a tx hang
- * and we then reset the device.
- *
- **********************************************************************/
-
-static void
-igb_watchdog(struct adapter *adapter)
-{
- struct tx_ring *txr = adapter->tx_rings;
- bool tx_hang = FALSE;
-
- IGB_CORE_LOCK_ASSERT(adapter);
-
- /*
- ** The timer is set to 5 every time start() queues a packet.
- ** Then txeof keeps resetting it as long as it cleans at
- ** least one descriptor.
- ** Finally, anytime all descriptors are clean the timer is
- ** set to 0.
- **
- ** With TX Multiqueue we need to check every queue's timer,
- ** if any time out we do the reset.
- */
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- IGB_TX_LOCK(txr);
- if (txr->watchdog_timer == 0 ||
- (--txr->watchdog_timer)) {
- IGB_TX_UNLOCK(txr);
- continue;
- } else {
- tx_hang = TRUE;
- IGB_TX_UNLOCK(txr);
- break;
- }
- }
- if (tx_hang == FALSE)
- return;
-
- /* If we are in this routine because of pause frames, then
- * don't reset the hardware.
- */
- if (E1000_READ_REG(&adapter->hw, E1000_STATUS) &
- E1000_STATUS_TXOFF) {
- txr = adapter->tx_rings; /* reset pointer */
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- IGB_TX_LOCK(txr);
- txr->watchdog_timer = IGB_TX_TIMEOUT;
- IGB_TX_UNLOCK(txr);
- }
- return;
- }
-
- if (e1000_check_for_link(&adapter->hw) == 0)
- device_printf(adapter->dev, "watchdog timeout -- resetting\n");
-
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- device_printf(adapter->dev, "Queue(%d) tdh = %d, tdt = %d\n",
- i, E1000_READ_REG(&adapter->hw, E1000_TDH(i)),
- E1000_READ_REG(&adapter->hw, E1000_TDT(i)));
- device_printf(adapter->dev, "Queue(%d) desc avail = %d,"
- " Next Desc to Clean = %d\n", i, txr->tx_avail,
- txr->next_to_clean);
- }
-
- adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
- adapter->watchdog_events++;
-
- igb_init_locked(adapter);
-}
/*********************************************************************
* Init entry point
@@ -1165,29 +1088,16 @@ igb_watchdog(struct adapter *adapter)
static void
igb_init_locked(struct adapter *adapter)
{
- struct rx_ring *rxr = adapter->rx_rings;
- struct tx_ring *txr = adapter->tx_rings;
struct ifnet *ifp = adapter->ifp;
device_t dev = adapter->dev;
- u32 pba = 0;
INIT_DEBUGOUT("igb_init: begin");
IGB_CORE_LOCK_ASSERT(adapter);
- igb_stop(adapter);
+ igb_disable_intr(adapter);
+ callout_stop(&adapter->timer);
- /*
- * Packet Buffer Allocation (PBA)
- * Writing PBA sets the receive portion of the buffer
- * the remainder is used for the transmit buffer.
- */
- if (adapter->hw.mac.type == e1000_82575) {
- INIT_DEBUGOUT1("igb_init: pba=%dK",pba);
- pba = E1000_PBA_32K; /* 32K for Rx, 16K for Tx */
- E1000_WRITE_REG(&adapter->hw, E1000_PBA, pba);
- }
-
/* Get the latest mac address, User can use a LAA */
bcopy(IF_LLADDR(adapter->ifp), adapter->hw.mac.addr,
ETHER_ADDR_LEN);
@@ -1195,11 +1105,7 @@ igb_init_locked(struct adapter *adapter)
/* Put the address into the Receive Address Array */
e1000_rar_set(&adapter->hw, adapter->hw.mac.addr, 0);
- /* Initialize the hardware */
- if (igb_hardware_init(adapter)) {
- device_printf(dev, "Unable to initialize the hardware\n");
- return;
- }
+ igb_reset(adapter);
igb_update_link_status(adapter);
E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN);
@@ -1239,7 +1145,6 @@ igb_init_locked(struct adapter *adapter)
/* Prepare receive descriptors and buffers */
if (igb_setup_receive_structures(adapter)) {
device_printf(dev, "Could not setup receive structures\n");
- igb_stop(adapter);
return;
}
igb_initialize_receive_units(adapter);
@@ -1259,25 +1164,20 @@ igb_init_locked(struct adapter *adapter)
/* Set up VLAN tag offload and filter */
igb_setup_vlan_hw_support(adapter);
- /* Set default RX interrupt moderation */
- for (int i = 0; i < adapter->num_queues; i++, rxr++) {
- E1000_WRITE_REG(&adapter->hw,
- E1000_EITR(rxr->msix), igb_ave_latency);
- rxr->eitr_setting = igb_ave_latency;
- }
-
- /* Set TX interrupt rate & reset TX watchdog */
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- E1000_WRITE_REG(&adapter->hw,
- E1000_EITR(txr->msix), igb_ave_latency);
- txr->watchdog_timer = FALSE;
- }
-
+ /* this clears any pending interrupts */
+ E1000_READ_REG(&adapter->hw, E1000_ICR);
+#ifdef DEVICE_POLLING
+ /*
+ * Only enable interrupts if we are not polling, make sure
+ * they are off otherwise.
+ */
+ if (ifp->if_capenable & IFCAP_POLLING)
+ igb_disable_intr(adapter);
+ else
+#endif /* DEVICE_POLLING */
{
- /* this clears any pending interrupts */
- E1000_READ_REG(&adapter->hw, E1000_ICR);
- igb_enable_intr(adapter);
- E1000_WRITE_REG(&adapter->hw, E1000_ICS, E1000_ICS_LSC);
+ igb_enable_intr(adapter);
+ E1000_WRITE_REG(&adapter->hw, E1000_ICS, E1000_ICS_LSC);
}
/* Don't reset the phy next time init gets called */
@@ -1298,15 +1198,15 @@ igb_init(void *arg)
static void
igb_handle_rxtx(void *context, int pending)
{
- struct adapter *adapter = context;
- struct tx_ring *txr = adapter->tx_rings;
- struct rx_ring *rxr = adapter->rx_rings;
- struct ifnet *ifp;
+ struct igb_queue *que = context;
+ struct adapter *adapter = que->adapter;
+ struct tx_ring *txr = adapter->tx_rings;
+ struct ifnet *ifp;
ifp = adapter->ifp;
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if (igb_rxeof(rxr, adapter->rx_process_limit))
+ if (igb_rxeof(que, adapter->rx_process_limit))
taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
IGB_TX_LOCK(txr);
igb_txeof(txr);
@@ -1325,40 +1225,50 @@ igb_handle_rxtx(void *context, int pending)
}
static void
-igb_handle_rx(void *context, int pending)
+igb_handle_que(void *context, int pending)
{
- struct rx_ring *rxr = context;
- struct adapter *adapter = rxr->adapter;
- struct ifnet *ifp = adapter->ifp;
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- if (igb_rxeof(rxr, adapter->rx_process_limit) != 0)
- /* More to clean, schedule another task */
- taskqueue_enqueue(adapter->tq, &rxr->rx_task);
-
-}
+ struct igb_queue *que = context;
+ struct adapter *adapter = que->adapter;
+ struct tx_ring *txr = que->txr;
+ struct ifnet *ifp = adapter->ifp;
+ u32 loop = IGB_MAX_LOOP;
+ bool more;
-static void
-igb_handle_tx(void *context, int pending)
-{
- struct tx_ring *txr = context;
- struct adapter *adapter = txr->adapter;
- struct ifnet *ifp = adapter->ifp;
+ /* RX first */
+ do {
+ more = igb_rxeof(que, -1);
+ } while (loop-- && more);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- IGB_TX_LOCK(txr);
- igb_txeof(txr);
+ if (IGB_TX_TRYLOCK(txr)) {
+ loop = IGB_MAX_LOOP;
+ do {
+ more = igb_txeof(txr);
+ } while (loop-- && more);
#if __FreeBSD_version >= 800000
- if (!drbr_empty(ifp, txr->br))
- igb_mq_start_locked(ifp, txr, NULL);
+ igb_mq_start_locked(ifp, txr, NULL);
#else
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
igb_start_locked(txr, ifp);
#endif
IGB_TX_UNLOCK(txr);
}
+
+ /* Reenable this interrupt */
+#ifdef DEVICE_POLLING
+ if (!(ifp->if_capenable & IFCAP_POLLING))
+#endif
+ E1000_WRITE_REG(&adapter->hw, E1000_EIMS, que->eims);
}
+/* Deal with link in a sleepable context */
+static void
+igb_handle_link(void *context, int pending)
+{
+ struct adapter *adapter = context;
+
+ adapter->hw.mac.get_link_status = 1;
+ igb_update_link_status(adapter);
+}
/*********************************************************************
*
@@ -1395,75 +1305,155 @@ igb_irq_fast(void *arg)
taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
/* Link status change */
- if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
- adapter->hw.mac.get_link_status = 1;
- igb_update_link_status(adapter);
- }
+ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))
+ taskqueue_enqueue(adapter->tq, &adapter->link_task);
if (reg_icr & E1000_ICR_RXO)
adapter->rx_overruns++;
return FILTER_HANDLED;
}
-
+#ifdef DEVICE_POLLING
/*********************************************************************
*
- * MSIX TX Interrupt Service routine
+ * Legacy polling routine : if using this code you MUST be sure that
+ * multiqueue is not defined, ie, set igb_num_queues to 1.
*
- **********************************************************************/
+ *********************************************************************/
+#if __FreeBSD_version >= 800000
+#define POLL_RETURN_COUNT(a) (a)
+static int
+#else
+#define POLL_RETURN_COUNT(a)
static void
-igb_msix_tx(void *arg)
+#endif
+igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
- struct tx_ring *txr = arg;
- struct adapter *adapter = txr->adapter;
- u32 loop = IGB_MAX_LOOP;
- bool more;
+ struct adapter *adapter = ifp->if_softc;
+ struct igb_queue *que = adapter->queues;
+ struct tx_ring *txr = adapter->tx_rings;
+ u32 reg_icr, rx_done = 0;
+ u32 loop = IGB_MAX_LOOP;
+ bool more;
- ++txr->tx_irq;
- IGB_TX_LOCK(txr);
+ IGB_CORE_LOCK(adapter);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ IGB_CORE_UNLOCK(adapter);
+ return POLL_RETURN_COUNT(rx_done);
+ }
+
+ if (cmd == POLL_AND_CHECK_STATUS) {
+ reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
+ /* Link status change */
+ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))
+ taskqueue_enqueue(adapter->tq, &adapter->link_task);
+
+ if (reg_icr & E1000_ICR_RXO)
+ adapter->rx_overruns++;
+ }
+ IGB_CORE_UNLOCK(adapter);
+
+ /* TODO: rx_count */
+ rx_done = igb_rxeof(que, count) ? 1 : 0;
+ IGB_TX_LOCK(txr);
do {
more = igb_txeof(txr);
} while (loop-- && more);
-
+#if __FreeBSD_version >= 800000
+ if (!drbr_empty(ifp, txr->br))
+ igb_mq_start_locked(ifp, txr, NULL);
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ igb_start_locked(txr, ifp);
+#endif
IGB_TX_UNLOCK(txr);
-
- /* Schedule a clean task */
- taskqueue_enqueue(adapter->tq, &txr->tx_task);
-
- /* Reenable this interrupt */
- E1000_WRITE_REG(&adapter->hw, E1000_EIMS, txr->eims);
- return;
+ return POLL_RETURN_COUNT(rx_done);
}
+#endif /* DEVICE_POLLING */
/*********************************************************************
*
- * MSIX RX Interrupt Service routine
+ * MSIX TX Interrupt Service routine
*
**********************************************************************/
-
static void
-igb_msix_rx(void *arg)
+igb_msix_que(void *arg)
{
- struct rx_ring *rxr = arg;
- struct adapter *adapter = rxr->adapter;
- u32 loop = IGB_MAX_LOOP;
- bool more;
+ struct igb_queue *que = arg;
+ struct adapter *adapter = que->adapter;
+ struct tx_ring *txr = que->txr;
+ struct rx_ring *rxr = que->rxr;
+ u32 newitr = 0;
+ bool more_tx, more_rx;
- ++rxr->rx_irq;
- do {
- more = igb_rxeof(rxr, adapter->rx_process_limit);
- } while (loop-- && more);
+ E1000_WRITE_REG(&adapter->hw, E1000_EIMC, que->eims);
+ ++que->irqs;
+
+ IGB_TX_LOCK(txr);
+ more_tx = igb_txeof(txr);
+ IGB_TX_UNLOCK(txr);
- /* Update interrupt rate */
- if (igb_enable_aim == TRUE)
- igb_update_aim(rxr);
+ more_rx = igb_rxeof(que, adapter->rx_process_limit);
- /* Schedule another clean */
- taskqueue_enqueue(adapter->tq, &rxr->rx_task);
+ if (igb_enable_aim == FALSE)
+ goto no_calc;
+ /*
+ ** Do Adaptive Interrupt Moderation:
+ ** - Write out last calculated setting
+ ** - Calculate based on average size over
+ ** the last interval.
+ */
+ if (que->eitr_setting)
+ E1000_WRITE_REG(&adapter->hw,
+ E1000_EITR(que->msix), que->eitr_setting);
+
+ que->eitr_setting = 0;
- /* Reenable this interrupt */
- E1000_WRITE_REG(&adapter->hw, E1000_EIMS, rxr->eims);
+ /* Idle, do nothing */
+ if ((txr->bytes == 0) && (rxr->bytes == 0))
+ goto no_calc;
+
+ /* Used half Default if sub-gig */
+ if (adapter->link_speed != 1000)
+ newitr = IGB_DEFAULT_ITR / 2;
+ else {
+ if ((txr->bytes) && (txr->packets))
+ newitr = txr->bytes/txr->packets;
+ if ((rxr->bytes) && (rxr->packets))
+ newitr = max(newitr,
+ (rxr->bytes / rxr->packets));
+ newitr += 24; /* account for hardware frame, crc */
+ /* set an upper boundary */
+ newitr = min(newitr, 3000);
+ /* Be nice to the mid range */
+ if ((newitr > 300) && (newitr < 1200))
+ newitr = (newitr / 3);
+ else
+ newitr = (newitr / 2);
+ }
+ newitr &= 0x7FFC; /* Mask invalid bits */
+ if (adapter->hw.mac.type == e1000_82575)
+ newitr |= newitr << 16;
+ else
+ newitr |= 0x8000000;
+
+ /* save for next interrupt */
+ que->eitr_setting = newitr;
+
+ /* Reset state */
+ txr->bytes = 0;
+ txr->packets = 0;
+ rxr->bytes = 0;
+ rxr->packets = 0;
+
+no_calc:
+ /* Schedule a clean task if needed*/
+ if (more_tx || more_rx)
+ taskqueue_enqueue(que->tq, &que->que_task);
+ else
+ /* Reenable this interrupt */
+ E1000_WRITE_REG(&adapter->hw, E1000_EIMS, que->eims);
return;
}
@@ -1484,8 +1474,7 @@ igb_msix_link(void *arg)
icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
if (!(icr & E1000_ICR_LSC))
goto spurious;
- adapter->hw.mac.get_link_status = 1;
- igb_update_link_status(adapter);
+ taskqueue_enqueue(adapter->tq, &adapter->link_task);
spurious:
/* Rearm */
@@ -1495,59 +1484,6 @@ spurious:
}
-/*
-** Routine to adjust the RX EITR value based on traffic,
-** its a simple three state model, but seems to help.
-**
-** Note that the three EITR values are tuneable using
-** sysctl in real time. The feature can be effectively
-** nullified by setting them equal.
-*/
-#define BULK_THRESHOLD 10000
-#define AVE_THRESHOLD 1600
-
-static void
-igb_update_aim(struct rx_ring *rxr)
-{
- struct adapter *adapter = rxr->adapter;
- u32 olditr, newitr;
-
- /* Update interrupt moderation based on traffic */
- olditr = rxr->eitr_setting;
- newitr = olditr;
-
- /* Idle, don't change setting */
- if (rxr->bytes == 0)
- return;
-
- if (olditr == igb_low_latency) {
- if (rxr->bytes > AVE_THRESHOLD)
- newitr = igb_ave_latency;
- } else if (olditr == igb_ave_latency) {
- if (rxr->bytes < AVE_THRESHOLD)
- newitr = igb_low_latency;
- else if (rxr->bytes > BULK_THRESHOLD)
- newitr = igb_bulk_latency;
- } else if (olditr == igb_bulk_latency) {
- if (rxr->bytes < BULK_THRESHOLD)
- newitr = igb_ave_latency;
- }
-
- if (olditr != newitr) {
- /* Change interrupt rate */
- rxr->eitr_setting = newitr;
- if (adapter->hw.mac.type == e1000_82575)
- newitr |= newitr << 16;
- else
- newitr |= 0x8000000;
- E1000_WRITE_REG(&adapter->hw, E1000_EITR(rxr->me), newitr);
- }
-
- rxr->bytes = 0;
- return;
-}
-
-
/*********************************************************************
*
* Media Ioctl callback
@@ -1780,15 +1716,14 @@ igb_xmit(struct tx_ring *txr, struct mbuf **m_headp)
} else if (igb_tx_ctx_setup(txr, m_head))
olinfo_status |= E1000_TXD_POPTS_TXSM << 8;
-#ifdef IGB_IEEE1588
- /* This is changing soon to an mtag detection */
- if (we detect this mbuf has a TSTAMP mtag)
- cmd_type_len |= E1000_ADVTXD_MAC_TSTAMP;
-#endif
/* Calculate payload length */
olinfo_status |= ((m_head->m_pkthdr.len - hdrlen)
<< E1000_ADVTXD_PAYLEN_SHIFT);
+ /* 82575 needs the queue index added */
+ if (adapter->hw.mac.type == e1000_82575)
+ olinfo_status |= txr->me << 4;
+
/* Set up our transmit descriptors */
i = txr->next_avail_desc;
for (j = 0; j < nsegs; j++) {
@@ -1801,8 +1736,7 @@ igb_xmit(struct tx_ring *txr, struct mbuf **m_headp)
seg_len = segs[j].ds_len;
txd->read.buffer_addr = htole64(seg_addr);
- txd->read.cmd_type_len = htole32(
- adapter->txd_cmd | cmd_type_len | seg_len);
+ txd->read.cmd_type_len = htole32(cmd_type_len | seg_len);
txd->read.olinfo_status = htole32(olinfo_status);
last = i;
if (++i == adapter->num_tx_desc)
@@ -1825,13 +1759,14 @@ igb_xmit(struct tx_ring *txr, struct mbuf **m_headp)
* and Report Status (RS)
*/
txd->read.cmd_type_len |=
- htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS);
+ htole32(E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS);
/*
* Keep track in the first buffer which
* descriptor will be written back
*/
tx_buffer = &txr->tx_buffers[first];
tx_buffer->next_eop = last;
+ txr->watchdog_time = ticks;
/*
* Advance the Transmit Descriptor Tail (TDT), this tells the E1000
@@ -1896,7 +1831,11 @@ igb_set_multi(struct adapter *adapter)
IOCTL_DEBUGOUT("igb_set_multi: begin");
+#if __FreeBSD_version < 800000
+ IF_ADDR_LOCK(ifp);
+#else
if_maddr_rlock(ifp);
+#endif
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
@@ -1908,7 +1847,11 @@ igb_set_multi(struct adapter *adapter)
&mta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN);
mcnt++;
}
+#if __FreeBSD_version < 800000
+ IF_ADDR_UNLOCK(ifp);
+#else
if_maddr_runlock(ifp);
+#endif
if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) {
reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
@@ -1920,17 +1863,20 @@ igb_set_multi(struct adapter *adapter)
/*********************************************************************
- * Timer routine
- *
- * This routine checks for link status and updates statistics.
+ * Timer routine:
+ * This routine checks for link status,
+ * updates statistics, and does the watchdog.
*
**********************************************************************/
static void
igb_local_timer(void *arg)
{
- struct adapter *adapter = arg;
- struct ifnet *ifp = adapter->ifp;
+ struct adapter *adapter = arg;
+ struct ifnet *ifp = adapter->ifp;
+ device_t dev = adapter->dev;
+ struct tx_ring *txr = adapter->tx_rings;
+
IGB_CORE_LOCK_ASSERT(adapter);
@@ -1940,17 +1886,35 @@ igb_local_timer(void *arg)
if (igb_display_debug_stats && ifp->if_drv_flags & IFF_DRV_RUNNING)
igb_print_hw_stats(adapter);
- /*
- * Each second we check the watchdog to
- * protect against hardware hangs.
- */
- igb_watchdog(adapter);
+ /*
+ ** Watchdog: check for time since any descriptor was cleaned
+ */
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ if (txr->watchdog_check == FALSE)
+ continue;
+ if ((ticks - txr->watchdog_time) > IGB_WATCHDOG)
+ goto timeout;
+ }
/* Trigger an RX interrupt on all queues */
+#ifdef DEVICE_POLLING
+ if (!(ifp->if_capenable & IFCAP_POLLING))
+#endif
E1000_WRITE_REG(&adapter->hw, E1000_EICS, adapter->rx_mask);
-
callout_reset(&adapter->timer, hz, igb_local_timer, adapter);
+ return;
+timeout:
+ device_printf(adapter->dev, "Watchdog timeout -- resetting\n");
+ device_printf(dev,"Queue(%d) tdh = %d, hw tdt = %d\n", txr->me,
+ E1000_READ_REG(&adapter->hw, E1000_TDH(txr->me)),
+ E1000_READ_REG(&adapter->hw, E1000_TDT(txr->me)));
+ device_printf(dev,"TX(%d) desc avail = %d,"
+ "Next TX to Clean = %d\n",
+ txr->me, txr->tx_avail, txr->next_to_clean);
+ adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ adapter->watchdog_events++;
+ igb_init_locked(adapter);
}
static void
@@ -1997,6 +1961,7 @@ igb_update_link_status(struct adapter *adapter)
"Full Duplex" : "Half Duplex"));
adapter->link_active = 1;
ifp->if_baudrate = adapter->link_speed * 1000000;
+ /* This can sleep */
if_link_state_change(ifp, LINK_STATE_UP);
} else if (!link_check && (adapter->link_active == 1)) {
ifp->if_baudrate = adapter->link_speed = 0;
@@ -2004,10 +1969,11 @@ igb_update_link_status(struct adapter *adapter)
if (bootverbose)
device_printf(dev, "Link is Down\n");
adapter->link_active = 0;
+ /* This can sleep */
if_link_state_change(ifp, LINK_STATE_DOWN);
/* Turn off watchdogs */
for (int i = 0; i < adapter->num_queues; i++, txr++)
- txr->watchdog_timer = FALSE;
+ txr->watchdog_check = FALSE;
}
}
@@ -2023,6 +1989,7 @@ igb_stop(void *arg)
{
struct adapter *adapter = arg;
struct ifnet *ifp = adapter->ifp;
+ struct tx_ring *txr = adapter->tx_rings;
IGB_CORE_LOCK_ASSERT(adapter);
@@ -2035,8 +2002,18 @@ igb_stop(void *arg)
/* Tell the stack that the interface is no longer active */
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ /* Unarm watchdog timer. */
+ for (int i = 0; i < adapter->num_queues; i++, txr++) {
+ IGB_TX_LOCK(txr);
+ txr->watchdog_check = FALSE;
+ IGB_TX_UNLOCK(txr);
+ }
+
e1000_reset_hw(&adapter->hw);
E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0);
+
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
}
@@ -2114,8 +2091,9 @@ igb_allocate_pci_resources(struct adapter *adapter)
static int
igb_allocate_legacy(struct adapter *adapter)
{
- device_t dev = adapter->dev;
- int error, rid = 0;
+ device_t dev = adapter->dev;
+ struct igb_queue *que = adapter->queues;
+ int error, rid = 0;
/* Turn off all interrupts */
E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff);
@@ -2137,7 +2115,9 @@ igb_allocate_legacy(struct adapter *adapter)
* Try allocating a fast interrupt and the associated deferred
* processing contexts.
*/
- TASK_INIT(&adapter->rxtx_task, 0, igb_handle_rxtx, adapter);
+ TASK_INIT(&adapter->rxtx_task, 0, igb_handle_rxtx, que);
+ /* Make tasklet for deferred link handling */
+ TASK_INIT(&adapter->link_task, 0, igb_handle_link, adapter);
adapter->tq = taskqueue_create_fast("igb_taskq", M_NOWAIT,
taskqueue_thread_enqueue, &adapter->tq);
taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq",
@@ -2158,96 +2138,56 @@ igb_allocate_legacy(struct adapter *adapter)
/*********************************************************************
*
- * Setup the MSIX Interrupt handlers:
+ * Setup the MSIX Queue Interrupt handlers:
*
**********************************************************************/
static int
igb_allocate_msix(struct adapter *adapter)
{
- device_t dev = adapter->dev;
- struct tx_ring *txr = adapter->tx_rings;
- struct rx_ring *rxr = adapter->rx_rings;
- int error, rid, vector = 0;
+ device_t dev = adapter->dev;
+ struct igb_queue *que = adapter->queues;
+ int error, rid, vector = 0;
- /*
- * Setup the interrupt handlers
- */
-
- /* TX Setup */
- for (int i = 0; i < adapter->num_queues; i++, vector++, txr++) {
- rid = vector +1;
- txr->res = bus_alloc_resource_any(dev,
- SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
- if (txr->res == NULL) {
- device_printf(dev,
- "Unable to allocate bus resource: "
- "MSIX TX Interrupt\n");
- return (ENXIO);
- }
- error = bus_setup_intr(dev, txr->res,
- INTR_TYPE_NET | INTR_MPSAFE, NULL,
- igb_msix_tx, txr, &txr->tag);
- if (error) {
- txr->res = NULL;
- device_printf(dev, "Failed to register TX handler");
- return (error);
- }
- /* Make tasklet for deferred handling - one per queue */
- TASK_INIT(&txr->tx_task, 0, igb_handle_tx, txr);
- txr->msix = vector;
- if (adapter->hw.mac.type == e1000_82575)
- txr->eims = E1000_EICR_TX_QUEUE0 << i;
- else
- txr->eims = 1 << vector;
- /*
- ** Bind the msix vector, and thus the
- ** ring to the corresponding cpu.
- */
- if (adapter->num_queues > 1)
- bus_bind_intr(dev, txr->res, i);
- }
- /* RX Setup */
- for (int i = 0; i < adapter->num_queues; i++, vector++, rxr++) {
+ for (int i = 0; i < adapter->num_queues; i++, vector++, que++) {
rid = vector +1;
- rxr->res = bus_alloc_resource_any(dev,
+ que->res = bus_alloc_resource_any(dev,
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
- if (rxr->res == NULL) {
+ if (que->res == NULL) {
device_printf(dev,
"Unable to allocate bus resource: "
- "MSIX RX Interrupt\n");
+ "MSIX Queue Interrupt\n");
return (ENXIO);
}
- error = bus_setup_intr(dev, rxr->res,
+ error = bus_setup_intr(dev, que->res,
INTR_TYPE_NET | INTR_MPSAFE, NULL,
- igb_msix_rx, rxr, &rxr->tag);
+ igb_msix_que, que, &que->tag);
if (error) {
- rxr->res = NULL;
- device_printf(dev, "Failed to register RX handler");
+ que->res = NULL;
+ device_printf(dev, "Failed to register Queue handler");
return (error);
}
- /* Make tasklet for deferred handling - one per queue */
- TASK_INIT(&rxr->rx_task, 0, igb_handle_rx, rxr);
- rxr->msix = vector;
+ que->msix = vector;
if (adapter->hw.mac.type == e1000_82575)
- rxr->eims = E1000_EICR_RX_QUEUE0 << i;
+ que->eims = E1000_EICR_TX_QUEUE0 << i;
else
- rxr->eims = 1 << vector;
- /* Get a mask for local timer */
- adapter->rx_mask |= rxr->eims;
+ que->eims = 1 << vector;
/*
** Bind the msix vector, and thus the
- ** ring to the corresponding cpu.
- ** Notice that this makes an RX/TX pair
- ** bound to each CPU, limited by the MSIX
- ** vectors.
+ ** rings to the corresponding cpu.
*/
if (adapter->num_queues > 1)
- bus_bind_intr(dev, rxr->res, i);
+ bus_bind_intr(dev, que->res, i);
+ /* Make tasklet for deferred handling */
+ TASK_INIT(&que->que_task, 0, igb_handle_que, que);
+ que->tq = taskqueue_create_fast("igb_que", M_NOWAIT,
+ taskqueue_thread_enqueue, &que->tq);
+ taskqueue_start_threads(&que->tq, 1, PI_NET, "%s que",
+ device_get_nameunit(adapter->dev));
}
/* And Link */
- rid = vector +1;
+ rid = vector + 1;
adapter->res = bus_alloc_resource_any(dev,
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
if (adapter->res == NULL) {
@@ -2263,9 +2203,12 @@ igb_allocate_msix(struct adapter *adapter)
return (error);
}
adapter->linkvec = vector;
- adapter->tq = taskqueue_create_fast("igb_taskq", M_NOWAIT,
+
+ /* Make tasklet for deferred handling */
+ TASK_INIT(&adapter->link_task, 0, igb_handle_link, adapter);
+ adapter->tq = taskqueue_create_fast("igb_link", M_NOWAIT,
taskqueue_thread_enqueue, &adapter->tq);
- taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq",
+ taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s link",
device_get_nameunit(adapter->dev));
return (0);
@@ -2275,52 +2218,86 @@ igb_allocate_msix(struct adapter *adapter)
static void
igb_configure_queues(struct adapter *adapter)
{
- struct e1000_hw *hw = &adapter->hw;
- struct tx_ring *txr;
- struct rx_ring *rxr;
+ struct e1000_hw *hw = &adapter->hw;
+ struct igb_queue *que;
+ u32 tmp, ivar = 0;
+ u32 newitr = IGB_DEFAULT_ITR;
- /* Turn on MSIX */
- /*
- ** 82576 uses IVARs to route MSI/X
- ** interrupts, its not very intuitive,
- ** study the code carefully :)
- */
- if (adapter->hw.mac.type == e1000_82576) {
- u32 ivar = 0;
- /* First turn on the capability */
+ /* First turn on RSS capability */
+ if (adapter->hw.mac.type > e1000_82575)
E1000_WRITE_REG(hw, E1000_GPIE,
- E1000_GPIE_MSIX_MODE |
- E1000_GPIE_EIAME |
+ E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME |
E1000_GPIE_PBA | E1000_GPIE_NSICR);
- /* RX */
+
+ /* Turn on MSIX */
+ switch (adapter->hw.mac.type) {
+ case e1000_82580:
+ /* RX entries */
+ for (int i = 0; i < adapter->num_queues; i++) {
+ u32 index = i >> 1;
+ ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index);
+ que = &adapter->queues[i];
+ if (i & 1) {
+ ivar &= 0xFF00FFFF;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 16;
+ } else {
+ ivar &= 0xFFFFFF00;
+ ivar |= que->msix | E1000_IVAR_VALID;
+ }
+ E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar);
+ }
+ /* TX entries */
+ for (int i = 0; i < adapter->num_queues; i++) {
+ u32 index = i >> 1;
+ ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index);
+ que = &adapter->queues[i];
+ if (i & 1) {
+ ivar &= 0x00FFFFFF;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 24;
+ } else {
+ ivar &= 0xFFFF00FF;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 8;
+ }
+ E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar);
+ adapter->eims_mask |= que->eims;
+ }
+
+ /* And for the link interrupt */
+ ivar = (adapter->linkvec | E1000_IVAR_VALID) << 8;
+ adapter->link_mask = 1 << adapter->linkvec;
+ adapter->eims_mask |= adapter->link_mask;
+ E1000_WRITE_REG(hw, E1000_IVAR_MISC, ivar);
+ break;
+ case e1000_82576:
+ /* RX entries */
for (int i = 0; i < adapter->num_queues; i++) {
u32 index = i & 0x7; /* Each IVAR has two entries */
ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index);
- rxr = &adapter->rx_rings[i];
+ que = &adapter->queues[i];
if (i < 8) {
ivar &= 0xFFFFFF00;
- ivar |= rxr->msix | E1000_IVAR_VALID;
+ ivar |= que->msix | E1000_IVAR_VALID;
} else {
ivar &= 0xFF00FFFF;
- ivar |= (rxr->msix | E1000_IVAR_VALID) << 16;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 16;
}
E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar);
- adapter->eims_mask |= rxr->eims;
+ adapter->eims_mask |= que->eims;
}
- /* TX */
+ /* TX entries */
for (int i = 0; i < adapter->num_queues; i++) {
u32 index = i & 0x7; /* Each IVAR has two entries */
ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index);
- txr = &adapter->tx_rings[i];
+ que = &adapter->queues[i];
if (i < 8) {
ivar &= 0xFFFF00FF;
- ivar |= (txr->msix | E1000_IVAR_VALID) << 8;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 8;
} else {
ivar &= 0x00FFFFFF;
- ivar |= (txr->msix | E1000_IVAR_VALID) << 24;
+ ivar |= (que->msix | E1000_IVAR_VALID) << 24;
}
E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar);
- adapter->eims_mask |= txr->eims;
+ adapter->eims_mask |= que->eims;
}
/* And for the link interrupt */
@@ -2328,11 +2305,10 @@ igb_configure_queues(struct adapter *adapter)
adapter->link_mask = 1 << adapter->linkvec;
adapter->eims_mask |= adapter->link_mask;
E1000_WRITE_REG(hw, E1000_IVAR_MISC, ivar);
- } else
- { /* 82575 */
- int tmp;
+ break;
- /* enable MSI-X PBA support*/
+ case e1000_82575:
+ /* enable MSI-X support*/
tmp = E1000_READ_REG(hw, E1000_CTRL_EXT);
tmp |= E1000_CTRL_EXT_PBA_CLR;
/* Auto-Mask interrupts upon ICR read. */
@@ -2340,20 +2316,15 @@ igb_configure_queues(struct adapter *adapter)
tmp |= E1000_CTRL_EXT_IRCA;
E1000_WRITE_REG(hw, E1000_CTRL_EXT, tmp);
- /* TX */
- for (int i = 0; i < adapter->num_queues; i++) {
- txr = &adapter->tx_rings[i];
- E1000_WRITE_REG(hw, E1000_MSIXBM(txr->msix),
- txr->eims);
- adapter->eims_mask |= txr->eims;
- }
-
- /* RX */
+ /* Queues */
for (int i = 0; i < adapter->num_queues; i++) {
- rxr = &adapter->rx_rings[i];
- E1000_WRITE_REG(hw, E1000_MSIXBM(rxr->msix),
- rxr->eims);
- adapter->eims_mask |= rxr->eims;
+ que = &adapter->queues[i];
+ tmp = E1000_EICR_RX_QUEUE0 << i;
+ tmp |= E1000_EICR_TX_QUEUE0 << i;
+ que->eims = tmp;
+ E1000_WRITE_REG_ARRAY(hw, E1000_MSIXBM(0),
+ i, que->eims);
+ adapter->eims_mask |= que->eims;
}
/* Link */
@@ -2361,7 +2332,21 @@ igb_configure_queues(struct adapter *adapter)
E1000_EIMS_OTHER);
adapter->link_mask |= E1000_EIMS_OTHER;
adapter->eims_mask |= adapter->link_mask;
+ default:
+ break;
+ }
+
+ /* Set the starting interrupt rate */
+ if (hw->mac.type == e1000_82575)
+ newitr |= newitr << 16;
+ else
+ newitr |= 0x8000000;
+
+ for (int i = 0; i < adapter->num_queues; i++) {
+ que = &adapter->queues[i];
+ E1000_WRITE_REG(hw, E1000_EITR(que->msix), newitr);
}
+
return;
}
@@ -2369,8 +2354,7 @@ igb_configure_queues(struct adapter *adapter)
static void
igb_free_pci_resources(struct adapter *adapter)
{
- struct tx_ring *txr = adapter->tx_rings;
- struct rx_ring *rxr = adapter->rx_rings;
+ struct igb_queue *que = adapter->queues;
device_t dev = adapter->dev;
int rid;
@@ -2386,26 +2370,17 @@ igb_free_pci_resources(struct adapter *adapter)
goto mem;
/*
- * First release all the TX/RX interrupt resources:
+ * First release all the interrupt resources:
*/
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- rid = txr->msix + 1;
- if (txr->tag != NULL) {
- bus_teardown_intr(dev, txr->res, txr->tag);
- txr->tag = NULL;
- }
- if (txr->res != NULL)
- bus_release_resource(dev, SYS_RES_IRQ, rid, txr->res);
- }
-
- for (int i = 0; i < adapter->num_queues; i++, rxr++) {
- rid = rxr->msix + 1;
- if (rxr->tag != NULL) {
- bus_teardown_intr(dev, rxr->res, rxr->tag);
- rxr->tag = NULL;
+ for (int i = 0; i < adapter->num_queues; i++, que++) {
+ rid = que->msix + 1;
+ if (que->tag != NULL) {
+ bus_teardown_intr(dev, que->res, que->tag);
+ que->tag = NULL;
}
- if (rxr->res != NULL)
- bus_release_resource(dev, SYS_RES_IRQ, rid, rxr->res);
+ if (que->res != NULL)
+ bus_release_resource(dev,
+ SYS_RES_IRQ, rid, que->res);
}
/* Clean the Legacy or Link interrupt last */
@@ -2444,6 +2419,10 @@ igb_setup_msix(struct adapter *adapter)
device_t dev = adapter->dev;
int rid, want, queues, msgs;
+ /* tuneable override */
+ if (igb_enable_msix == 0)
+ goto msi;
+
/* First try MSI/X */
rid = PCIR_BAR(IGB_MSIX_BAR);
adapter->msix_mem = bus_alloc_resource_any(dev,
@@ -2464,15 +2443,21 @@ igb_setup_msix(struct adapter *adapter)
}
/* Figure out a reasonable auto config value */
- queues = (mp_ncpus > ((msgs-1)/2)) ? (msgs-1)/2 : mp_ncpus;
+ queues = (mp_ncpus > (msgs-1)) ? (msgs-1) : mp_ncpus;
+
+ /* Manual override */
+ if (igb_num_queues != 0)
+ queues = igb_num_queues;
+
+ /* Can have max of 4 queues on 82575 */
+ if ((adapter->hw.mac.type == e1000_82575) && (queues > 4))
+ queues = 4;
- if (igb_num_queues == 0)
- igb_num_queues = queues;
/*
- ** Two vectors (RX/TX pair) per queue
+ ** One vector (RX/TX pair) per queue
** plus an additional for Link interrupt
*/
- want = (igb_num_queues * 2) + 1;
+ want = queues + 1;
if (msgs >= want)
msgs = want;
else {
@@ -2485,7 +2470,7 @@ igb_setup_msix(struct adapter *adapter)
if ((msgs) && pci_alloc_msix(dev, &msgs) == 0) {
device_printf(adapter->dev,
"Using MSIX interrupts with %d vectors\n", msgs);
- adapter->num_queues = igb_num_queues;
+ adapter->num_queues = queues;
return (msgs);
}
msi:
@@ -2497,66 +2482,145 @@ msi:
/*********************************************************************
*
- * Initialize the hardware to a configuration
- * as specified by the adapter structure.
+ * Set up an fresh starting state
*
**********************************************************************/
-static int
-igb_hardware_init(struct adapter *adapter)
+static void
+igb_reset(struct adapter *adapter)
{
device_t dev = adapter->dev;
- u32 rx_buffer_size;
-
- INIT_DEBUGOUT("igb_hardware_init: begin");
+ struct e1000_hw *hw = &adapter->hw;
+ struct e1000_fc_info *fc = &hw->fc;
+ struct ifnet *ifp = adapter->ifp;
+ u32 pba = 0;
+ u16 hwm;
- /* Issue a global reset */
- e1000_reset_hw(&adapter->hw);
+ INIT_DEBUGOUT("igb_reset: begin");
/* Let the firmware know the OS is in control */
igb_get_hw_control(adapter);
/*
+ * Packet Buffer Allocation (PBA)
+ * Writing PBA sets the receive portion of the buffer
+ * the remainder is used for the transmit buffer.
+ */
+ switch (hw->mac.type) {
+ case e1000_82575:
+ pba = E1000_PBA_32K;
+ break;
+ case e1000_82576:
+ pba = E1000_PBA_64K;
+ break;
+ case e1000_82580:
+ pba = E1000_PBA_35K;
+ default:
+ break;
+ }
+
+ /* Special needs in case of Jumbo frames */
+ if ((hw->mac.type == e1000_82575) && (ifp->if_mtu > ETHERMTU)) {
+ u32 tx_space, min_tx, min_rx;
+ pba = E1000_READ_REG(hw, E1000_PBA);
+ tx_space = pba >> 16;
+ pba &= 0xffff;
+ min_tx = (adapter->max_frame_size +
+ sizeof(struct e1000_tx_desc) - ETHERNET_FCS_SIZE) * 2;
+ min_tx = roundup2(min_tx, 1024);
+ min_tx >>= 10;
+ min_rx = adapter->max_frame_size;
+ min_rx = roundup2(min_rx, 1024);
+ min_rx >>= 10;
+ if (tx_space < min_tx &&
+ ((min_tx - tx_space) < pba)) {
+ pba = pba - (min_tx - tx_space);
+ /*
+ * if short on rx space, rx wins
+ * and must trump tx adjustment
+ */
+ if (pba < min_rx)
+ pba = min_rx;
+ }
+ E1000_WRITE_REG(hw, E1000_PBA, pba);
+ }
+
+ INIT_DEBUGOUT1("igb_init: pba=%dK",pba);
+
+ /*
* These parameters control the automatic generation (Tx) and
* response (Rx) to Ethernet PAUSE frames.
* - High water mark should allow for at least two frames to be
* received after sending an XOFF.
* - Low water mark works best when it is very near the high water mark.
* This allows the receiver to restart by sending XON when it has
- * drained a bit. Here we use an arbitary value of 1500 which will
- * restart after one full frame is pulled from the buffer. There
- * could be several smaller frames in the buffer and if so they will
- * not trigger the XON until their total number reduces the buffer
- * by 1500.
- * - The pause time is fairly large at 1000 x 512ns = 512 usec.
+ * drained a bit.
*/
- if (adapter->hw.mac.type == e1000_82576)
- rx_buffer_size = ((E1000_READ_REG(&adapter->hw,
- E1000_RXPBS) & 0xffff) << 10 );
- else
- rx_buffer_size = ((E1000_READ_REG(&adapter->hw,
- E1000_PBA) & 0xffff) << 10 );
+ hwm = min(((pba << 10) * 9 / 10),
+ ((pba << 10) - 2 * adapter->max_frame_size));
- adapter->hw.fc.high_water = rx_buffer_size -
- roundup2(adapter->max_frame_size, 1024);
- adapter->hw.fc.low_water = adapter->hw.fc.high_water - 1500;
+ if (hw->mac.type < e1000_82576) {
+ fc->high_water = hwm & 0xFFF8; /* 8-byte granularity */
+ fc->low_water = fc->high_water - 8;
+ } else {
+ fc->high_water = hwm & 0xFFF0; /* 16-byte granularity */
+ fc->low_water = fc->high_water - 16;
+ }
- adapter->hw.fc.pause_time = IGB_FC_PAUSE_TIME;
- adapter->hw.fc.send_xon = TRUE;
+ fc->pause_time = IGB_FC_PAUSE_TIME;
+ fc->send_xon = TRUE;
/* Set Flow control, use the tunable location if sane */
if ((igb_fc_setting >= 0) || (igb_fc_setting < 4))
- adapter->hw.fc.requested_mode = igb_fc_setting;
+ fc->requested_mode = igb_fc_setting;
else
- adapter->hw.fc.requested_mode = e1000_fc_none;
+ fc->requested_mode = e1000_fc_none;
+
+ fc->current_mode = fc->requested_mode;
+
+ /* Issue a global reset */
+ e1000_reset_hw(hw);
+ E1000_WRITE_REG(hw, E1000_WUC, 0);
- if (e1000_init_hw(&adapter->hw) < 0) {
+ if (e1000_init_hw(hw) < 0)
device_printf(dev, "Hardware Initialization Failed\n");
- return (EIO);
- }
- e1000_check_for_link(&adapter->hw);
+ if (hw->mac.type == e1000_82580) {
+ u32 reg;
- return (0);
+ hwm = (pba << 10) - (2 * adapter->max_frame_size);
+ /*
+ * 0x80000000 - enable DMA COAL
+ * 0x10000000 - use L0s as low power
+ * 0x20000000 - use L1 as low power
+ * X << 16 - exit dma coal when rx data exceeds X kB
+ * Y - upper limit to stay in dma coal in units of 32usecs
+ */
+ E1000_WRITE_REG(hw, E1000_DMACR,
+ 0xA0000006 | ((hwm << 6) & 0x00FF0000));
+
+ /* set hwm to PBA - 2 * max frame size */
+ E1000_WRITE_REG(hw, E1000_FCRTC, hwm);
+ /*
+ * This sets the time to wait before requesting transition to
+ * low power state to number of usecs needed to receive 1 512
+ * byte frame at gigabit line rate
+ */
+ E1000_WRITE_REG(hw, E1000_DMCTLX, 4);
+
+ /* free space in tx packet buffer to wake from DMA coal */
+ E1000_WRITE_REG(hw, E1000_DMCTXTH,
+ (20480 - (2 * adapter->max_frame_size)) >> 6);
+
+ /* make low power state decision controlled by DMA coal */
+ reg = E1000_READ_REG(hw, E1000_PCIEMISC);
+ E1000_WRITE_REG(hw, E1000_PCIEMISC,
+ reg | E1000_PCIEMISC_LX_DECISION);
+ }
+
+ E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN);
+ e1000_get_phy_info(hw);
+ e1000_check_for_link(hw);
+ return;
}
/*********************************************************************
@@ -2596,7 +2660,13 @@ igb_setup_interface(device_t dev, struct adapter *adapter)
ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_MTU;
ifp->if_capabilities |= IFCAP_TSO4;
ifp->if_capabilities |= IFCAP_JUMBO_MTU;
+ if (igb_header_split)
+ ifp->if_capabilities |= IFCAP_LRO;
+
ifp->if_capenable = ifp->if_capabilities;
+#ifdef DEVICE_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
/*
* Tell the upper layer(s) we support long frames.
@@ -2654,7 +2724,7 @@ igb_dma_malloc(struct adapter *adapter, bus_size_t size,
int error;
error = bus_dma_tag_create(bus_get_dma_tag(adapter->dev), /* parent */
- 1, 0, /* alignment, bounds */
+ IGB_DBA_ALIGN, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
@@ -2732,22 +2802,31 @@ static int
igb_allocate_queues(struct adapter *adapter)
{
device_t dev = adapter->dev;
- struct tx_ring *txr;
- struct rx_ring *rxr;
+ struct igb_queue *que = NULL;
+ struct tx_ring *txr = NULL;
+ struct rx_ring *rxr = NULL;
int rsize, tsize, error = E1000_SUCCESS;
int txconf = 0, rxconf = 0;
- /* First allocate the TX ring struct memory */
+ /* First allocate the top level queue structs */
+ if (!(adapter->queues =
+ (struct igb_queue *) malloc(sizeof(struct igb_queue) *
+ adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "Unable to allocate queue memory\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ /* Next allocate the TX ring struct memory */
if (!(adapter->tx_rings =
(struct tx_ring *) malloc(sizeof(struct tx_ring) *
adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) {
device_printf(dev, "Unable to allocate TX ring memory\n");
error = ENOMEM;
- goto fail;
+ goto tx_fail;
}
- txr = adapter->tx_rings;
- /* Next allocate the RX */
+ /* Now allocate the RX */
if (!(adapter->rx_rings =
(struct rx_ring *) malloc(sizeof(struct rx_ring) *
adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) {
@@ -2755,7 +2834,6 @@ igb_allocate_queues(struct adapter *adapter)
error = ENOMEM;
goto rx_fail;
}
- rxr = adapter->rx_rings;
tsize = roundup2(adapter->num_tx_desc *
sizeof(union e1000_adv_tx_desc), IGB_DBA_ALIGN);
@@ -2833,6 +2911,16 @@ igb_allocate_queues(struct adapter *adapter)
}
}
+ /*
+ ** Finally set up the queue holding structs
+ */
+ for (int i = 0; i < adapter->num_queues; i++) {
+ que = &adapter->queues[i];
+ que->adapter = adapter;
+ que->txr = &adapter->tx_rings[i];
+ que->rxr = &adapter->rx_rings[i];
+ }
+
return (0);
err_rx_desc:
@@ -2843,7 +2931,10 @@ err_tx_desc:
igb_dma_free(adapter, &txr->txdma);
free(adapter->rx_rings, M_DEVBUF);
rx_fail:
+ buf_ring_free(txr->br, M_DEVBUF);
free(adapter->tx_rings, M_DEVBUF);
+tx_fail:
+ free(adapter->queues, M_DEVBUF);
fail:
return (error);
}
@@ -2866,7 +2957,7 @@ igb_allocate_transmit_buffers(struct tx_ring *txr)
/*
* Setup DMA descriptor areas.
*/
- if ((error = bus_dma_tag_create(NULL, /* parent */
+ if ((error = bus_dma_tag_create(bus_get_dma_tag(dev),
1, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
@@ -2920,6 +3011,7 @@ igb_setup_transmit_ring(struct tx_ring *txr)
int i;
/* Clear the old descriptor contents */
+ IGB_TX_LOCK(txr);
bzero((void *)txr->tx_base,
(sizeof(union e1000_adv_tx_desc)) * adapter->num_tx_desc);
/* Reset indices */
@@ -2945,7 +3037,7 @@ igb_setup_transmit_ring(struct tx_ring *txr)
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
-
+ IGB_TX_UNLOCK(txr);
}
/*********************************************************************
@@ -2973,48 +3065,50 @@ static void
igb_initialize_transmit_units(struct adapter *adapter)
{
struct tx_ring *txr = adapter->tx_rings;
+ struct e1000_hw *hw = &adapter->hw;
u32 tctl, txdctl;
INIT_DEBUGOUT("igb_initialize_transmit_units: begin");
- /* Setup the Base and Length of the Tx Descriptor Rings */
+ /* Setup the Tx Descriptor Rings */
for (int i = 0; i < adapter->num_queues; i++, txr++) {
u64 bus_addr = txr->txdma.dma_paddr;
- E1000_WRITE_REG(&adapter->hw, E1000_TDLEN(i),
+ E1000_WRITE_REG(hw, E1000_TDLEN(i),
adapter->num_tx_desc * sizeof(struct e1000_tx_desc));
- E1000_WRITE_REG(&adapter->hw, E1000_TDBAH(i),
+ E1000_WRITE_REG(hw, E1000_TDBAH(i),
(uint32_t)(bus_addr >> 32));
- E1000_WRITE_REG(&adapter->hw, E1000_TDBAL(i),
+ E1000_WRITE_REG(hw, E1000_TDBAL(i),
(uint32_t)bus_addr);
/* Setup the HW Tx Head and Tail descriptor pointers */
- E1000_WRITE_REG(&adapter->hw, E1000_TDT(i), 0);
- E1000_WRITE_REG(&adapter->hw, E1000_TDH(i), 0);
+ E1000_WRITE_REG(hw, E1000_TDT(i), 0);
+ E1000_WRITE_REG(hw, E1000_TDH(i), 0);
HW_DEBUGOUT2("Base = %x, Length = %x\n",
- E1000_READ_REG(&adapter->hw, E1000_TDBAL(i)),
- E1000_READ_REG(&adapter->hw, E1000_TDLEN(i)));
+ E1000_READ_REG(hw, E1000_TDBAL(i)),
+ E1000_READ_REG(hw, E1000_TDLEN(i)));
- /* Setup Transmit Descriptor Base Settings */
- adapter->txd_cmd = E1000_TXD_CMD_IFCS;
+ txr->watchdog_check = FALSE;
- txdctl = E1000_READ_REG(&adapter->hw, E1000_TXDCTL(i));
+ txdctl = E1000_READ_REG(hw, E1000_TXDCTL(i));
+ txdctl |= IGB_TX_PTHRESH;
+ txdctl |= IGB_TX_HTHRESH << 8;
+ txdctl |= IGB_TX_WTHRESH << 16;
txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
- E1000_WRITE_REG(&adapter->hw, E1000_TXDCTL(i), txdctl);
+ E1000_WRITE_REG(hw, E1000_TXDCTL(i), txdctl);
}
/* Program the Transmit Control Register */
- tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL);
+ tctl = E1000_READ_REG(hw, E1000_TCTL);
tctl &= ~E1000_TCTL_CT;
tctl |= (E1000_TCTL_PSP | E1000_TCTL_RTLC | E1000_TCTL_EN |
(E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT));
- e1000_config_collision_dist(&adapter->hw);
+ e1000_config_collision_dist(hw);
/* This write will effectively turn on the transmit unit. */
- E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
-
+ E1000_WRITE_REG(hw, E1000_TCTL, tctl);
}
/*********************************************************************
@@ -3093,8 +3187,7 @@ igb_free_transmit_buffers(struct tx_ring *txr)
/**********************************************************************
*
- * Setup work for hardware segmentation offload (TSO) on
- * adapters using advanced tx descriptors (82575)
+ * Setup work for hardware segmentation offload (TSO)
*
**********************************************************************/
static boolean_t
@@ -3165,6 +3258,9 @@ igb_tso_setup(struct tx_ring *txr, struct mbuf *mp, u32 *hdrlen)
/* MSS L4LEN IDX */
mss_l4len_idx |= (mp->m_pkthdr.tso_segsz << E1000_ADVTXD_MSS_SHIFT);
mss_l4len_idx |= (tcp_hlen << E1000_ADVTXD_L4LEN_SHIFT);
+ /* 82575 needs the queue index added */
+ if (adapter->hw.mac.type == e1000_82575)
+ mss_l4len_idx |= txr->me << 4;
TXD->mss_l4len_idx = htole32(mss_l4len_idx);
TXD->seqnum_seed = htole32(0);
@@ -3192,7 +3288,7 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp)
struct adapter *adapter = txr->adapter;
struct e1000_adv_tx_context_desc *TXD;
struct igb_tx_buffer *tx_buffer;
- uint32_t vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+ u32 vlan_macip_lens, type_tucmd_mlhl, mss_l4len_idx;
struct ether_vlan_header *eh;
struct ip *ip = NULL;
struct ip6_hdr *ip6;
@@ -3204,6 +3300,7 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp)
if ((mp->m_pkthdr.csum_flags & CSUM_OFFLOAD) == 0)
offload = FALSE;
+ vlan_macip_lens = type_tucmd_mlhl = mss_l4len_idx = 0;
ctxd = txr->next_avail_desc;
tx_buffer = &txr->tx_buffers[ctxd];
TXD = (struct e1000_adv_tx_context_desc *) &txr->tx_base[ctxd];
@@ -3283,11 +3380,15 @@ igb_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp)
break;
}
+ /* 82575 needs the queue index added */
+ if (adapter->hw.mac.type == e1000_82575)
+ mss_l4len_idx = txr->me << 4;
+
/* Now copy bits into descriptor */
TXD->vlan_macip_lens |= htole32(vlan_macip_lens);
TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl);
TXD->seqnum_seed = htole32(0);
- TXD->mss_l4len_idx = htole32(0);
+ TXD->mss_l4len_idx = htole32(mss_l4len_idx);
tx_buffer->m_head = NULL;
tx_buffer->next_eop = -1;
@@ -3314,8 +3415,7 @@ static bool
igb_txeof(struct tx_ring *txr)
{
struct adapter *adapter = txr->adapter;
- int first, last, done, num_avail;
- u32 cleaned = 0;
+ int first, last, done;
struct igb_tx_buffer *tx_buffer;
struct e1000_tx_desc *tx_desc, *eop_desc;
struct ifnet *ifp = adapter->ifp;
@@ -3325,7 +3425,6 @@ igb_txeof(struct tx_ring *txr)
if (txr->tx_avail == adapter->num_tx_desc)
return FALSE;
- num_avail = txr->tx_avail;
first = txr->next_to_clean;
tx_desc = &txr->tx_base[first];
tx_buffer = &txr->tx_buffers[first];
@@ -3343,7 +3442,7 @@ igb_txeof(struct tx_ring *txr)
done = last;
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
- BUS_DMASYNC_POSTREAD);
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) {
/* We clean the range of the packet */
@@ -3351,10 +3450,11 @@ igb_txeof(struct tx_ring *txr)
tx_desc->upper.data = 0;
tx_desc->lower.data = 0;
tx_desc->buffer_addr = 0;
- ++num_avail; ++cleaned;
+ ++txr->tx_avail;
if (tx_buffer->m_head) {
- ifp->if_opackets++;
+ txr->bytes +=
+ tx_buffer->m_head->m_pkthdr.len;
bus_dmamap_sync(txr->txtag,
tx_buffer->map,
BUS_DMASYNC_POSTWRITE);
@@ -3365,6 +3465,7 @@ igb_txeof(struct tx_ring *txr)
tx_buffer->m_head = NULL;
}
tx_buffer->next_eop = -1;
+ txr->watchdog_time = ticks;
if (++first == adapter->num_tx_desc)
first = 0;
@@ -3372,6 +3473,8 @@ igb_txeof(struct tx_ring *txr)
tx_buffer = &txr->tx_buffers[first];
tx_desc = &txr->tx_base[first];
}
+ ++txr->packets;
+ ++ifp->if_opackets;
/* See if we can continue to the next packet */
last = tx_buffer->next_eop;
if (last != -1) {
@@ -3388,138 +3491,103 @@ igb_txeof(struct tx_ring *txr)
txr->next_to_clean = first;
/*
- * If we have enough room, clear IFF_DRV_OACTIVE to tell the stack
- * that it is OK to send packets.
- * If there are no pending descriptors, clear the timeout. Otherwise,
- * if some descriptors have been freed, restart the timeout.
+ * If we have enough room, clear IFF_DRV_OACTIVE
+ * to tell the stack that it is OK to send packets.
*/
- if (num_avail > IGB_TX_CLEANUP_THRESHOLD) {
+ if (txr->tx_avail > IGB_TX_CLEANUP_THRESHOLD) {
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- /* All clean, turn off the timer */
- if (num_avail == adapter->num_tx_desc) {
- txr->watchdog_timer = 0;
- txr->tx_avail = num_avail;
+ /* All clean, turn off the watchdog */
+ if (txr->tx_avail == adapter->num_tx_desc) {
+ txr->watchdog_check = FALSE;
return FALSE;
}
}
- /* Some cleaned, reset the timer */
- if (cleaned)
- txr->watchdog_timer = IGB_TX_TIMEOUT;
- txr->tx_avail = num_avail;
- return TRUE;
+ return (TRUE);
}
/*********************************************************************
*
- * Setup descriptor buffer(s) from system mbuf buffer pools.
- * i - designates the ring index
- * clean - tells the function whether to update
- * the header, the packet buffer, or both.
+ * Refresh mbuf buffers for RX descriptor rings
+ * - now keeps its own state so discards due to resource
+ * exhaustion are unnecessary, if an mbuf cannot be obtained
+ * it just returns, keeping its placeholder, thus it can simply
+ * be recalled to try again.
*
**********************************************************************/
-static int
-igb_get_buf(struct rx_ring *rxr, int i, u8 clean)
+static void
+igb_refresh_mbufs(struct rx_ring *rxr, int limit)
{
struct adapter *adapter = rxr->adapter;
+ bus_dma_segment_t hseg[1];
+ bus_dma_segment_t pseg[1];
+ struct igb_rx_buf *rxbuf;
struct mbuf *mh, *mp;
- bus_dma_segment_t seg[2];
- bus_dmamap_t map;
- struct igb_rx_buffer *rx_buffer;
- int error, nsegs;
- int merr = 0;
-
-
- rx_buffer = &rxr->rx_buffers[i];
-
- /* First get our header and payload mbuf */
- if (clean & IGB_CLEAN_HEADER) {
- mh = m_gethdr(M_DONTWAIT, MT_DATA);
- if (mh == NULL)
- goto remap;
- } else /* reuse */
- mh = rxr->rx_buffers[i].m_head;
-
- mh->m_len = MHLEN;
- mh->m_flags |= M_PKTHDR;
+ int i, nsegs, error, cleaned;
- if (clean & IGB_CLEAN_PAYLOAD) {
- mp = m_getjcl(M_DONTWAIT, MT_DATA,
- M_PKTHDR, adapter->rx_mbuf_sz);
- if (mp == NULL)
- goto remap;
- mp->m_len = adapter->rx_mbuf_sz;
- mp->m_flags &= ~M_PKTHDR;
- } else { /* reusing */
- mp = rxr->rx_buffers[i].m_pack;
- mp->m_len = adapter->rx_mbuf_sz;
- mp->m_flags &= ~M_PKTHDR;
- }
- /*
- ** Need to create a chain for the following
- ** dmamap call at this point.
- */
- mh->m_next = mp;
- mh->m_pkthdr.len = mh->m_len + mp->m_len;
-
- /* Get the memory mapping */
- error = bus_dmamap_load_mbuf_sg(rxr->rxtag,
- rxr->rx_spare_map, mh, seg, &nsegs, BUS_DMA_NOWAIT);
- if (error != 0) {
- printf("GET BUF: dmamap load failure - %d\n", error);
- m_free(mh);
- return (error);
- }
-
- /* Unload old mapping and update buffer struct */
- if (rx_buffer->m_head != NULL)
- bus_dmamap_unload(rxr->rxtag, rx_buffer->map);
- map = rx_buffer->map;
- rx_buffer->map = rxr->rx_spare_map;
- rxr->rx_spare_map = map;
- rx_buffer->m_head = mh;
- rx_buffer->m_pack = mp;
- bus_dmamap_sync(rxr->rxtag,
- rx_buffer->map, BUS_DMASYNC_PREREAD);
-
- /* Update descriptor */
- rxr->rx_base[i].read.hdr_addr = htole64(seg[0].ds_addr);
- rxr->rx_base[i].read.pkt_addr = htole64(seg[1].ds_addr);
+ i = rxr->next_to_refresh;
+ cleaned = -1; /* Signify no completions */
+ while (i != limit) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->m_head == NULL) {
+ mh = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (mh == NULL)
+ goto update;
+ mh->m_pkthdr.len = mh->m_len = MHLEN;
+ mh->m_len = MHLEN;
+ mh->m_flags |= M_PKTHDR;
+ m_adj(mh, ETHER_ALIGN);
+ /* Get the memory mapping */
+ error = bus_dmamap_load_mbuf_sg(rxr->htag,
+ rxbuf->hmap, mh, hseg, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ printf("GET BUF: dmamap load"
+ " failure - %d\n", error);
+ m_free(mh);
+ goto update;
+ }
+ rxbuf->m_head = mh;
+ bus_dmamap_sync(rxr->htag, rxbuf->hmap,
+ BUS_DMASYNC_PREREAD);
+ rxr->rx_base[i].read.hdr_addr =
+ htole64(hseg[0].ds_addr);
+ }
- return (0);
+ if (rxbuf->m_pack == NULL) {
+ mp = m_getjcl(M_DONTWAIT, MT_DATA,
+ M_PKTHDR, adapter->rx_mbuf_sz);
+ if (mp == NULL)
+ goto update;
+ mp->m_pkthdr.len = mp->m_len = adapter->rx_mbuf_sz;
+ /* Get the memory mapping */
+ error = bus_dmamap_load_mbuf_sg(rxr->ptag,
+ rxbuf->pmap, mp, pseg, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ printf("GET BUF: dmamap load"
+ " failure - %d\n", error);
+ m_free(mp);
+ goto update;
+ }
+ rxbuf->m_pack = mp;
+ bus_dmamap_sync(rxr->ptag, rxbuf->pmap,
+ BUS_DMASYNC_PREREAD);
+ rxr->rx_base[i].read.pkt_addr =
+ htole64(pseg[0].ds_addr);
+ }
- /*
- ** If we get here, we have an mbuf resource
- ** issue, so we discard the incoming packet
- ** and attempt to reuse existing mbufs next
- ** pass thru the ring, but to do so we must
- ** fix up the descriptor which had the address
- ** clobbered with writeback info.
- */
-remap:
- adapter->mbuf_header_failed++;
- merr = ENOBUFS;
- /* Is there a reusable buffer? */
- mh = rxr->rx_buffers[i].m_head;
- if (mh == NULL) /* Nope, init error */
- return (merr);
- mp = rxr->rx_buffers[i].m_pack;
- if (mp == NULL) /* Nope, init error */
- return (merr);
- /* Get our old mapping */
- rx_buffer = &rxr->rx_buffers[i];
- error = bus_dmamap_load_mbuf_sg(rxr->rxtag,
- rx_buffer->map, mh, seg, &nsegs, BUS_DMA_NOWAIT);
- if (error != 0) {
- /* We really have a problem */
- m_free(mh);
- return (error);
+ cleaned = i;
+ /* Calculate next index */
+ if (++i == adapter->num_rx_desc)
+ i = 0;
+ /* This is the work marker for refresh */
+ rxr->next_to_refresh = i;
}
- /* Now fix the descriptor as needed */
- rxr->rx_base[i].read.hdr_addr = htole64(seg[0].ds_addr);
- rxr->rx_base[i].read.pkt_addr = htole64(seg[1].ds_addr);
- return (merr);
+update:
+ if (cleaned != -1) /* If we refreshed some, bump tail */
+ E1000_WRITE_REG(&adapter->hw,
+ E1000_RDT(rxr->me), cleaned);
+ return;
}
@@ -3536,55 +3604,64 @@ igb_allocate_receive_buffers(struct rx_ring *rxr)
{
struct adapter *adapter = rxr->adapter;
device_t dev = adapter->dev;
- struct igb_rx_buffer *rxbuf;
+ struct igb_rx_buf *rxbuf;
int i, bsize, error;
- bsize = sizeof(struct igb_rx_buffer) * adapter->num_rx_desc;
+ bsize = sizeof(struct igb_rx_buf) * adapter->num_rx_desc;
if (!(rxr->rx_buffers =
- (struct igb_rx_buffer *) malloc(bsize,
+ (struct igb_rx_buf *) malloc(bsize,
M_DEVBUF, M_NOWAIT | M_ZERO))) {
device_printf(dev, "Unable to allocate rx_buffer memory\n");
error = ENOMEM;
goto fail;
}
- /*
- ** The tag is made to accomodate the largest buffer size
- ** with packet split (hence the two segments, even though
- ** it may not always use this.
- */
- if ((error = bus_dma_tag_create(NULL, /* parent */
+ if ((error = bus_dma_tag_create(bus_get_dma_tag(dev),
1, 0, /* alignment, bounds */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
- MJUM16BYTES, /* maxsize */
- 2, /* nsegments */
- MJUMPAGESIZE, /* maxsegsize */
+ MSIZE, /* maxsize */
+ 1, /* nsegments */
+ MSIZE, /* maxsegsize */
0, /* flags */
NULL, /* lockfunc */
NULL, /* lockfuncarg */
- &rxr->rxtag))) {
+ &rxr->htag))) {
device_printf(dev, "Unable to create RX DMA tag\n");
goto fail;
}
- /* Create the spare map (used by getbuf) */
- error = bus_dmamap_create(rxr->rxtag, BUS_DMA_NOWAIT,
- &rxr->rx_spare_map);
- if (error) {
- device_printf(dev,
- "%s: bus_dmamap_create header spare failed: %d\n",
- __func__, error);
+ if ((error = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MJUMPAGESIZE, /* maxsize */
+ 1, /* nsegments */
+ MJUMPAGESIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &rxr->ptag))) {
+ device_printf(dev, "Unable to create RX payload DMA tag\n");
goto fail;
}
- for (i = 0; i < adapter->num_rx_desc; i++, rxbuf++) {
+ for (i = 0; i < adapter->num_rx_desc; i++) {
rxbuf = &rxr->rx_buffers[i];
- error = bus_dmamap_create(rxr->rxtag,
- BUS_DMA_NOWAIT, &rxbuf->map);
+ error = bus_dmamap_create(rxr->htag,
+ BUS_DMA_NOWAIT, &rxbuf->hmap);
+ if (error) {
+ device_printf(dev,
+ "Unable to create RX head DMA maps\n");
+ goto fail;
+ }
+ error = bus_dmamap_create(rxr->ptag,
+ BUS_DMA_NOWAIT, &rxbuf->pmap);
if (error) {
- device_printf(dev, "Unable to create RX DMA maps\n");
+ device_printf(dev,
+ "Unable to create RX packet DMA maps\n");
goto fail;
}
}
@@ -3597,6 +3674,37 @@ fail:
return (error);
}
+
+static void
+igb_free_receive_ring(struct rx_ring *rxr)
+{
+ struct adapter *adapter;
+ struct igb_rx_buf *rxbuf;
+ int i;
+
+ adapter = rxr->adapter;
+ for (i = 0; i < adapter->num_rx_desc; i++) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->m_head != NULL) {
+ bus_dmamap_sync(rxr->htag, rxbuf->hmap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->htag, rxbuf->hmap);
+ rxbuf->m_head->m_flags |= M_PKTHDR;
+ m_freem(rxbuf->m_head);
+ }
+ if (rxbuf->m_pack != NULL) {
+ bus_dmamap_sync(rxr->ptag, rxbuf->pmap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->ptag, rxbuf->pmap);
+ rxbuf->m_pack->m_flags |= M_PKTHDR;
+ m_freem(rxbuf->m_pack);
+ }
+ rxbuf->m_head = NULL;
+ rxbuf->m_pack = NULL;
+ }
+}
+
+
/*********************************************************************
*
* Initialize a receive ring and its buffers.
@@ -3608,17 +3716,17 @@ igb_setup_receive_ring(struct rx_ring *rxr)
struct adapter *adapter;
struct ifnet *ifp;
device_t dev;
- struct igb_rx_buffer *rxbuf;
+ struct igb_rx_buf *rxbuf;
+ bus_dma_segment_t pseg[1], hseg[1];
struct lro_ctrl *lro = &rxr->lro;
- int j, rsize;
+ int rsize, nsegs, error = 0;
adapter = rxr->adapter;
dev = adapter->dev;
ifp = adapter->ifp;
- rxr->lro_enabled = FALSE;
- rxr->hdr_split = FALSE;
/* Clear the ring contents */
+ IGB_RX_LOCK(rxr);
rsize = roundup2(adapter->num_rx_desc *
sizeof(union e1000_adv_rx_desc), IGB_DBA_ALIGN);
bzero((void *)rxr->rx_base, rsize);
@@ -3626,33 +3734,62 @@ igb_setup_receive_ring(struct rx_ring *rxr)
/*
** Free current RX buffer structures and their mbufs
*/
- for (int i = 0; i < adapter->num_rx_desc; i++) {
- rxbuf = &rxr->rx_buffers[i];
- bus_dmamap_sync(rxr->rxtag, rxbuf->map,
- BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(rxr->rxtag, rxbuf->map);
- if (rxbuf->m_head) {
- rxbuf->m_head->m_next = rxbuf->m_pack;
- m_freem(rxbuf->m_head);
- }
- rxbuf->m_head = NULL;
- rxbuf->m_pack = NULL;
- }
+ igb_free_receive_ring(rxr);
- /* Next replenish the ring */
- for (j = 0; j < adapter->num_rx_desc; j++) {
- if (igb_get_buf(rxr, j, IGB_CLEAN_BOTH) == ENOBUFS) {
- rxr->rx_buffers[j].m_head = NULL;
- rxr->rx_buffers[j].m_pack = NULL;
- rxr->rx_base[j].read.hdr_addr = 0;
- rxr->rx_base[j].read.pkt_addr = 0;
- goto fail;
- }
- }
+ /* Now replenish the ring mbufs */
+ for (int j = 0; j != adapter->num_rx_desc; ++j) {
+ struct mbuf *mh, *mp;
+
+ rxbuf = &rxr->rx_buffers[j];
+
+ /* First the header */
+ rxbuf->m_head = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (rxbuf->m_head == NULL)
+ goto fail;
+ m_adj(rxbuf->m_head, ETHER_ALIGN);
+ mh = rxbuf->m_head;
+ mh->m_len = mh->m_pkthdr.len = MHLEN;
+ mh->m_flags |= M_PKTHDR;
+ /* Get the memory mapping */
+ error = bus_dmamap_load_mbuf_sg(rxr->htag,
+ rxbuf->hmap, rxbuf->m_head, hseg,
+ &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) /* Nothing elegant to do here */
+ goto fail;
+ bus_dmamap_sync(rxr->htag,
+ rxbuf->hmap, BUS_DMASYNC_PREREAD);
+ /* Update descriptor */
+ rxr->rx_base[j].read.hdr_addr = htole64(hseg[0].ds_addr);
+
+ /* Now the payload cluster */
+ rxbuf->m_pack = m_getjcl(M_DONTWAIT, MT_DATA,
+ M_PKTHDR, adapter->rx_mbuf_sz);
+ if (rxbuf->m_pack == NULL)
+ goto fail;
+ mp = rxbuf->m_pack;
+ mp->m_pkthdr.len = mp->m_len = adapter->rx_mbuf_sz;
+ /* Get the memory mapping */
+ error = bus_dmamap_load_mbuf_sg(rxr->ptag,
+ rxbuf->pmap, mp, pseg,
+ &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0)
+ goto fail;
+ bus_dmamap_sync(rxr->ptag,
+ rxbuf->pmap, BUS_DMASYNC_PREREAD);
+ /* Update descriptor */
+ rxr->rx_base[j].read.pkt_addr = htole64(pseg[0].ds_addr);
+ }
+ rxr->next_to_refresh = 0;
+ rxr->lro_enabled = FALSE;
+
+ if (igb_header_split)
+ rxr->hdr_split = TRUE;
+ else
+ ifp->if_capabilities &= ~IFCAP_LRO;
- /* Setup our descriptor indices */
- rxr->next_to_check = 0;
- rxr->last_cleaned = 0;
+ rxr->fmp = NULL;
+ rxr->lmp = NULL;
+ rxr->discard = FALSE;
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -3666,32 +3803,21 @@ igb_setup_receive_ring(struct rx_ring *rxr)
if (ifp->if_capenable & IFCAP_LRO) {
int err = tcp_lro_init(lro);
if (err) {
- device_printf(dev,"LRO Initialization failed!\n");
+ device_printf(dev, "LRO Initialization failed!\n");
goto fail;
}
INIT_DEBUGOUT("RX LRO Initialized\n");
rxr->lro_enabled = TRUE;
- rxr->hdr_split = TRUE;
lro->ifp = adapter->ifp;
}
+ IGB_RX_UNLOCK(rxr);
return (0);
+
fail:
- /*
- * We need to clean up any buffers allocated
- * so far, 'j' is the failing index.
- */
- for (int i = 0; i < j; i++) {
- rxbuf = &rxr->rx_buffers[i];
- if (rxbuf->m_head != NULL) {
- bus_dmamap_sync(rxr->rxtag, rxbuf->map,
- BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(rxr->rxtag, rxbuf->map);
- m_freem(rxbuf->m_head);
- rxbuf->m_head = NULL;
- }
- }
- return (ENOBUFS);
+ igb_free_receive_ring(rxr);
+ IGB_RX_UNLOCK(rxr);
+ return (error);
}
/*********************************************************************
@@ -3719,17 +3845,8 @@ fail:
*/
rxr = adapter->rx_rings;
for (--i; i > 0; i--, rxr++) {
- for (j = 0; j < adapter->num_rx_desc; j++) {
- struct igb_rx_buffer *rxbuf;
- rxbuf = &rxr->rx_buffers[j];
- if (rxbuf->m_head != NULL) {
- bus_dmamap_sync(rxr->rxtag, rxbuf->map,
- BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(rxr->rxtag, rxbuf->map);
- m_freem(rxbuf->m_head);
- rxbuf->m_head = NULL;
- }
- }
+ for (j = 0; j < adapter->num_rx_desc; j++)
+ igb_free_receive_ring(rxr);
}
return (ENOBUFS);
@@ -3745,6 +3862,7 @@ igb_initialize_receive_units(struct adapter *adapter)
{
struct rx_ring *rxr = adapter->rx_rings;
struct ifnet *ifp = adapter->ifp;
+ struct e1000_hw *hw = &adapter->hw;
u32 rctl, rxcsum, psize, srrctl = 0;
INIT_DEBUGOUT("igb_initialize_receive_unit: begin");
@@ -3753,8 +3871,8 @@ igb_initialize_receive_units(struct adapter *adapter)
* Make sure receives are disabled while setting
* up the descriptor ring
*/
- rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
- E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
+ rctl = E1000_READ_REG(hw, E1000_RCTL);
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
/*
** Set up for header split
@@ -3791,27 +3909,27 @@ igb_initialize_receive_units(struct adapter *adapter)
u64 bus_addr = rxr->rxdma.dma_paddr;
u32 rxdctl;
- E1000_WRITE_REG(&adapter->hw, E1000_RDLEN(i),
+ E1000_WRITE_REG(hw, E1000_RDLEN(i),
adapter->num_rx_desc * sizeof(struct e1000_rx_desc));
- E1000_WRITE_REG(&adapter->hw, E1000_RDBAH(i),
+ E1000_WRITE_REG(hw, E1000_RDBAH(i),
(uint32_t)(bus_addr >> 32));
- E1000_WRITE_REG(&adapter->hw, E1000_RDBAL(i),
+ E1000_WRITE_REG(hw, E1000_RDBAL(i),
(uint32_t)bus_addr);
- E1000_WRITE_REG(&adapter->hw, E1000_SRRCTL(i), srrctl);
+ E1000_WRITE_REG(hw, E1000_SRRCTL(i), srrctl);
/* Enable this Queue */
- rxdctl = E1000_READ_REG(&adapter->hw, E1000_RXDCTL(i));
+ rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(i));
rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
rxdctl &= 0xFFF00000;
rxdctl |= IGB_RX_PTHRESH;
rxdctl |= IGB_RX_HTHRESH << 8;
rxdctl |= IGB_RX_WTHRESH << 16;
- E1000_WRITE_REG(&adapter->hw, E1000_RXDCTL(i), rxdctl);
+ E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl);
}
/*
** Setup for RX MultiQueue
*/
- rxcsum = E1000_READ_REG(&adapter->hw, E1000_RXCSUM);
+ rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
if (adapter->num_queues >1) {
u32 random[10], mrqc, shift = 0;
union igb_reta {
@@ -3827,13 +3945,13 @@ igb_initialize_receive_units(struct adapter *adapter)
reta.bytes[i & 3] =
(i % adapter->num_queues) << shift;
if ((i & 3) == 3)
- E1000_WRITE_REG(&adapter->hw,
+ E1000_WRITE_REG(hw,
E1000_RETA(i >> 2), reta.dword);
}
/* Now fill in hash table */
mrqc = E1000_MRQC_ENABLE_RSS_4Q;
for (int i = 0; i < 10; i++)
- E1000_WRITE_REG_ARRAY(&adapter->hw,
+ E1000_WRITE_REG_ARRAY(hw,
E1000_RSSRK(0), i, random[i]);
mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 |
@@ -3845,7 +3963,7 @@ igb_initialize_receive_units(struct adapter *adapter)
mrqc |=( E1000_MRQC_RSS_FIELD_IPV6_UDP_EX |
E1000_MRQC_RSS_FIELD_IPV6_TCP_EX);
- E1000_WRITE_REG(&adapter->hw, E1000_MRQC, mrqc);
+ E1000_WRITE_REG(hw, E1000_MRQC, mrqc);
/*
** NOTE: Receive Full-Packet Checksum Offload
@@ -3856,7 +3974,7 @@ igb_initialize_receive_units(struct adapter *adapter)
rxcsum |= E1000_RXCSUM_PCSD;
#if __FreeBSD_version >= 800000
/* For SCTP Offload */
- if ((adapter->hw.mac.type == e1000_82576)
+ if ((hw->mac.type == e1000_82576)
&& (ifp->if_capenable & IFCAP_RXCSUM))
rxcsum |= E1000_RXCSUM_CRCOFL;
#endif
@@ -3871,29 +3989,30 @@ igb_initialize_receive_units(struct adapter *adapter)
} else
rxcsum &= ~E1000_RXCSUM_TUOFL;
}
- E1000_WRITE_REG(&adapter->hw, E1000_RXCSUM, rxcsum);
+ E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
/* Setup the Receive Control Register */
rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO |
E1000_RCTL_RDMTS_HALF |
- (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
-
+ (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
+ /* Strip CRC bytes. */
+ rctl |= E1000_RCTL_SECRC;
/* Make sure VLAN Filters are off */
rctl &= ~E1000_RCTL_VFE;
/* Don't store bad packets */
rctl &= ~E1000_RCTL_SBP;
/* Enable Receives */
- E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl);
+ E1000_WRITE_REG(hw, E1000_RCTL, rctl);
/*
* Setup the HW Rx Head and Tail Descriptor Pointers
* - needs to be after enable
*/
for (int i = 0; i < adapter->num_queues; i++) {
- E1000_WRITE_REG(&adapter->hw, E1000_RDH(i), 0);
- E1000_WRITE_REG(&adapter->hw, E1000_RDT(i),
+ E1000_WRITE_REG(hw, E1000_RDH(i), 0);
+ E1000_WRITE_REG(hw, E1000_RDT(i),
adapter->num_rx_desc - 1);
}
return;
@@ -3927,48 +4046,115 @@ igb_free_receive_structures(struct adapter *adapter)
static void
igb_free_receive_buffers(struct rx_ring *rxr)
{
- struct adapter *adapter = rxr->adapter;
- struct igb_rx_buffer *rx_buffer;
+ struct adapter *adapter = rxr->adapter;
+ struct igb_rx_buf *rxbuf;
+ int i;
INIT_DEBUGOUT("free_receive_structures: begin");
- if (rxr->rx_spare_map) {
- bus_dmamap_destroy(rxr->rxtag, rxr->rx_spare_map);
- rxr->rx_spare_map = NULL;
- }
-
/* Cleanup any existing buffers */
if (rxr->rx_buffers != NULL) {
- rx_buffer = &rxr->rx_buffers[0];
- for (int i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
- if (rx_buffer->m_head != NULL) {
- bus_dmamap_sync(rxr->rxtag, rx_buffer->map,
+ for (i = 0; i < adapter->num_rx_desc; i++) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->m_head != NULL) {
+ bus_dmamap_sync(rxr->htag, rxbuf->hmap,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->htag, rxbuf->hmap);
+ rxbuf->m_head->m_flags |= M_PKTHDR;
+ m_freem(rxbuf->m_head);
+ }
+ if (rxbuf->m_pack != NULL) {
+ bus_dmamap_sync(rxr->ptag, rxbuf->pmap,
BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(rxr->rxtag,
- rx_buffer->map);
- m_freem(rx_buffer->m_head);
- rx_buffer->m_head = NULL;
- } else if (rx_buffer->map != NULL)
- bus_dmamap_unload(rxr->rxtag,
- rx_buffer->map);
- if (rx_buffer->map != NULL) {
- bus_dmamap_destroy(rxr->rxtag,
- rx_buffer->map);
- rx_buffer->map = NULL;
+ bus_dmamap_unload(rxr->ptag, rxbuf->pmap);
+ rxbuf->m_pack->m_flags |= M_PKTHDR;
+ m_freem(rxbuf->m_pack);
+ }
+ rxbuf->m_head = NULL;
+ rxbuf->m_pack = NULL;
+ if (rxbuf->hmap != NULL) {
+ bus_dmamap_destroy(rxr->htag, rxbuf->hmap);
+ rxbuf->hmap = NULL;
}
+ if (rxbuf->pmap != NULL) {
+ bus_dmamap_destroy(rxr->ptag, rxbuf->pmap);
+ rxbuf->pmap = NULL;
+ }
+ }
+ if (rxr->rx_buffers != NULL) {
+ free(rxr->rx_buffers, M_DEVBUF);
+ rxr->rx_buffers = NULL;
}
}
- if (rxr->rx_buffers != NULL) {
- free(rxr->rx_buffers, M_DEVBUF);
- rxr->rx_buffers = NULL;
+ if (rxr->htag != NULL) {
+ bus_dma_tag_destroy(rxr->htag);
+ rxr->htag = NULL;
}
+ if (rxr->ptag != NULL) {
+ bus_dma_tag_destroy(rxr->ptag);
+ rxr->ptag = NULL;
+ }
+}
+
+static __inline void
+igb_rx_discard(struct rx_ring *rxr, int i)
+{
+ struct adapter *adapter = rxr->adapter;
+ struct igb_rx_buf *rbuf;
+ struct mbuf *mh, *mp;
+
+ rbuf = &rxr->rx_buffers[i];
+ if (rxr->fmp != NULL) {
+ rxr->fmp->m_flags |= M_PKTHDR;
+ m_freem(rxr->fmp);
+ rxr->fmp = NULL;
+ rxr->lmp = NULL;
+ }
+
+ mh = rbuf->m_head;
+ mp = rbuf->m_pack;
+
+ /* Reuse loaded DMA map and just update mbuf chain */
+ mh->m_len = MHLEN;
+ mh->m_flags |= M_PKTHDR;
+ mh->m_next = NULL;
+
+ mp->m_len = mp->m_pkthdr.len = adapter->rx_mbuf_sz;
+ mp->m_data = mp->m_ext.ext_buf;
+ mp->m_next = NULL;
+ return;
+}
+
+static __inline void
+igb_rx_input(struct rx_ring *rxr, struct ifnet *ifp, struct mbuf *m, u32 ptype)
+{
- if (rxr->rxtag != NULL) {
- bus_dma_tag_destroy(rxr->rxtag);
- rxr->rxtag = NULL;
+ /*
+ * ATM LRO is only for IPv4/TCP packets and TCP checksum of the packet
+ * should be computed by hardware. Also it should not have VLAN tag in
+ * ethernet header.
+ */
+ if (rxr->lro_enabled &&
+ (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 &&
+ (ptype & E1000_RXDADV_PKTTYPE_ETQF) == 0 &&
+ (ptype & (E1000_RXDADV_PKTTYPE_IPV4 | E1000_RXDADV_PKTTYPE_TCP)) ==
+ (E1000_RXDADV_PKTTYPE_IPV4 | E1000_RXDADV_PKTTYPE_TCP) &&
+ (m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) ==
+ (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) {
+ /*
+ * Send to the stack if:
+ ** - LRO not enabled, or
+ ** - no LRO resources, or
+ ** - lro enqueue fails
+ */
+ if (rxr->lro.lro_cnt != 0)
+ if (tcp_lro_rx(&rxr->lro, m, 0) == 0)
+ return;
}
+ (*ifp->if_input)(ifp, m);
}
+
/*********************************************************************
*
* This routine executes in interrupt context. It replenishes
@@ -3981,48 +4167,57 @@ igb_free_receive_buffers(struct rx_ring *rxr)
* Return TRUE if more to clean, FALSE otherwise
*********************************************************************/
static bool
-igb_rxeof(struct rx_ring *rxr, int count)
+igb_rxeof(struct igb_queue *que, int count)
{
- struct adapter *adapter = rxr->adapter;
- struct ifnet *ifp;
+ struct adapter *adapter = que->adapter;
+ struct rx_ring *rxr = que->rxr;
+ struct ifnet *ifp = adapter->ifp;
struct lro_ctrl *lro = &rxr->lro;
struct lro_entry *queued;
- int i;
- u32 staterr;
+ int i, processed = 0;
+ u32 ptype, staterr = 0;
union e1000_adv_rx_desc *cur;
-
IGB_RX_LOCK(rxr);
- ifp = adapter->ifp;
- i = rxr->next_to_check;
- cur = &rxr->rx_base[i];
- staterr = cur->wb.upper.status_error;
-
- if (!(staterr & E1000_RXD_STAT_DD)) {
- IGB_RX_UNLOCK(rxr);
- return FALSE;
- }
-
- /* Sync the ring */
+ /* Sync the ring. */
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
- BUS_DMASYNC_POSTREAD);
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
/* Main clean loop */
- while ((staterr & E1000_RXD_STAT_DD) &&
- (count != 0) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- struct mbuf *sendmp, *mh, *mp;
- u16 hlen, plen, hdr, ptype, len_adj, vtag;
- u8 dopayload, accept_frame, eop;
+ for (i = rxr->next_to_check; count != 0;) {
+ struct mbuf *sendmp, *mh, *mp;
+ struct igb_rx_buf *rxbuf;
+ u16 hlen, plen, hdr, vtag;
+ bool eop = FALSE;
- accept_frame = 1;
- hlen = plen = len_adj = vtag = 0;
+ cur = &rxr->rx_base[i];
+ staterr = le32toh(cur->wb.upper.status_error);
+ if ((staterr & E1000_RXD_STAT_DD) == 0)
+ break;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
+ count--;
sendmp = mh = mp = NULL;
- ptype = (u16)(cur->wb.lower.lo_dword.data >> 4);
+ cur->wb.upper.status_error = 0;
+ rxbuf = &rxr->rx_buffers[i];
+ plen = le16toh(cur->wb.upper.length);
+ ptype = le32toh(cur->wb.lower.lo_dword.data) & IGB_PKTTYPE_MASK;
+ vtag = le16toh(cur->wb.upper.vlan);
+ hdr = le16toh(cur->wb.lower.lo_dword.hs_rss.hdr_info);
+ eop = ((staterr & E1000_RXD_STAT_EOP) == E1000_RXD_STAT_EOP);
- /* Sync the buffers */
- bus_dmamap_sync(rxr->rxtag, rxr->rx_buffers[i].map,
- BUS_DMASYNC_POSTREAD);
+ /* Make sure all segments of a bad packet are discarded */
+ if (((staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) != 0) ||
+ (rxr->discard)) {
+ ifp->if_ierrors++;
+ ++rxr->rx_discarded;
+ if (!eop) /* Catch subsequent segs */
+ rxr->discard = TRUE;
+ else
+ rxr->discard = FALSE;
+ igb_rx_discard(rxr, i);
+ goto next_desc;
+ }
/*
** The way the hardware is configured to
@@ -4035,35 +4230,28 @@ igb_rxeof(struct rx_ring *rxr, int count)
** packet spans multiple descriptors, in that
** case only the first header is valid.
*/
- if ((rxr->hdr_split) && (rxr->fmp == NULL)){
- hdr = le16toh(cur->
- wb.lower.lo_dword.hs_rss.hdr_info);
+ if (rxr->hdr_split && rxr->fmp == NULL) {
hlen = (hdr & E1000_RXDADV_HDRBUFLEN_MASK) >>
E1000_RXDADV_HDRBUFLEN_SHIFT;
if (hlen > IGB_HDR_BUF)
hlen = IGB_HDR_BUF;
- plen = le16toh(cur->wb.upper.length);
/* Handle the header mbuf */
mh = rxr->rx_buffers[i].m_head;
mh->m_len = hlen;
- dopayload = IGB_CLEAN_HEADER;
+ /* clear buf info for refresh */
+ rxbuf->m_head = NULL;
/*
** Get the payload length, this
** could be zero if its a small
** packet.
*/
- if (plen) {
+ if (plen > 0) {
mp = rxr->rx_buffers[i].m_pack;
mp->m_len = plen;
- mp->m_next = NULL;
- mp->m_flags &= ~M_PKTHDR;
mh->m_next = mp;
- mh->m_flags |= M_PKTHDR;
- dopayload = IGB_CLEAN_BOTH;
+ /* clear buf info for refresh */
+ rxbuf->m_pack = NULL;
rxr->rx_split_packets++;
- } else { /* small packets */
- mh->m_flags &= ~M_PKTHDR;
- mh->m_next = NULL;
}
} else {
/*
@@ -4072,179 +4260,91 @@ igb_rxeof(struct rx_ring *rxr, int count)
** split packet.
*/
mh = rxr->rx_buffers[i].m_pack;
- mh->m_flags |= M_PKTHDR;
- mh->m_len = le16toh(cur->wb.upper.length);
- dopayload = IGB_CLEAN_PAYLOAD;
+ mh->m_len = plen;
+ /* clear buf info for refresh */
+ rxbuf->m_pack = NULL;
}
- if (staterr & E1000_RXD_STAT_EOP) {
- count--;
- eop = 1;
- /*
- ** Strip CRC and account for frag
- */
- if (mp) {
- if (mp->m_len < ETHER_CRC_LEN) {
- /* a frag, how much is left? */
- len_adj = ETHER_CRC_LEN - mp->m_len;
- mp->m_len = 0;
- } else
- mp->m_len -= ETHER_CRC_LEN;
- } else { /* not split */
- if (mh->m_len < ETHER_CRC_LEN) {
- len_adj = ETHER_CRC_LEN - mh->m_len;
- mh->m_len = 0;
- } else
- mh->m_len -= ETHER_CRC_LEN;
- }
- } else
- eop = 0;
+ ++processed; /* So we know when to refresh */
- if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK)
- accept_frame = 0;
-#ifdef IGB_IEEE1588
- This linux code needs to be converted to work here
- -----------------------------------------------------
- if (unlikely(staterr & E1000_RXD_STAT_TS)) {
- u64 regval;
- u64 ns;
-// Create an mtag and set it up
- struct skb_shared_hwtstamps *shhwtstamps =
- skb_hwtstamps(skb);
-
- rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID),
- "igb: no RX time stamp available for time stamped packet");
- regval = rd32(E1000_RXSTMPL);
- regval |= (u64)rd32(E1000_RXSTMPH) << 32;
-// Do time conversion from the register
- ns = timecounter_cyc2time(&adapter->clock, regval);
- clocksync_update(&adapter->sync, ns);
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
- shhwtstamps->syststamp =
- clocksync_hw2sys(&adapter->sync, ns);
- }
-#endif
- if (accept_frame) {
- /*
- ** get_buf will overwrite the writeback
- ** descriptor so save the VLAN tag now.
- */
- vtag = le16toh(cur->wb.upper.vlan);
- if (igb_get_buf(rxr, i, dopayload) != 0) {
- ifp->if_iqdrops++;
- goto discard;
- }
- /* Initial frame - setup */
- if (rxr->fmp == NULL) {
- mh->m_flags |= M_PKTHDR;
- mh->m_pkthdr.len = mh->m_len;
- rxr->fmp = mh; /* Store the first mbuf */
- rxr->lmp = mh;
- if (mp) { /* Add payload if split */
- mh->m_pkthdr.len += mp->m_len;
- rxr->lmp = mh->m_next;
- }
- } else {
- /* Chain mbuf's together */
- mh->m_flags &= ~M_PKTHDR;
- rxr->lmp->m_next = mh;
- rxr->lmp = rxr->lmp->m_next;
- rxr->fmp->m_pkthdr.len += mh->m_len;
- /* Adjust for CRC frag */
- if (len_adj) {
- rxr->lmp->m_len -= len_adj;
- rxr->fmp->m_pkthdr.len -= len_adj;
- }
+ /* Initial frame - setup */
+ if (rxr->fmp == NULL) {
+ mh->m_pkthdr.len = mh->m_len;
+ /* Store the first mbuf */
+ rxr->fmp = mh;
+ rxr->lmp = mh;
+ if (mp != NULL) {
+ /* Add payload if split */
+ mh->m_pkthdr.len += mp->m_len;
+ rxr->lmp = mh->m_next;
}
+ } else {
+ /* Chain mbuf's together */
+ rxr->lmp->m_next = mh;
+ rxr->lmp = rxr->lmp->m_next;
+ rxr->fmp->m_pkthdr.len += mh->m_len;
+ }
- if (eop) {
- bool sctp = ((ptype & 0x40) != 0);
- rxr->fmp->m_pkthdr.rcvif = ifp;
- ifp->if_ipackets++;
- rxr->rx_packets++;
- /* capture data for AIM */
- rxr->bytes += rxr->fmp->m_pkthdr.len;
- rxr->rx_bytes += rxr->fmp->m_pkthdr.len;
+ if (eop) {
+ rxr->fmp->m_pkthdr.rcvif = ifp;
+ ifp->if_ipackets++;
+ rxr->rx_packets++;
+ /* capture data for AIM */
+ rxr->packets++;
+ rxr->bytes += rxr->fmp->m_pkthdr.len;
+ rxr->rx_bytes += rxr->fmp->m_pkthdr.len;
- igb_rx_checksum(staterr, rxr->fmp, sctp);
- if (staterr & E1000_RXD_STAT_VP) {
- rxr->fmp->m_pkthdr.ether_vtag = vtag;
- rxr->fmp->m_flags |= M_VLANTAG;
- }
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ igb_rx_checksum(staterr, rxr->fmp, ptype);
+
+ if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 &&
+ (staterr & E1000_RXD_STAT_VP) != 0) {
+ rxr->fmp->m_pkthdr.ether_vtag = vtag;
+ rxr->fmp->m_flags |= M_VLANTAG;
+ }
#if __FreeBSD_version >= 800000
- rxr->fmp->m_pkthdr.flowid = curcpu;
- rxr->fmp->m_flags |= M_FLOWID;
+ rxr->fmp->m_pkthdr.flowid = que->msix;
+ rxr->fmp->m_flags |= M_FLOWID;
#endif
- sendmp = rxr->fmp;
- rxr->fmp = NULL;
- rxr->lmp = NULL;
- }
- } else {
- ifp->if_ierrors++;
-discard:
- /* Reuse loaded DMA map and just update mbuf chain */
- if (hlen) {
- mh = rxr->rx_buffers[i].m_head;
- mh->m_len = MHLEN;
- mh->m_next = NULL;
- }
- mp = rxr->rx_buffers[i].m_pack;
- mp->m_len = mp->m_pkthdr.len = adapter->rx_mbuf_sz;
- mp->m_data = mp->m_ext.ext_buf;
- mp->m_next = NULL;
- if (adapter->max_frame_size <=
- (MCLBYTES - ETHER_ALIGN))
- m_adj(mp, ETHER_ALIGN);
- if (rxr->fmp != NULL) {
- /* handles the whole chain */
- m_freem(rxr->fmp);
- rxr->fmp = NULL;
- rxr->lmp = NULL;
- }
- sendmp = NULL;
+ sendmp = rxr->fmp;
+ /* Make sure to set M_PKTHDR. */
+ sendmp->m_flags |= M_PKTHDR;
+ rxr->fmp = NULL;
+ rxr->lmp = NULL;
}
+next_desc:
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
- rxr->last_cleaned = i; /* For updating tail */
-
/* Advance our pointers to the next descriptor. */
if (++i == adapter->num_rx_desc)
i = 0;
-
/*
- ** Note that we hold the RX lock thru
- ** the following call so this ring's
- ** next_to_check is not gonna change.
+ ** Send to the stack or LRO
*/
- if (sendmp != NULL) {
- /*
- ** Send to the stack if:
- ** - LRO not enabled, or
- ** - no LRO resources, or
- ** - lro enqueue fails
- */
- if ((!rxr->lro_enabled) ||
- ((!lro->lro_cnt) || (tcp_lro_rx(lro, sendmp, 0))))
- (*ifp->if_input)(ifp, sendmp);
- }
+ if (sendmp != NULL)
+ igb_rx_input(rxr, ifp, sendmp, ptype);
- /* Get the next descriptor */
- cur = &rxr->rx_base[i];
- staterr = cur->wb.upper.status_error;
+ /* Every 8 descriptors we go to refresh mbufs */
+ if (processed == 8) {
+ igb_refresh_mbufs(rxr, i);
+ processed = 0;
+ }
}
- rxr->next_to_check = i;
- /* Advance the E1000's Receive Queue #0 "Tail Pointer". */
- E1000_WRITE_REG(&adapter->hw, E1000_RDT(rxr->me), rxr->last_cleaned);
+ /* Catch any remainders */
+ if (processed != 0) {
+ igb_refresh_mbufs(rxr, i);
+ processed = 0;
+ }
+
+ rxr->next_to_check = i;
/*
* Flush any outstanding LRO work
*/
- while (!SLIST_EMPTY(&lro->lro_active)) {
- queued = SLIST_FIRST(&lro->lro_active);
+ while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) {
SLIST_REMOVE_HEAD(&lro->lro_active, next);
tcp_lro_flush(lro, queued);
}
@@ -4255,15 +4355,12 @@ discard:
** We still have cleaning to do?
** Schedule another interrupt if so.
*/
- if (staterr & E1000_RXD_STAT_DD) {
- E1000_WRITE_REG(&adapter->hw, E1000_EICS, rxr->eims);
- return TRUE;
- }
+ if ((staterr & E1000_RXD_STAT_DD) != 0)
+ return (TRUE);
- return FALSE;
+ return (FALSE);
}
-
/*********************************************************************
*
* Verify that the hardware indicated that the checksum is valid.
@@ -4272,10 +4369,11 @@ discard:
*
*********************************************************************/
static void
-igb_rx_checksum(u32 staterr, struct mbuf *mp, bool sctp)
+igb_rx_checksum(u32 staterr, struct mbuf *mp, u32 ptype)
{
u16 status = (u16)staterr;
u8 errors = (u8) (staterr >> 24);
+ int sctp;
/* Ignore Checksum bit is set */
if (status & E1000_RXD_STAT_IXSM) {
@@ -4283,6 +4381,11 @@ igb_rx_checksum(u32 staterr, struct mbuf *mp, bool sctp)
return;
}
+ if ((ptype & E1000_RXDADV_PKTTYPE_ETQF) == 0 &&
+ (ptype & E1000_RXDADV_PKTTYPE_SCTP) != 0)
+ sctp = 1;
+ else
+ sctp = 0;
if (status & E1000_RXD_STAT_IPCS) {
/* Did it pass? */
if (!(errors & E1000_RXD_ERR_IPE)) {
@@ -4302,7 +4405,7 @@ igb_rx_checksum(u32 staterr, struct mbuf *mp, bool sctp)
/* Did it pass? */
if (!(errors & E1000_RXD_ERR_TCPE)) {
mp->m_pkthdr.csum_flags |= type;
- if (!sctp)
+ if (sctp == 0)
mp->m_pkthdr.csum_data = htons(0xffff);
}
}
@@ -4522,7 +4625,7 @@ igb_is_valid_ether_addr(uint8_t *addr)
/*
* Enable PCI Wake On Lan capability
*/
-void
+static void
igb_enable_wakeup(device_t dev)
{
u16 cap, status;
@@ -4543,6 +4646,21 @@ igb_enable_wakeup(device_t dev)
return;
}
+static void
+igb_led_func(void *arg, int onoff)
+{
+ struct adapter *adapter = arg;
+
+ IGB_CORE_LOCK(adapter);
+ if (onoff) {
+ e1000_setup_led(&adapter->hw);
+ e1000_led_on(&adapter->hw);
+ } else {
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
+ }
+ IGB_CORE_UNLOCK(adapter);
+}
/**********************************************************************
*
@@ -4650,6 +4768,7 @@ static void
igb_print_debug_info(struct adapter *adapter)
{
device_t dev = adapter->dev;
+ struct igb_queue *que = adapter->queues;
struct rx_ring *rxr = adapter->rx_rings;
struct tx_ring *txr = adapter->tx_rings;
uint8_t *hw_addr = adapter->hw.hw_addr;
@@ -4672,16 +4791,19 @@ igb_print_debug_info(struct adapter *adapter)
adapter->hw.fc.high_water,
adapter->hw.fc.low_water);
- for (int i = 0; i < adapter->num_queues; i++, txr++) {
- device_printf(dev, "Queue(%d) tdh = %d, tdt = %d\n", i,
+ for (int i = 0; i < adapter->num_queues; i++, rxr++, txr++) {
+ device_printf(dev, "Queue(%d) tdh = %d, tdt = %d ", i,
E1000_READ_REG(&adapter->hw, E1000_TDH(i)),
E1000_READ_REG(&adapter->hw, E1000_TDT(i)));
+ device_printf(dev, "rdh = %d, rdt = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_RDH(i)),
+ E1000_READ_REG(&adapter->hw, E1000_RDT(i)));
device_printf(dev, "TX(%d) no descriptors avail event = %lld\n",
txr->me, (long long)txr->no_desc_avail);
- device_printf(dev, "TX(%d) MSIX IRQ Handled = %lld\n", txr->me,
- (long long)txr->tx_irq);
- device_printf(dev, "TX(%d) Packets sent = %lld\n", txr->me,
- (long long)txr->tx_packets);
+ device_printf(dev, "TX(%d) Packets sent = %lld\n",
+ txr->me, (long long)txr->tx_packets);
+ device_printf(dev, "RX(%d) Packets received = %lld ",
+ rxr->me, (long long)rxr->rx_packets);
}
for (int i = 0; i < adapter->num_queues; i++, rxr++) {
@@ -4691,20 +4813,20 @@ igb_print_debug_info(struct adapter *adapter)
E1000_READ_REG(&adapter->hw, E1000_RDT(i)));
device_printf(dev, "RX(%d) Packets received = %lld\n", rxr->me,
(long long)rxr->rx_packets);
- device_printf(dev, "RX(%d) Split Packets = %lld\n", rxr->me,
+ device_printf(dev, " Split Packets = %lld ",
(long long)rxr->rx_split_packets);
- device_printf(dev, "RX(%d) Byte count = %lld\n", rxr->me,
+ device_printf(dev, " Byte count = %lld\n",
(long long)rxr->rx_bytes);
- device_printf(dev, "RX(%d) MSIX IRQ Handled = %lld\n", rxr->me,
- (long long)rxr->rx_irq);
- device_printf(dev,"RX(%d) LRO Queued= %d\n",
- rxr->me, lro->lro_queued);
- device_printf(dev,"RX(%d) LRO Flushed= %d\n",
- rxr->me, lro->lro_flushed);
+ device_printf(dev,"RX(%d) LRO Queued= %d ",
+ i, lro->lro_queued);
+ device_printf(dev,"LRO Flushed= %d\n",lro->lro_flushed);
}
- device_printf(dev, "LINK MSIX IRQ Handled = %u\n", adapter->link_irq);
+ for (int i = 0; i < adapter->num_queues; i++, que++)
+ device_printf(dev,"QUE(%d) IRQs = %llx\n",
+ i, (long long)que->irqs);
+ device_printf(dev, "LINK MSIX IRQ Handled = %u\n", adapter->link_irq);
device_printf(dev, "Mbuf defrag failed = %ld\n",
adapter->mbuf_defrag_failed);
device_printf(dev, "Std mbuf header failed = %ld\n",
@@ -4857,173 +4979,3 @@ igb_add_rx_process_limit(struct adapter *adapter, const char *name,
SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)),
OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description);
}
-
-#ifdef IGB_IEEE1588
-/*
-** igb_hwtstamp_ioctl - control hardware time stamping
-**
-** Outgoing time stamping can be enabled and disabled. Play nice and
-** disable it when requested, although it shouldn't case any overhead
-** when no packet needs it. At most one packet in the queue may be
-** marked for time stamping, otherwise it would be impossible to tell
-** for sure to which packet the hardware time stamp belongs.
-**
-** Incoming time stamping has to be configured via the hardware
-** filters. Not all combinations are supported, in particular event
-** type has to be specified. Matching the kind of event packet is
-** not supported, with the exception of "all V2 events regardless of
-** level 2 or 4".
-**
-*/
-static int
-igb_hwtstamp_ioctl(struct adapter *adapter, struct ifreq *ifr)
-{
- struct e1000_hw *hw = &adapter->hw;
- struct hwtstamp_ctrl *config;
- u32 tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED;
- u32 tsync_rx_ctl_bit = E1000_TSYNCRXCTL_ENABLED;
- u32 tsync_rx_ctl_type = 0;
- u32 tsync_rx_cfg = 0;
- int is_l4 = 0;
- int is_l2 = 0;
- u16 port = 319; /* PTP */
- u32 regval;
-
- config = (struct hwtstamp_ctrl *) ifr->ifr_data;
-
- /* reserved for future extensions */
- if (config->flags)
- return (EINVAL);
-
- switch (config->tx_type) {
- case HWTSTAMP_TX_OFF:
- tsync_tx_ctl_bit = 0;
- break;
- case HWTSTAMP_TX_ON:
- tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED;
- break;
- default:
- return (ERANGE);
- }
-
- switch (config->rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- tsync_rx_ctl_bit = 0;
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- case HWTSTAMP_FILTER_ALL:
- /*
- * register TSYNCRXCFG must be set, therefore it is not
- * possible to time stamp both Sync and Delay_Req messages
- * => fall back to time stamping all packets
- */
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_ALL;
- config->rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1;
- tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE;
- is_l4 = 1;
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1;
- tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE;
- is_l4 = 1;
- break;
- case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
- tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE;
- is_l2 = 1;
- is_l4 = 1;
- config->rx_filter = HWTSTAMP_FILTER_SOME;
- break;
- case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
- tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE;
- is_l2 = 1;
- is_l4 = 1;
- config->rx_filter = HWTSTAMP_FILTER_SOME;
- break;
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_EVENT_V2;
- config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
- is_l2 = 1;
- break;
- default:
- return -ERANGE;
- }
-
- /* enable/disable TX */
- regval = E1000_READ_REG(hw, E1000_TSYNCTXCTL);
- regval = (regval & ~E1000_TSYNCTXCTL_ENABLED) | tsync_tx_ctl_bit;
- E1000_WRITE_REG(hw, E1000_TSYNCTXCTL, regval);
-
- /* enable/disable RX, define which PTP packets are time stamped */
- regval = E1000_READ_REG(hw, E1000_TSYNCRXCTL);
- regval = (regval & ~E1000_TSYNCRXCTL_ENABLED) | tsync_rx_ctl_bit;
- regval = (regval & ~0xE) | tsync_rx_ctl_type;
- E1000_WRITE_REG(hw, E1000_TSYNCRXCTL, regval);
- E1000_WRITE_REG(hw, E1000_TSYNCRXCFG, tsync_rx_cfg);
-
- /*
- * Ethertype Filter Queue Filter[0][15:0] = 0x88F7
- * (Ethertype to filter on)
- * Ethertype Filter Queue Filter[0][26] = 0x1 (Enable filter)
- * Ethertype Filter Queue Filter[0][30] = 0x1 (Enable Timestamping)
- */
- E1000_WRITE_REG(hw, E1000_ETQF0, is_l2 ? 0x440088f7 : 0);
-
- /* L4 Queue Filter[0]: only filter by source and destination port */
- E1000_WRITE_REG(hw, E1000_SPQF0, htons(port));
- E1000_WRITE_REG(hw, E1000_IMIREXT(0), is_l4 ?
- ((1<<12) | (1<<19) /* bypass size and control flags */) : 0);
- E1000_WRITE_REG(hw, E1000_IMIR(0), is_l4 ?
- (htons(port)
- | (0<<16) /* immediate interrupt disabled */
- | 0 /* (1<<17) bit cleared: do not bypass
- destination port check */)
- : 0);
- E1000_WRITE_REG(hw, E1000_FTQF0, is_l4 ?
- (0x11 /* UDP */
- | (1<<15) /* VF not compared */
- | (1<<27) /* Enable Timestamping */
- | (7<<28) /* only source port filter enabled,
- source/target address and protocol
- masked */)
- : ((1<<15) | (15<<28) /* all mask bits set = filter not
- enabled */));
-
- wrfl();
-
- adapter->hwtstamp_ctrl = config;
-
- /* clear TX/RX time stamp registers, just to be sure */
- regval = E1000_READ_REG(hw, E1000_TXSTMPH);
- regval = E1000_READ_REG(hw, E1000_RXSTMPH);
-
- return (error);
-}
-
-/*
-** igb_read_clock - read raw cycle counter (to be used by time counter)
-*/
-static cycle_t igb_read_clock(const struct cyclecounter *tc)
-{
- struct igb_adapter *adapter =
- container_of(tc, struct igb_adapter, cycles);
- struct e1000_hw *hw = &adapter->hw;
- u64 stamp;
-
- stamp = E1000_READ_REG(hw, E1000_SYSTIML);
- stamp |= (u64)E1000_READ_REG(hw, E1000_SYSTIMH) << 32ULL;
-
- return (stamp);
-}
-
-#endif /* IGB_IEEE1588 */
diff --git a/sys/dev/e1000/if_igb.h b/sys/dev/e1000/if_igb.h
index ddc4d8ae9e6a..28bcc91caa0c 100644
--- a/sys/dev/e1000/if_igb.h
+++ b/sys/dev/e1000/if_igb.h
@@ -1,6 +1,6 @@
/******************************************************************************
- Copyright (c) 2001-2009, Intel Corporation
+ Copyright (c) 2001-2010, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -47,8 +47,8 @@
* desscriptors should meet the following condition.
* (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
*/
-#define IGB_MIN_TXD 80
-#define IGB_DEFAULT_TXD 256
+#define IGB_MIN_TXD 256
+#define IGB_DEFAULT_TXD 1024
#define IGB_MAX_TXD 4096
/*
@@ -62,8 +62,8 @@
* desscriptors should meet the following condition.
* (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
*/
-#define IGB_MIN_RXD 80
-#define IGB_DEFAULT_RXD 256
+#define IGB_MIN_RXD 256
+#define IGB_DEFAULT_RXD 1024
#define IGB_MAX_RXD 4096
/*
@@ -128,7 +128,7 @@
/*
* This parameter controls the duration of transmit watchdog timer.
*/
-#define IGB_TX_TIMEOUT 5 /* set to 5 seconds */
+#define IGB_WATCHDOG (10 * hz)
/*
* This parameter controls when the driver calls the routine to reclaim
@@ -173,10 +173,16 @@
#define IGB_SMARTSPEED_DOWNSHIFT 3
#define IGB_SMARTSPEED_MAX 15
#define IGB_MAX_LOOP 10
-#define IGB_RX_PTHRESH 16
+
+#define IGB_RX_PTHRESH (hw->mac.type <= e1000_82576 ? 16 : 8)
#define IGB_RX_HTHRESH 8
#define IGB_RX_WTHRESH 1
+#define IGB_TX_PTHRESH 8
+#define IGB_TX_HTHRESH 1
+#define IGB_TX_WTHRESH ((hw->mac.type == e1000_82576 && \
+ adapter->msix_mem) ? 1 : 16)
+
#define MAX_NUM_MULTICAST_ADDRESSES 128
#define PCI_ANY_ID (~0U)
#define ETHER_ALIGN 2
@@ -225,6 +231,7 @@
#define IGB_TSO_SIZE (65535 + sizeof(struct ether_vlan_header))
#define IGB_TSO_SEG_SIZE 4096 /* Max dma segment size */
#define IGB_HDR_BUF 128
+#define IGB_PKTTYPE_MASK 0x0000FFF0
#define ETH_ZLEN 60
#define ETH_ADDR_LEN 6
@@ -235,17 +242,16 @@
#define CSUM_OFFLOAD (CSUM_IP|CSUM_TCP|CSUM_UDP)
#endif
+/* Define the starting Interrupt rate per Queue */
+#define IGB_INTS_PER_SEC 8000
+#define IGB_DEFAULT_ITR 1000000000/(IGB_INTS_PER_SEC * 256)
+
+
/* Header split codes for get_buf */
-#define IGB_CLEAN_HEADER 1
-#define IGB_CLEAN_PAYLOAD 2
-#define IGB_CLEAN_BOTH 3
+#define IGB_CLEAN_HEADER 0x01
+#define IGB_CLEAN_PAYLOAD 0x02
+#define IGB_CLEAN_BOTH (IGB_CLEAN_HEADER | IGB_CLEAN_PAYLOAD)
-/*
- * Interrupt Moderation parameters
- */
-#define IGB_LOW_LATENCY 128
-#define IGB_AVE_LATENCY 450
-#define IGB_BULK_LATENCY 1200
#define IGB_LINK_ITR 2000
/* Precision Time Sync (IEEE 1588) defines */
@@ -268,18 +274,33 @@ struct igb_dma_alloc {
/*
- * Transmit ring: one per tx queue
+** Driver queue struct: this is the interrupt container
+** for the associated tx and rx ring.
+*/
+struct igb_queue {
+ struct adapter *adapter;
+ u32 msix; /* This queue's MSIX vector */
+ u32 eims; /* This queue's EIMS bit */
+ u32 eitr_setting;
+ struct resource *res;
+ void *tag;
+ struct tx_ring *txr;
+ struct rx_ring *rxr;
+ struct task que_task;
+ struct taskqueue *tq;
+ u64 irqs;
+};
+
+/*
+ * Transmit ring: one per queue
*/
struct tx_ring {
struct adapter *adapter;
u32 me;
- u32 msix; /* This ring's MSIX vector */
- u32 eims; /* This ring's EIMS bit */
struct mtx tx_mtx;
char mtx_name[16];
- struct igb_dma_alloc txdma; /* bus_dma glue for tx desc */
+ struct igb_dma_alloc txdma;
struct e1000_tx_desc *tx_base;
- struct task tx_task; /* cleanup tasklet */
u32 next_avail_desc;
u32 next_to_clean;
volatile u16 tx_avail;
@@ -287,37 +308,36 @@ struct tx_ring {
#if __FreeBSD_version >= 800000
struct buf_ring *br;
#endif
- bus_dma_tag_t txtag; /* dma tag for tx */
- struct resource *res;
- void *tag;
+ bus_dma_tag_t txtag;
+
+ u32 bytes;
+ u32 packets;
- u32 watchdog_timer;
+ bool watchdog_check;
+ int watchdog_time;
u64 no_desc_avail;
- u64 tx_irq;
u64 tx_packets;
};
/*
- * Receive ring: one per rx queue
+ * Receive ring: one per queue
*/
struct rx_ring {
struct adapter *adapter;
u32 me;
- u32 msix; /* This ring's MSIX vector */
- u32 eims; /* This ring's EIMS bit */
- struct igb_dma_alloc rxdma; /* bus_dma glue for tx desc */
+ struct igb_dma_alloc rxdma;
union e1000_adv_rx_desc *rx_base;
struct lro_ctrl lro;
bool lro_enabled;
bool hdr_split;
- struct task rx_task; /* cleanup tasklet */
+ bool discard;
struct mtx rx_mtx;
char mtx_name[16];
- u32 last_cleaned;
+ u32 next_to_refresh;
u32 next_to_check;
- struct igb_rx_buffer *rx_buffers;
- bus_dma_tag_t rxtag; /* dma tag for tx */
- bus_dmamap_t rx_spare_map;
+ struct igb_rx_buf *rx_buffers;
+ bus_dma_tag_t htag; /* dma tag for rx head */
+ bus_dma_tag_t ptag; /* dma tag for rx packet */
/*
* First/last mbuf pointers, for
* collecting multisegment RX packets.
@@ -326,14 +346,11 @@ struct rx_ring {
struct mbuf *lmp;
u32 bytes;
- u32 eitr_setting;
-
- struct resource *res;
- void *tag;
+ u32 packets;
/* Soft stats */
- u64 rx_irq;
u64 rx_split_packets;
+ u64 rx_discarded;
u64 rx_packets;
u64 rx_bytes;
};
@@ -342,9 +359,9 @@ struct adapter {
struct ifnet *ifp;
struct e1000_hw hw;
- /* FreeBSD operating-system-specific structures. */
struct e1000_osdep osdep;
struct device *dev;
+ struct cdev *led_dev;
struct resource *pci_mem;
struct resource *msix_mem;
@@ -354,6 +371,7 @@ struct adapter {
int linkvec;
int link_mask;
+ struct task link_task;
int link_irq;
struct ifmedia media;
@@ -364,9 +382,8 @@ struct adapter {
int min_frame_size;
struct mtx core_mtx;
int igb_insert_vlan_header;
- struct task link_task;
struct task rxtx_task;
- struct taskqueue *tq; /* private task queue */
+ struct taskqueue *tq; /* adapter task queue */
u16 num_queues;
eventhandler_tag vlan_attach;
@@ -383,12 +400,14 @@ struct adapter {
u16 link_duplex;
u32 smartspeed;
+ /* Interface queues */
+ struct igb_queue *queues;
+
/*
* Transmit rings
*/
struct tx_ring *tx_rings;
u16 num_tx_desc;
- u32 txd_cmd;
/*
* Receive rings
@@ -445,25 +464,29 @@ struct igb_tx_buffer {
bus_dmamap_t map; /* bus_dma map for packet */
};
-struct igb_rx_buffer {
+struct igb_rx_buf {
struct mbuf *m_head;
struct mbuf *m_pack;
- bus_dmamap_t map; /* bus_dma map for packet */
+ bus_dmamap_t hmap; /* bus_dma map for header */
+ bus_dmamap_t pmap; /* bus_dma map for packet */
};
#define IGB_CORE_LOCK_INIT(_sc, _name) \
mtx_init(&(_sc)->core_mtx, _name, "IGB Core Lock", MTX_DEF)
#define IGB_CORE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->core_mtx)
-#define IGB_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx)
-#define IGB_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rx_mtx)
#define IGB_CORE_LOCK(_sc) mtx_lock(&(_sc)->core_mtx)
-#define IGB_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx)
-#define IGB_TX_TRYLOCK(_sc) mtx_trylock(&(_sc)->tx_mtx)
-#define IGB_RX_LOCK(_sc) mtx_lock(&(_sc)->rx_mtx)
#define IGB_CORE_UNLOCK(_sc) mtx_unlock(&(_sc)->core_mtx)
+#define IGB_CORE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->core_mtx, MA_OWNED)
+
+#define IGB_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx)
+#define IGB_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx)
#define IGB_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->tx_mtx)
+#define IGB_TX_TRYLOCK(_sc) mtx_trylock(&(_sc)->tx_mtx)
+#define IGB_TX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->tx_mtx, MA_OWNED)
+
+#define IGB_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rx_mtx)
+#define IGB_RX_LOCK(_sc) mtx_lock(&(_sc)->rx_mtx)
#define IGB_RX_UNLOCK(_sc) mtx_unlock(&(_sc)->rx_mtx)
-#define IGB_CORE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->core_mtx, MA_OWNED)
#define IGB_TX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->tx_mtx, MA_OWNED)
#endif /* _IGB_H_DEFINED_ */
diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c
new file mode 100644
index 000000000000..cf71f2bb74bf
--- /dev/null
+++ b/sys/dev/e1000/if_lem.c
@@ -0,0 +1,4706 @@
+/******************************************************************************
+
+ Copyright (c) 2001-2010, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+/*$FreeBSD$*/
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_device_polling.h"
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#if __FreeBSD_version >= 800000
+#include <sys/buf_ring.h>
+#endif
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#if __FreeBSD_version >= 700029
+#include <sys/eventhandler.h>
+#endif
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <machine/in_cksum.h>
+#include <dev/led/led.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include "e1000_api.h"
+#include "if_lem.h"
+
+/*********************************************************************
+ * Set this to one to display debug statistics
+ *********************************************************************/
+int lem_display_debug_stats = 0;
+
+/*********************************************************************
+ * Legacy Em Driver version:
+ *********************************************************************/
+char lem_driver_version[] = "1.0.0";
+
+
+/*********************************************************************
+ * PCI Device ID Table
+ *
+ * Used by probe to select devices to load on
+ * Last field stores an index into e1000_strings
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index }
+ *********************************************************************/
+
+static em_vendor_info_t lem_vendor_info_array[] =
+{
+ /* Intel(R) PRO/1000 Network Connection */
+ { 0x8086, E1000_DEV_ID_82540EM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82540EM_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82540EP, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82540EP_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82540EP_LP, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82541EI, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541ER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541ER_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541GI, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541GI_LF, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82541GI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82542, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82543GC_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82543GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82544EI_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82544EI_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82544GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82544GC_LOM, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82545EM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82545EM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82545GM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82545GM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82545GM_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82546EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546EB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546EB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_PCIE, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3,
+ PCI_ANY_ID, PCI_ANY_ID, 0},
+
+ { 0x8086, E1000_DEV_ID_82547EI, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82547EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0},
+ { 0x8086, E1000_DEV_ID_82547GI, PCI_ANY_ID, PCI_ANY_ID, 0},
+ /* required last entry */
+ { 0, 0, 0, 0, 0}
+};
+
+/*********************************************************************
+ * Table of branding strings for all supported NICs.
+ *********************************************************************/
+
+static char *lem_strings[] = {
+ "Intel(R) PRO/1000 Legacy Network Connection"
+};
+
+/*********************************************************************
+ * Function prototypes
+ *********************************************************************/
+static int lem_probe(device_t);
+static int lem_attach(device_t);
+static int lem_detach(device_t);
+static int lem_shutdown(device_t);
+static int lem_suspend(device_t);
+static int lem_resume(device_t);
+static void lem_start(struct ifnet *);
+static void lem_start_locked(struct ifnet *ifp);
+#if __FreeBSD_version >= 800000
+static int lem_mq_start(struct ifnet *, struct mbuf *);
+static int lem_mq_start_locked(struct ifnet *, struct mbuf *);
+static void lem_qflush(struct ifnet *);
+#endif
+static int lem_ioctl(struct ifnet *, u_long, caddr_t);
+static void lem_init(void *);
+static void lem_init_locked(struct adapter *);
+static void lem_stop(void *);
+static void lem_media_status(struct ifnet *, struct ifmediareq *);
+static int lem_media_change(struct ifnet *);
+static void lem_identify_hardware(struct adapter *);
+static int lem_allocate_pci_resources(struct adapter *);
+static int lem_allocate_irq(struct adapter *adapter);
+static void lem_free_pci_resources(struct adapter *);
+static void lem_local_timer(void *);
+static int lem_hardware_init(struct adapter *);
+static void lem_setup_interface(device_t, struct adapter *);
+static void lem_setup_transmit_structures(struct adapter *);
+static void lem_initialize_transmit_unit(struct adapter *);
+static int lem_setup_receive_structures(struct adapter *);
+static void lem_initialize_receive_unit(struct adapter *);
+static void lem_enable_intr(struct adapter *);
+static void lem_disable_intr(struct adapter *);
+static void lem_free_transmit_structures(struct adapter *);
+static void lem_free_receive_structures(struct adapter *);
+static void lem_update_stats_counters(struct adapter *);
+static void lem_txeof(struct adapter *);
+static void lem_tx_purge(struct adapter *);
+static int lem_allocate_receive_structures(struct adapter *);
+static int lem_allocate_transmit_structures(struct adapter *);
+static int lem_rxeof(struct adapter *, int);
+#ifndef __NO_STRICT_ALIGNMENT
+static int lem_fixup_rx(struct adapter *);
+#endif
+static void lem_receive_checksum(struct adapter *, struct e1000_rx_desc *,
+ struct mbuf *);
+static void lem_transmit_checksum_setup(struct adapter *, struct mbuf *,
+ u32 *, u32 *);
+static void lem_set_promisc(struct adapter *);
+static void lem_disable_promisc(struct adapter *);
+static void lem_set_multi(struct adapter *);
+static void lem_print_hw_stats(struct adapter *);
+static void lem_update_link_status(struct adapter *);
+static int lem_get_buf(struct adapter *, int);
+#if __FreeBSD_version >= 700029
+static void lem_register_vlan(void *, struct ifnet *, u16);
+static void lem_unregister_vlan(void *, struct ifnet *, u16);
+static void lem_setup_vlan_hw_support(struct adapter *);
+#endif
+static int lem_xmit(struct adapter *, struct mbuf **);
+static void lem_smartspeed(struct adapter *);
+static int lem_82547_fifo_workaround(struct adapter *, int);
+static void lem_82547_update_fifo_head(struct adapter *, int);
+static int lem_82547_tx_fifo_reset(struct adapter *);
+static void lem_82547_move_tail(void *);
+static int lem_dma_malloc(struct adapter *, bus_size_t,
+ struct em_dma_alloc *, int);
+static void lem_dma_free(struct adapter *, struct em_dma_alloc *);
+static void lem_print_debug_info(struct adapter *);
+static void lem_print_nvm_info(struct adapter *);
+static int lem_is_valid_ether_addr(u8 *);
+static int lem_sysctl_stats(SYSCTL_HANDLER_ARGS);
+static int lem_sysctl_debug_info(SYSCTL_HANDLER_ARGS);
+static u32 lem_fill_descriptors (bus_addr_t address, u32 length,
+ PDESC_ARRAY desc_array);
+static int lem_sysctl_int_delay(SYSCTL_HANDLER_ARGS);
+static void lem_add_int_delay_sysctl(struct adapter *, const char *,
+ const char *, struct em_int_delay_info *, int, int);
+/* Management and WOL Support */
+static void lem_init_manageability(struct adapter *);
+static void lem_release_manageability(struct adapter *);
+static void lem_get_hw_control(struct adapter *);
+static void lem_release_hw_control(struct adapter *);
+static void lem_get_wakeup(device_t);
+static void lem_enable_wakeup(device_t);
+static int lem_enable_phy_wakeup(struct adapter *);
+static void lem_led_func(void *, int);
+
+#ifdef EM_LEGACY_IRQ
+static void lem_intr(void *);
+#else /* FAST IRQ */
+#if __FreeBSD_version < 700000
+static void lem_irq_fast(void *);
+#else
+static int lem_irq_fast(void *);
+#endif
+static void lem_handle_rxtx(void *context, int pending);
+static void lem_handle_link(void *context, int pending);
+static void lem_add_rx_process_limit(struct adapter *, const char *,
+ const char *, int *, int);
+#endif /* ~EM_LEGACY_IRQ */
+
+#ifdef DEVICE_POLLING
+static poll_handler_t lem_poll;
+#endif /* POLLING */
+
+/*********************************************************************
+ * FreeBSD Device Interface Entry Points
+ *********************************************************************/
+
+static device_method_t lem_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, lem_probe),
+ DEVMETHOD(device_attach, lem_attach),
+ DEVMETHOD(device_detach, lem_detach),
+ DEVMETHOD(device_shutdown, lem_shutdown),
+ DEVMETHOD(device_suspend, lem_suspend),
+ DEVMETHOD(device_resume, lem_resume),
+ {0, 0}
+};
+
+static driver_t lem_driver = {
+ "em", lem_methods, sizeof(struct adapter),
+};
+
+extern devclass_t em_devclass;
+DRIVER_MODULE(lem, pci, lem_driver, em_devclass, 0, 0);
+MODULE_DEPEND(lem, pci, 1, 1, 1);
+MODULE_DEPEND(lem, ether, 1, 1, 1);
+
+/*********************************************************************
+ * Tunable default values.
+ *********************************************************************/
+
+#define EM_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000)
+#define EM_USECS_TO_TICKS(usecs) ((1000 * (usecs) + 512) / 1024)
+#define M_TSO_LEN 66
+
+/* Allow common code without TSO */
+#ifndef CSUM_TSO
+#define CSUM_TSO 0
+#endif
+
+static int lem_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV);
+static int lem_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR);
+static int lem_tx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_TADV);
+static int lem_rx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_RADV);
+static int lem_rxd = EM_DEFAULT_RXD;
+static int lem_txd = EM_DEFAULT_TXD;
+static int lem_smart_pwr_down = FALSE;
+
+/* Controls whether promiscuous also shows bad packets */
+static int lem_debug_sbp = FALSE;
+
+TUNABLE_INT("hw.em.tx_int_delay", &lem_tx_int_delay_dflt);
+TUNABLE_INT("hw.em.rx_int_delay", &lem_rx_int_delay_dflt);
+TUNABLE_INT("hw.em.tx_abs_int_delay", &lem_tx_abs_int_delay_dflt);
+TUNABLE_INT("hw.em.rx_abs_int_delay", &lem_rx_abs_int_delay_dflt);
+TUNABLE_INT("hw.em.rxd", &lem_rxd);
+TUNABLE_INT("hw.em.txd", &lem_txd);
+TUNABLE_INT("hw.em.smart_pwr_down", &lem_smart_pwr_down);
+TUNABLE_INT("hw.em.sbp", &lem_debug_sbp);
+
+#ifndef EM_LEGACY_IRQ
+/* How many packets rxeof tries to clean at a time */
+static int lem_rx_process_limit = 100;
+TUNABLE_INT("hw.em.rx_process_limit", &lem_rx_process_limit);
+#endif
+
+/* Flow control setting - default to FULL */
+static int lem_fc_setting = e1000_fc_full;
+TUNABLE_INT("hw.em.fc_setting", &lem_fc_setting);
+
+/*
+** Shadow VFTA table, this is needed because
+** the real vlan filter table gets cleared during
+** a soft reset and the driver needs to be able
+** to repopulate it.
+*/
+static u32 lem_shadow_vfta[EM_VFTA_SIZE];
+
+/* Global used in WOL setup with multiport cards */
+static int global_quad_port_a = 0;
+
+/*********************************************************************
+ * Device identification routine
+ *
+ * em_probe determines if the driver should be loaded on
+ * adapter based on PCI vendor/device id of the adapter.
+ *
+ * return BUS_PROBE_DEFAULT on success, positive on failure
+ *********************************************************************/
+
+static int
+lem_probe(device_t dev)
+{
+ char adapter_name[60];
+ u16 pci_vendor_id = 0;
+ u16 pci_device_id = 0;
+ u16 pci_subvendor_id = 0;
+ u16 pci_subdevice_id = 0;
+ em_vendor_info_t *ent;
+
+ INIT_DEBUGOUT("em_probe: begin");
+
+ pci_vendor_id = pci_get_vendor(dev);
+ if (pci_vendor_id != EM_VENDOR_ID)
+ return (ENXIO);
+
+ pci_device_id = pci_get_device(dev);
+ pci_subvendor_id = pci_get_subvendor(dev);
+ pci_subdevice_id = pci_get_subdevice(dev);
+
+ ent = lem_vendor_info_array;
+ while (ent->vendor_id != 0) {
+ if ((pci_vendor_id == ent->vendor_id) &&
+ (pci_device_id == ent->device_id) &&
+
+ ((pci_subvendor_id == ent->subvendor_id) ||
+ (ent->subvendor_id == PCI_ANY_ID)) &&
+
+ ((pci_subdevice_id == ent->subdevice_id) ||
+ (ent->subdevice_id == PCI_ANY_ID))) {
+ sprintf(adapter_name, "%s %s",
+ lem_strings[ent->index],
+ lem_driver_version);
+ device_set_desc_copy(dev, adapter_name);
+ return (BUS_PROBE_DEFAULT);
+ }
+ ent++;
+ }
+
+ return (ENXIO);
+}
+
+/*********************************************************************
+ * Device initialization routine
+ *
+ * The attach entry point is called when the driver is being loaded.
+ * This routine identifies the type of hardware, allocates all resources
+ * and initializes the hardware.
+ *
+ * return 0 on success, positive on failure
+ *********************************************************************/
+
+static int
+lem_attach(device_t dev)
+{
+ struct adapter *adapter;
+ int tsize, rsize;
+ int error = 0;
+
+ INIT_DEBUGOUT("lem_attach: begin");
+
+ adapter = device_get_softc(dev);
+ adapter->dev = adapter->osdep.dev = dev;
+ EM_CORE_LOCK_INIT(adapter, device_get_nameunit(dev));
+ EM_TX_LOCK_INIT(adapter, device_get_nameunit(dev));
+ EM_RX_LOCK_INIT(adapter, device_get_nameunit(dev));
+
+ /* SYSCTL stuff */
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "debug", CTLTYPE_INT|CTLFLAG_RW, adapter, 0,
+ lem_sysctl_debug_info, "I", "Debug Information");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "stats", CTLTYPE_INT|CTLFLAG_RW, adapter, 0,
+ lem_sysctl_stats, "I", "Statistics");
+
+ callout_init_mtx(&adapter->timer, &adapter->core_mtx, 0);
+ callout_init_mtx(&adapter->tx_fifo_timer, &adapter->tx_mtx, 0);
+
+ /* Determine hardware and mac info */
+ lem_identify_hardware(adapter);
+
+ /* Setup PCI resources */
+ if (lem_allocate_pci_resources(adapter)) {
+ device_printf(dev, "Allocation of PCI resources failed\n");
+ error = ENXIO;
+ goto err_pci;
+ }
+
+ /* Do Shared Code initialization */
+ if (e1000_setup_init_funcs(&adapter->hw, TRUE)) {
+ device_printf(dev, "Setup of Shared code failed\n");
+ error = ENXIO;
+ goto err_pci;
+ }
+
+ e1000_get_bus_info(&adapter->hw);
+
+ /* Set up some sysctls for the tunable interrupt delays */
+ lem_add_int_delay_sysctl(adapter, "rx_int_delay",
+ "receive interrupt delay in usecs", &adapter->rx_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_RDTR), lem_rx_int_delay_dflt);
+ lem_add_int_delay_sysctl(adapter, "tx_int_delay",
+ "transmit interrupt delay in usecs", &adapter->tx_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_TIDV), lem_tx_int_delay_dflt);
+ if (adapter->hw.mac.type >= e1000_82540) {
+ lem_add_int_delay_sysctl(adapter, "rx_abs_int_delay",
+ "receive interrupt delay limit in usecs",
+ &adapter->rx_abs_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_RADV),
+ lem_rx_abs_int_delay_dflt);
+ lem_add_int_delay_sysctl(adapter, "tx_abs_int_delay",
+ "transmit interrupt delay limit in usecs",
+ &adapter->tx_abs_int_delay,
+ E1000_REGISTER(&adapter->hw, E1000_TADV),
+ lem_tx_abs_int_delay_dflt);
+ }
+
+#ifndef EM_LEGACY_IRQ
+ /* Sysctls for limiting the amount of work done in the taskqueue */
+ lem_add_rx_process_limit(adapter, "rx_processing_limit",
+ "max number of rx packets to process", &adapter->rx_process_limit,
+ lem_rx_process_limit);
+#endif
+
+ /*
+ * Validate number of transmit and receive descriptors. It
+ * must not exceed hardware maximum, and must be multiple
+ * of E1000_DBA_ALIGN.
+ */
+ if (((lem_txd * sizeof(struct e1000_tx_desc)) % EM_DBA_ALIGN) != 0 ||
+ (adapter->hw.mac.type >= e1000_82544 && lem_txd > EM_MAX_TXD) ||
+ (adapter->hw.mac.type < e1000_82544 && lem_txd > EM_MAX_TXD_82543) ||
+ (lem_txd < EM_MIN_TXD)) {
+ device_printf(dev, "Using %d TX descriptors instead of %d!\n",
+ EM_DEFAULT_TXD, lem_txd);
+ adapter->num_tx_desc = EM_DEFAULT_TXD;
+ } else
+ adapter->num_tx_desc = lem_txd;
+ if (((lem_rxd * sizeof(struct e1000_rx_desc)) % EM_DBA_ALIGN) != 0 ||
+ (adapter->hw.mac.type >= e1000_82544 && lem_rxd > EM_MAX_RXD) ||
+ (adapter->hw.mac.type < e1000_82544 && lem_rxd > EM_MAX_RXD_82543) ||
+ (lem_rxd < EM_MIN_RXD)) {
+ device_printf(dev, "Using %d RX descriptors instead of %d!\n",
+ EM_DEFAULT_RXD, lem_rxd);
+ adapter->num_rx_desc = EM_DEFAULT_RXD;
+ } else
+ adapter->num_rx_desc = lem_rxd;
+
+ adapter->hw.mac.autoneg = DO_AUTO_NEG;
+ adapter->hw.phy.autoneg_wait_to_complete = FALSE;
+ adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT;
+ adapter->rx_buffer_len = 2048;
+
+ e1000_init_script_state_82541(&adapter->hw, TRUE);
+ e1000_set_tbi_compatibility_82543(&adapter->hw, TRUE);
+
+ /* Copper options */
+ if (adapter->hw.phy.media_type == e1000_media_type_copper) {
+ adapter->hw.phy.mdix = AUTO_ALL_MODES;
+ adapter->hw.phy.disable_polarity_correction = FALSE;
+ adapter->hw.phy.ms_type = EM_MASTER_SLAVE;
+ }
+
+ /*
+ * Set the frame limits assuming
+ * standard ethernet sized frames.
+ */
+ adapter->max_frame_size = ETHERMTU + ETHER_HDR_LEN + ETHERNET_FCS_SIZE;
+ adapter->min_frame_size = ETH_ZLEN + ETHERNET_FCS_SIZE;
+
+ /*
+ * This controls when hardware reports transmit completion
+ * status.
+ */
+ adapter->hw.mac.report_tx_early = 1;
+
+ tsize = roundup2(adapter->num_tx_desc * sizeof(struct e1000_tx_desc),
+ EM_DBA_ALIGN);
+
+ /* Allocate Transmit Descriptor ring */
+ if (lem_dma_malloc(adapter, tsize, &adapter->txdma, BUS_DMA_NOWAIT)) {
+ device_printf(dev, "Unable to allocate tx_desc memory\n");
+ error = ENOMEM;
+ goto err_tx_desc;
+ }
+ adapter->tx_desc_base =
+ (struct e1000_tx_desc *)adapter->txdma.dma_vaddr;
+
+ rsize = roundup2(adapter->num_rx_desc * sizeof(struct e1000_rx_desc),
+ EM_DBA_ALIGN);
+
+ /* Allocate Receive Descriptor ring */
+ if (lem_dma_malloc(adapter, rsize, &adapter->rxdma, BUS_DMA_NOWAIT)) {
+ device_printf(dev, "Unable to allocate rx_desc memory\n");
+ error = ENOMEM;
+ goto err_rx_desc;
+ }
+ adapter->rx_desc_base =
+ (struct e1000_rx_desc *)adapter->rxdma.dma_vaddr;
+
+ /*
+ ** Start from a known state, this is
+ ** important in reading the nvm and
+ ** mac from that.
+ */
+ e1000_reset_hw(&adapter->hw);
+
+ /* Make sure we have a good EEPROM before we read from it */
+ if (e1000_validate_nvm_checksum(&adapter->hw) < 0) {
+ /*
+ ** Some PCI-E parts fail the first check due to
+ ** the link being in sleep state, call it again,
+ ** if it fails a second time its a real issue.
+ */
+ if (e1000_validate_nvm_checksum(&adapter->hw) < 0) {
+ device_printf(dev,
+ "The EEPROM Checksum Is Not Valid\n");
+ error = EIO;
+ goto err_hw_init;
+ }
+ }
+
+ /* Copy the permanent MAC address out of the EEPROM */
+ if (e1000_read_mac_addr(&adapter->hw) < 0) {
+ device_printf(dev, "EEPROM read error while reading MAC"
+ " address\n");
+ error = EIO;
+ goto err_hw_init;
+ }
+
+ if (!lem_is_valid_ether_addr(adapter->hw.mac.addr)) {
+ device_printf(dev, "Invalid MAC address\n");
+ error = EIO;
+ goto err_hw_init;
+ }
+
+ /* Initialize the hardware */
+ if (lem_hardware_init(adapter)) {
+ device_printf(dev, "Unable to initialize the hardware\n");
+ error = EIO;
+ goto err_hw_init;
+ }
+
+ /* Allocate transmit descriptors and buffers */
+ if (lem_allocate_transmit_structures(adapter)) {
+ device_printf(dev, "Could not setup transmit structures\n");
+ error = ENOMEM;
+ goto err_tx_struct;
+ }
+
+ /* Allocate receive descriptors and buffers */
+ if (lem_allocate_receive_structures(adapter)) {
+ device_printf(dev, "Could not setup receive structures\n");
+ error = ENOMEM;
+ goto err_rx_struct;
+ }
+
+ /*
+ ** Do interrupt configuration
+ */
+ error = lem_allocate_irq(adapter);
+ if (error)
+ goto err_rx_struct;
+
+ /*
+ * Get Wake-on-Lan and Management info for later use
+ */
+ lem_get_wakeup(dev);
+
+ /* Setup OS specific network interface */
+ lem_setup_interface(dev, adapter);
+
+ /* Initialize statistics */
+ lem_update_stats_counters(adapter);
+
+ adapter->hw.mac.get_link_status = 1;
+ lem_update_link_status(adapter);
+
+ /* Indicate SOL/IDER usage */
+ if (e1000_check_reset_block(&adapter->hw))
+ device_printf(dev,
+ "PHY reset is blocked due to SOL/IDER session.\n");
+
+ /* Do we need workaround for 82544 PCI-X adapter? */
+ if (adapter->hw.bus.type == e1000_bus_type_pcix &&
+ adapter->hw.mac.type == e1000_82544)
+ adapter->pcix_82544 = TRUE;
+ else
+ adapter->pcix_82544 = FALSE;
+
+#if __FreeBSD_version >= 700029
+ /* Register for VLAN events */
+ adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
+ lem_register_vlan, adapter, EVENTHANDLER_PRI_FIRST);
+ adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
+ lem_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST);
+#endif
+
+ /* Non-AMT based hardware can now take control from firmware */
+ if (adapter->has_manage && !adapter->has_amt)
+ lem_get_hw_control(adapter);
+
+ /* Tell the stack that the interface is not active */
+ adapter->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ adapter->led_dev = led_create(lem_led_func, adapter,
+ device_get_nameunit(dev));
+
+ INIT_DEBUGOUT("lem_attach: end");
+
+ return (0);
+
+err_rx_struct:
+ lem_free_transmit_structures(adapter);
+err_tx_struct:
+err_hw_init:
+ lem_release_hw_control(adapter);
+ lem_dma_free(adapter, &adapter->rxdma);
+err_rx_desc:
+ lem_dma_free(adapter, &adapter->txdma);
+err_tx_desc:
+err_pci:
+ lem_free_pci_resources(adapter);
+ EM_TX_LOCK_DESTROY(adapter);
+ EM_RX_LOCK_DESTROY(adapter);
+ EM_CORE_LOCK_DESTROY(adapter);
+
+ return (error);
+}
+
+/*********************************************************************
+ * Device removal routine
+ *
+ * The detach entry point is called when the driver is being removed.
+ * This routine stops the adapter and deallocates all the resources
+ * that were allocated for driver operation.
+ *
+ * return 0 on success, positive on failure
+ *********************************************************************/
+
+static int
+lem_detach(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+ struct ifnet *ifp = adapter->ifp;
+
+ INIT_DEBUGOUT("em_detach: begin");
+
+ /* Make sure VLANS are not using driver */
+#if __FreeBSD_version >= 700000
+ if (adapter->ifp->if_vlantrunk != NULL) {
+#else
+ if (adapter->ifp->if_nvlans != 0) {
+#endif
+ device_printf(dev,"Vlan in use, detach first\n");
+ return (EBUSY);
+ }
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING)
+ ether_poll_deregister(ifp);
+#endif
+
+ if (adapter->led_dev != NULL)
+ led_destroy(adapter->led_dev);
+
+ EM_CORE_LOCK(adapter);
+ EM_TX_LOCK(adapter);
+ adapter->in_detach = 1;
+ lem_stop(adapter);
+ e1000_phy_hw_reset(&adapter->hw);
+
+ lem_release_manageability(adapter);
+
+ EM_TX_UNLOCK(adapter);
+ EM_CORE_UNLOCK(adapter);
+
+#if __FreeBSD_version >= 700029
+ /* Unregister VLAN events */
+ if (adapter->vlan_attach != NULL)
+ EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach);
+ if (adapter->vlan_detach != NULL)
+ EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach);
+#endif
+
+ ether_ifdetach(adapter->ifp);
+ callout_drain(&adapter->timer);
+ callout_drain(&adapter->tx_fifo_timer);
+
+ lem_free_pci_resources(adapter);
+ bus_generic_detach(dev);
+ if_free(ifp);
+
+ lem_free_transmit_structures(adapter);
+ lem_free_receive_structures(adapter);
+
+ /* Free Transmit Descriptor ring */
+ if (adapter->tx_desc_base) {
+ lem_dma_free(adapter, &adapter->txdma);
+ adapter->tx_desc_base = NULL;
+ }
+
+ /* Free Receive Descriptor ring */
+ if (adapter->rx_desc_base) {
+ lem_dma_free(adapter, &adapter->rxdma);
+ adapter->rx_desc_base = NULL;
+ }
+
+ lem_release_hw_control(adapter);
+ EM_TX_LOCK_DESTROY(adapter);
+ EM_RX_LOCK_DESTROY(adapter);
+ EM_CORE_LOCK_DESTROY(adapter);
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Shutdown entry point
+ *
+ **********************************************************************/
+
+static int
+lem_shutdown(device_t dev)
+{
+ return lem_suspend(dev);
+}
+
+/*
+ * Suspend/resume device methods.
+ */
+static int
+lem_suspend(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+
+ EM_CORE_LOCK(adapter);
+
+ lem_release_manageability(adapter);
+ lem_release_hw_control(adapter);
+ lem_enable_wakeup(dev);
+
+ EM_CORE_UNLOCK(adapter);
+
+ return bus_generic_suspend(dev);
+}
+
+static int
+lem_resume(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+ struct ifnet *ifp = adapter->ifp;
+
+ EM_CORE_LOCK(adapter);
+ lem_init_locked(adapter);
+ lem_init_manageability(adapter);
+ EM_CORE_UNLOCK(adapter);
+ lem_start(ifp);
+
+ return bus_generic_resume(dev);
+}
+
+
+/*********************************************************************
+ * Transmit entry point
+ *
+ * em_start is called by the stack to initiate a transmit.
+ * The driver will remain in this routine as long as there are
+ * packets to transmit and transmit resources are available.
+ * In case resources are not available stack is notified and
+ * the packet is requeued.
+ **********************************************************************/
+
+#if __FreeBSD_version >= 800000
+static int
+lem_mq_start_locked(struct ifnet *ifp, struct mbuf *m)
+{
+ struct adapter *adapter = ifp->if_softc;
+ struct mbuf *next;
+ int error = E1000_SUCCESS;
+
+ EM_TX_LOCK_ASSERT(adapter);
+ /* To allow being called from a tasklet */
+ if (m == NULL)
+ goto process;
+
+ if (((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ || (!adapter->link_active)) {
+ error = drbr_enqueue(ifp, adapter->br, m);
+ return (error);
+ } else if (drbr_empty(ifp, adapter->br) &&
+ (adapter->num_tx_desc_avail > EM_TX_OP_THRESHOLD)) {
+ if ((error = lem_xmit(adapter, &m)) != 0) {
+ if (m)
+ error = drbr_enqueue(ifp, adapter->br, m);
+ return (error);
+ } else {
+ /*
+ * We've bypassed the buf ring so we need to update
+ * ifp directly
+ */
+ drbr_stats_update(ifp, m->m_pkthdr.len, m->m_flags);
+ /*
+ ** Send a copy of the frame to the BPF
+ ** listener and set the watchdog on.
+ */
+ ETHER_BPF_MTAP(ifp, m);
+ adapter->watchdog_check = TRUE;
+ }
+ } else if ((error = drbr_enqueue(ifp, adapter->br, m)) != 0)
+ return (error);
+
+process:
+ if (drbr_empty(ifp, adapter->br))
+ return(error);
+ /* Process the queue */
+ while (TRUE) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
+ next = drbr_dequeue(ifp, adapter->br);
+ if (next == NULL)
+ break;
+ if ((error = lem_xmit(adapter, &next)) != 0) {
+ if (next != NULL)
+ error = drbr_enqueue(ifp, adapter->br, next);
+ break;
+ }
+ drbr_stats_update(ifp, next->m_pkthdr.len, next->m_flags);
+ ETHER_BPF_MTAP(ifp, next);
+ /* Set the watchdog */
+ adapter->watchdog_check = TRUE;
+ }
+
+ if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD)
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ return (error);
+}
+
+/*
+** Multiqueue capable stack interface, this is not
+** yet truely multiqueue, but that is coming...
+*/
+static int
+lem_mq_start(struct ifnet *ifp, struct mbuf *m)
+{
+
+ struct adapter *adapter = ifp->if_softc;
+ int error = 0;
+
+ if (EM_TX_TRYLOCK(adapter)) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ error = lem_mq_start_locked(ifp, m);
+ EM_TX_UNLOCK(adapter);
+ } else
+ error = drbr_enqueue(ifp, adapter->br, m);
+
+ return (error);
+}
+
+static void
+lem_qflush(struct ifnet *ifp)
+{
+ struct mbuf *m;
+ struct adapter *adapter = (struct adapter *)ifp->if_softc;
+
+ EM_TX_LOCK(adapter);
+ while ((m = buf_ring_dequeue_sc(adapter->br)) != NULL)
+ m_freem(m);
+ if_qflush(ifp);
+ EM_TX_UNLOCK(adapter);
+}
+#endif /* FreeBSD_version */
+
+static void
+lem_start_locked(struct ifnet *ifp)
+{
+ struct adapter *adapter = ifp->if_softc;
+ struct mbuf *m_head;
+
+ EM_TX_LOCK_ASSERT(adapter);
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return;
+ if (!adapter->link_active)
+ return;
+
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL)
+ break;
+ /*
+ * Encapsulation can modify our pointer, and or make it
+ * NULL on failure. In that event, we can't requeue.
+ */
+ if (lem_xmit(adapter, &m_head)) {
+ if (m_head == NULL)
+ break;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+ break;
+ }
+
+ /* Send a copy of the frame to the BPF listener */
+ ETHER_BPF_MTAP(ifp, m_head);
+
+ /* Set timeout in case hardware has problems transmitting. */
+ adapter->watchdog_check = TRUE;
+ }
+ if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD)
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ return;
+}
+
+static void
+lem_start(struct ifnet *ifp)
+{
+ struct adapter *adapter = ifp->if_softc;
+
+ EM_TX_LOCK(adapter);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ lem_start_locked(ifp);
+ EM_TX_UNLOCK(adapter);
+}
+
+/*********************************************************************
+ * Ioctl entry point
+ *
+ * em_ioctl is called when the user wants to configure the
+ * interface.
+ *
+ * return 0 on success, positive on failure
+ **********************************************************************/
+
+static int
+lem_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct adapter *adapter = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+#ifdef INET
+ struct ifaddr *ifa = (struct ifaddr *)data;
+#endif
+ int error = 0;
+
+ if (adapter->in_detach)
+ return (error);
+
+ switch (command) {
+ case SIOCSIFADDR:
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ /*
+ * XXX
+ * Since resetting hardware takes a very long time
+ * and results in link renegotiation we only
+ * initialize the hardware only when it is absolutely
+ * required.
+ */
+ ifp->if_flags |= IFF_UP;
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ EM_CORE_LOCK(adapter);
+ lem_init_locked(adapter);
+ EM_CORE_UNLOCK(adapter);
+ }
+ arp_ifinit(ifp, ifa);
+ } else
+#endif
+ error = ether_ioctl(ifp, command, data);
+ break;
+ case SIOCSIFMTU:
+ {
+ int max_frame_size;
+
+ IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)");
+
+ EM_CORE_LOCK(adapter);
+ switch (adapter->hw.mac.type) {
+ case e1000_82542:
+ max_frame_size = ETHER_MAX_LEN;
+ break;
+ default:
+ max_frame_size = MAX_JUMBO_FRAME_SIZE;
+ }
+ if (ifr->ifr_mtu > max_frame_size - ETHER_HDR_LEN -
+ ETHER_CRC_LEN) {
+ EM_CORE_UNLOCK(adapter);
+ error = EINVAL;
+ break;
+ }
+
+ ifp->if_mtu = ifr->ifr_mtu;
+ adapter->max_frame_size =
+ ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
+ lem_init_locked(adapter);
+ EM_CORE_UNLOCK(adapter);
+ break;
+ }
+ case SIOCSIFFLAGS:
+ IOCTL_DEBUGOUT("ioctl rcv'd:\
+ SIOCSIFFLAGS (Set Interface Flags)");
+ EM_CORE_LOCK(adapter);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ if ((ifp->if_flags ^ adapter->if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI)) {
+ lem_disable_promisc(adapter);
+ lem_set_promisc(adapter);
+ }
+ } else
+ lem_init_locked(adapter);
+ } else
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ EM_TX_LOCK(adapter);
+ lem_stop(adapter);
+ EM_TX_UNLOCK(adapter);
+ }
+ adapter->if_flags = ifp->if_flags;
+ EM_CORE_UNLOCK(adapter);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ IOCTL_DEBUGOUT("ioctl rcv'd: SIOC(ADD|DEL)MULTI");
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ EM_CORE_LOCK(adapter);
+ lem_disable_intr(adapter);
+ lem_set_multi(adapter);
+ if (adapter->hw.mac.type == e1000_82542 &&
+ adapter->hw.revision_id == E1000_REVISION_2) {
+ lem_initialize_receive_unit(adapter);
+ }
+#ifdef DEVICE_POLLING
+ if (!(ifp->if_capenable & IFCAP_POLLING))
+#endif
+ lem_enable_intr(adapter);
+ EM_CORE_UNLOCK(adapter);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ /* Check SOL/IDER usage */
+ EM_CORE_LOCK(adapter);
+ if (e1000_check_reset_block(&adapter->hw)) {
+ EM_CORE_UNLOCK(adapter);
+ device_printf(adapter->dev, "Media change is"
+ " blocked due to SOL/IDER session.\n");
+ break;
+ }
+ EM_CORE_UNLOCK(adapter);
+ case SIOCGIFMEDIA:
+ IOCTL_DEBUGOUT("ioctl rcv'd: \
+ SIOCxIFMEDIA (Get/Set Interface Media)");
+ error = ifmedia_ioctl(ifp, ifr, &adapter->media, command);
+ break;
+ case SIOCSIFCAP:
+ {
+ int mask, reinit;
+
+ IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFCAP (Set Capabilities)");
+ reinit = 0;
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+#ifdef DEVICE_POLLING
+ if (mask & IFCAP_POLLING) {
+ if (ifr->ifr_reqcap & IFCAP_POLLING) {
+ error = ether_poll_register(lem_poll, ifp);
+ if (error)
+ return (error);
+ EM_CORE_LOCK(adapter);
+ lem_disable_intr(adapter);
+ ifp->if_capenable |= IFCAP_POLLING;
+ EM_CORE_UNLOCK(adapter);
+ } else {
+ error = ether_poll_deregister(ifp);
+ /* Enable interrupt even in error case */
+ EM_CORE_LOCK(adapter);
+ lem_enable_intr(adapter);
+ ifp->if_capenable &= ~IFCAP_POLLING;
+ EM_CORE_UNLOCK(adapter);
+ }
+ }
+#endif
+ if (mask & IFCAP_HWCSUM) {
+ ifp->if_capenable ^= IFCAP_HWCSUM;
+ reinit = 1;
+ }
+#if __FreeBSD_version >= 700000
+ if (mask & IFCAP_TSO4) {
+ ifp->if_capenable ^= IFCAP_TSO4;
+ reinit = 1;
+ }
+#endif
+ if (mask & IFCAP_VLAN_HWTAGGING) {
+ ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
+ reinit = 1;
+ }
+ if ((mask & IFCAP_WOL) &&
+ (ifp->if_capabilities & IFCAP_WOL) != 0) {
+ if (mask & IFCAP_WOL_MCAST)
+ ifp->if_capenable ^= IFCAP_WOL_MCAST;
+ if (mask & IFCAP_WOL_MAGIC)
+ ifp->if_capenable ^= IFCAP_WOL_MAGIC;
+ }
+ if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING))
+ lem_init(adapter);
+#if __FreeBSD_version >= 700000
+ VLAN_CAPABILITIES(ifp);
+#endif
+ break;
+ }
+
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return (error);
+}
+
+
+/*********************************************************************
+ * Init entry point
+ *
+ * This routine is used in two ways. It is used by the stack as
+ * init entry point in network interface structure. It is also used
+ * by the driver as a hw/sw initialization routine to get to a
+ * consistent state.
+ *
+ * return 0 on success, positive on failure
+ **********************************************************************/
+
+static void
+lem_init_locked(struct adapter *adapter)
+{
+ struct ifnet *ifp = adapter->ifp;
+ device_t dev = adapter->dev;
+ u32 pba;
+
+ INIT_DEBUGOUT("lem_init: begin");
+
+ EM_CORE_LOCK_ASSERT(adapter);
+
+ EM_TX_LOCK(adapter);
+ lem_stop(adapter);
+ EM_TX_UNLOCK(adapter);
+
+ /*
+ * Packet Buffer Allocation (PBA)
+ * Writing PBA sets the receive portion of the buffer
+ * the remainder is used for the transmit buffer.
+ *
+ * Devices before the 82547 had a Packet Buffer of 64K.
+ * Default allocation: PBA=48K for Rx, leaving 16K for Tx.
+ * After the 82547 the buffer was reduced to 40K.
+ * Default allocation: PBA=30K for Rx, leaving 10K for Tx.
+ * Note: default does not leave enough room for Jumbo Frame >10k.
+ */
+ switch (adapter->hw.mac.type) {
+ case e1000_82547:
+ case e1000_82547_rev_2: /* 82547: Total Packet Buffer is 40K */
+ if (adapter->max_frame_size > 8192)
+ pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */
+ else
+ pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */
+ adapter->tx_fifo_head = 0;
+ adapter->tx_head_addr = pba << EM_TX_HEAD_ADDR_SHIFT;
+ adapter->tx_fifo_size =
+ (E1000_PBA_40K - pba) << EM_PBA_BYTES_SHIFT;
+ break;
+ default:
+ /* Devices before 82547 had a Packet Buffer of 64K. */
+ if (adapter->max_frame_size > 8192)
+ pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */
+ else
+ pba = E1000_PBA_48K; /* 48K for Rx, 16K for Tx */
+ }
+
+ INIT_DEBUGOUT1("lem_init: pba=%dK",pba);
+ E1000_WRITE_REG(&adapter->hw, E1000_PBA, pba);
+
+ /* Get the latest mac address, User can use a LAA */
+ bcopy(IF_LLADDR(adapter->ifp), adapter->hw.mac.addr,
+ ETHER_ADDR_LEN);
+
+ /* Put the address into the Receive Address Array */
+ e1000_rar_set(&adapter->hw, adapter->hw.mac.addr, 0);
+
+ /* Initialize the hardware */
+ if (lem_hardware_init(adapter)) {
+ device_printf(dev, "Unable to initialize the hardware\n");
+ return;
+ }
+ lem_update_link_status(adapter);
+
+ /* Setup VLAN support, basic and offload if available */
+ E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN);
+
+#if __FreeBSD_version < 700029
+ if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) {
+ u32 ctrl;
+ ctrl = E1000_READ_REG(&adapter->hw, E1000_CTRL);
+ ctrl |= E1000_CTRL_VME;
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL, ctrl);
+ }
+#else
+ /* Use real VLAN Filter support */
+ lem_setup_vlan_hw_support(adapter);
+#endif
+
+ /* Set hardware offload abilities */
+ ifp->if_hwassist = 0;
+ if (adapter->hw.mac.type >= e1000_82543) {
+ if (ifp->if_capenable & IFCAP_TXCSUM)
+ ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
+#if __FreeBSD_version >= 700000
+ if (ifp->if_capenable & IFCAP_TSO4)
+ ifp->if_hwassist |= CSUM_TSO;
+#endif
+ }
+
+ /* Configure for OS presence */
+ lem_init_manageability(adapter);
+
+ /* Prepare transmit descriptors and buffers */
+ lem_setup_transmit_structures(adapter);
+ lem_initialize_transmit_unit(adapter);
+
+ /* Setup Multicast table */
+ lem_set_multi(adapter);
+
+ /* Prepare receive descriptors and buffers */
+ if (lem_setup_receive_structures(adapter)) {
+ device_printf(dev, "Could not setup receive structures\n");
+ EM_TX_LOCK(adapter);
+ lem_stop(adapter);
+ EM_TX_UNLOCK(adapter);
+ return;
+ }
+ lem_initialize_receive_unit(adapter);
+
+ /* Don't lose promiscuous settings */
+ lem_set_promisc(adapter);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ callout_reset(&adapter->timer, hz, lem_local_timer, adapter);
+ e1000_clear_hw_cntrs_base_generic(&adapter->hw);
+
+ /* MSI/X configuration for 82574 */
+ if (adapter->hw.mac.type == e1000_82574) {
+ int tmp;
+ tmp = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ tmp |= E1000_CTRL_EXT_PBA_CLR;
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, tmp);
+ /*
+ ** Set the IVAR - interrupt vector routing.
+ ** Each nibble represents a vector, high bit
+ ** is enable, other 3 bits are the MSIX table
+ ** entry, we map RXQ0 to 0, TXQ0 to 1, and
+ ** Link (other) to 2, hence the magic number.
+ */
+ E1000_WRITE_REG(&adapter->hw, E1000_IVAR, 0x800A0908);
+ }
+
+#ifdef DEVICE_POLLING
+ /*
+ * Only enable interrupts if we are not polling, make sure
+ * they are off otherwise.
+ */
+ if (ifp->if_capenable & IFCAP_POLLING)
+ lem_disable_intr(adapter);
+ else
+#endif /* DEVICE_POLLING */
+ lem_enable_intr(adapter);
+
+ /* AMT based hardware can now take control from firmware */
+ if (adapter->has_manage && adapter->has_amt)
+ lem_get_hw_control(adapter);
+
+ /* Don't reset the phy next time init gets called */
+ adapter->hw.phy.reset_disable = TRUE;
+}
+
+static void
+lem_init(void *arg)
+{
+ struct adapter *adapter = arg;
+
+ EM_CORE_LOCK(adapter);
+ lem_init_locked(adapter);
+ EM_CORE_UNLOCK(adapter);
+}
+
+
+#ifdef DEVICE_POLLING
+/*********************************************************************
+ *
+ * Legacy polling routine
+ *
+ *********************************************************************/
+static int
+lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct adapter *adapter = ifp->if_softc;
+ u32 reg_icr, rx_done = 0;
+
+ EM_CORE_LOCK(adapter);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ EM_CORE_UNLOCK(adapter);
+ return (rx_done);
+ }
+
+ if (cmd == POLL_AND_CHECK_STATUS) {
+ reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
+ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
+ callout_stop(&adapter->timer);
+ adapter->hw.mac.get_link_status = 1;
+ lem_update_link_status(adapter);
+ callout_reset(&adapter->timer, hz,
+ lem_local_timer, adapter);
+ }
+ }
+ EM_CORE_UNLOCK(adapter);
+
+ rx_done = lem_rxeof(adapter, count);
+
+ EM_TX_LOCK(adapter);
+ lem_txeof(adapter);
+#if __FreeBSD_version >= 800000
+ if (!drbr_empty(ifp, adapter->br))
+ lem_mq_start_locked(ifp, NULL);
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ lem_start_locked(ifp);
+#endif
+ EM_TX_UNLOCK(adapter);
+ return (rx_done);
+}
+#endif /* DEVICE_POLLING */
+
+#ifdef EM_LEGACY_IRQ
+/*********************************************************************
+ *
+ * Legacy Interrupt Service routine
+ *
+ *********************************************************************/
+
+static void
+lem_intr(void *arg)
+{
+ struct adapter *adapter = arg;
+ struct ifnet *ifp = adapter->ifp;
+ u32 reg_icr;
+
+
+ if (ifp->if_capenable & IFCAP_POLLING)
+ return;
+
+ EM_CORE_LOCK(adapter);
+ reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
+ if (reg_icr & E1000_ICR_RXO)
+ adapter->rx_overruns++;
+
+ if ((reg_icr == 0xffffffff) || (reg_icr == 0))
+ goto out;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto out;
+
+ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
+ callout_stop(&adapter->timer);
+ adapter->hw.mac.get_link_status = 1;
+ lem_update_link_status(adapter);
+ /* Deal with TX cruft when link lost */
+ lem_tx_purge(adapter);
+ callout_reset(&adapter->timer, hz,
+ lem_local_timer, adapter);
+ goto out;
+ }
+
+ EM_TX_LOCK(adapter);
+ lem_txeof(adapter);
+ lem_rxeof(adapter, -1);
+ lem_txeof(adapter);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ lem_start_locked(ifp);
+ EM_TX_UNLOCK(adapter);
+
+out:
+ EM_CORE_UNLOCK(adapter);
+ return;
+}
+
+#else /* EM_FAST_IRQ, then fast interrupt routines only */
+
+static void
+lem_handle_link(void *context, int pending)
+{
+ struct adapter *adapter = context;
+ struct ifnet *ifp = adapter->ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ return;
+
+ EM_CORE_LOCK(adapter);
+ callout_stop(&adapter->timer);
+ lem_update_link_status(adapter);
+ /* Deal with TX cruft when link lost */
+ lem_tx_purge(adapter);
+ callout_reset(&adapter->timer, hz, lem_local_timer, adapter);
+ EM_CORE_UNLOCK(adapter);
+}
+
+
+/* Combined RX/TX handler, used by Legacy and MSI */
+static void
+lem_handle_rxtx(void *context, int pending)
+{
+ struct adapter *adapter = context;
+ struct ifnet *ifp = adapter->ifp;
+
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (lem_rxeof(adapter, adapter->rx_process_limit) != 0)
+ taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
+ EM_TX_LOCK(adapter);
+ lem_txeof(adapter);
+
+#if __FreeBSD_version >= 800000
+ if (!drbr_empty(ifp, adapter->br))
+ lem_mq_start_locked(ifp, NULL);
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ lem_start_locked(ifp);
+#endif
+ EM_TX_UNLOCK(adapter);
+ }
+
+ lem_enable_intr(adapter);
+}
+
+/*********************************************************************
+ *
+ * Fast Legacy/MSI Combined Interrupt Service routine
+ *
+ *********************************************************************/
+#if __FreeBSD_version < 700000
+#define FILTER_STRAY
+#define FILTER_HANDLED
+static void
+#else
+static int
+#endif
+lem_irq_fast(void *arg)
+{
+ struct adapter *adapter = arg;
+ struct ifnet *ifp;
+ u32 reg_icr;
+
+ ifp = adapter->ifp;
+
+ reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR);
+
+ /* Hot eject? */
+ if (reg_icr == 0xffffffff)
+ return FILTER_STRAY;
+
+ /* Definitely not our interrupt. */
+ if (reg_icr == 0x0)
+ return FILTER_STRAY;
+
+ /*
+ * Mask interrupts until the taskqueue is finished running. This is
+ * cheap, just assume that it is needed. This also works around the
+ * MSI message reordering errata on certain systems.
+ */
+ lem_disable_intr(adapter);
+ taskqueue_enqueue(adapter->tq, &adapter->rxtx_task);
+
+ /* Link status change */
+ if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
+ adapter->hw.mac.get_link_status = 1;
+ taskqueue_enqueue(taskqueue_fast, &adapter->link_task);
+ }
+
+ if (reg_icr & E1000_ICR_RXO)
+ adapter->rx_overruns++;
+ return FILTER_HANDLED;
+}
+#endif /* ~EM_LEGACY_IRQ */
+
+
+/*********************************************************************
+ *
+ * Media Ioctl callback
+ *
+ * This routine is called whenever the user queries the status of
+ * the interface using ifconfig.
+ *
+ **********************************************************************/
+static void
+lem_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct adapter *adapter = ifp->if_softc;
+ u_char fiber_type = IFM_1000_SX;
+
+ INIT_DEBUGOUT("lem_media_status: begin");
+
+ EM_CORE_LOCK(adapter);
+ lem_update_link_status(adapter);
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ if (!adapter->link_active) {
+ EM_CORE_UNLOCK(adapter);
+ return;
+ }
+
+ ifmr->ifm_status |= IFM_ACTIVE;
+
+ if ((adapter->hw.phy.media_type == e1000_media_type_fiber) ||
+ (adapter->hw.phy.media_type == e1000_media_type_internal_serdes)) {
+ if (adapter->hw.mac.type == e1000_82545)
+ fiber_type = IFM_1000_LX;
+ ifmr->ifm_active |= fiber_type | IFM_FDX;
+ } else {
+ switch (adapter->link_speed) {
+ case 10:
+ ifmr->ifm_active |= IFM_10_T;
+ break;
+ case 100:
+ ifmr->ifm_active |= IFM_100_TX;
+ break;
+ case 1000:
+ ifmr->ifm_active |= IFM_1000_T;
+ break;
+ }
+ if (adapter->link_duplex == FULL_DUPLEX)
+ ifmr->ifm_active |= IFM_FDX;
+ else
+ ifmr->ifm_active |= IFM_HDX;
+ }
+ EM_CORE_UNLOCK(adapter);
+}
+
+/*********************************************************************
+ *
+ * Media Ioctl callback
+ *
+ * This routine is called when the user changes speed/duplex using
+ * media/mediopt option with ifconfig.
+ *
+ **********************************************************************/
+static int
+lem_media_change(struct ifnet *ifp)
+{
+ struct adapter *adapter = ifp->if_softc;
+ struct ifmedia *ifm = &adapter->media;
+
+ INIT_DEBUGOUT("lem_media_change: begin");
+
+ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
+ return (EINVAL);
+
+ EM_CORE_LOCK(adapter);
+ switch (IFM_SUBTYPE(ifm->ifm_media)) {
+ case IFM_AUTO:
+ adapter->hw.mac.autoneg = DO_AUTO_NEG;
+ adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT;
+ break;
+ case IFM_1000_LX:
+ case IFM_1000_SX:
+ case IFM_1000_T:
+ adapter->hw.mac.autoneg = DO_AUTO_NEG;
+ adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
+ break;
+ case IFM_100_TX:
+ adapter->hw.mac.autoneg = FALSE;
+ adapter->hw.phy.autoneg_advertised = 0;
+ if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
+ adapter->hw.mac.forced_speed_duplex = ADVERTISE_100_FULL;
+ else
+ adapter->hw.mac.forced_speed_duplex = ADVERTISE_100_HALF;
+ break;
+ case IFM_10_T:
+ adapter->hw.mac.autoneg = FALSE;
+ adapter->hw.phy.autoneg_advertised = 0;
+ if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
+ adapter->hw.mac.forced_speed_duplex = ADVERTISE_10_FULL;
+ else
+ adapter->hw.mac.forced_speed_duplex = ADVERTISE_10_HALF;
+ break;
+ default:
+ device_printf(adapter->dev, "Unsupported media type\n");
+ }
+
+ /* As the speed/duplex settings my have changed we need to
+ * reset the PHY.
+ */
+ adapter->hw.phy.reset_disable = FALSE;
+
+ lem_init_locked(adapter);
+ EM_CORE_UNLOCK(adapter);
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * This routine maps the mbufs to tx descriptors.
+ *
+ * return 0 on success, positive on failure
+ **********************************************************************/
+
+static int
+lem_xmit(struct adapter *adapter, struct mbuf **m_headp)
+{
+ bus_dma_segment_t segs[EM_MAX_SCATTER];
+ bus_dmamap_t map;
+ struct em_buffer *tx_buffer, *tx_buffer_mapped;
+ struct e1000_tx_desc *ctxd = NULL;
+ struct mbuf *m_head;
+ u32 txd_upper, txd_lower, txd_used, txd_saved;
+ int error, nsegs, i, j, first, last = 0;
+#if __FreeBSD_version < 700000
+ struct m_tag *mtag;
+#endif
+ m_head = *m_headp;
+ txd_upper = txd_lower = txd_used = txd_saved = 0;
+
+ /*
+ * Force a cleanup if number of TX descriptors
+ * available hits the threshold
+ */
+ if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) {
+ lem_txeof(adapter);
+ /* Now do we at least have a minimal? */
+ if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD) {
+ adapter->no_tx_desc_avail1++;
+ return (ENOBUFS);
+ }
+ }
+
+ /*
+ * Map the packet for DMA
+ *
+ * Capture the first descriptor index,
+ * this descriptor will have the index
+ * of the EOP which is the only one that
+ * now gets a DONE bit writeback.
+ */
+ first = adapter->next_avail_tx_desc;
+ tx_buffer = &adapter->tx_buffer_area[first];
+ tx_buffer_mapped = tx_buffer;
+ map = tx_buffer->map;
+
+ error = bus_dmamap_load_mbuf_sg(adapter->txtag, map,
+ *m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
+
+ /*
+ * There are two types of errors we can (try) to handle:
+ * - EFBIG means the mbuf chain was too long and bus_dma ran
+ * out of segments. Defragment the mbuf chain and try again.
+ * - ENOMEM means bus_dma could not obtain enough bounce buffers
+ * at this point in time. Defer sending and try again later.
+ * All other errors, in particular EINVAL, are fatal and prevent the
+ * mbuf chain from ever going through. Drop it and report error.
+ */
+ if (error == EFBIG) {
+ struct mbuf *m;
+
+ m = m_defrag(*m_headp, M_DONTWAIT);
+ if (m == NULL) {
+ adapter->mbuf_alloc_failed++;
+ m_freem(*m_headp);
+ *m_headp = NULL;
+ return (ENOBUFS);
+ }
+ *m_headp = m;
+
+ /* Try it again */
+ error = bus_dmamap_load_mbuf_sg(adapter->txtag, map,
+ *m_headp, segs, &nsegs, BUS_DMA_NOWAIT);
+
+ if (error) {
+ adapter->no_tx_dma_setup++;
+ m_freem(*m_headp);
+ *m_headp = NULL;
+ return (error);
+ }
+ } else if (error != 0) {
+ adapter->no_tx_dma_setup++;
+ return (error);
+ }
+
+ if (nsegs > (adapter->num_tx_desc_avail - 2)) {
+ adapter->no_tx_desc_avail2++;
+ bus_dmamap_unload(adapter->txtag, map);
+ return (ENOBUFS);
+ }
+ m_head = *m_headp;
+
+ /* Do hardware assists */
+ if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD)
+ lem_transmit_checksum_setup(adapter, m_head,
+ &txd_upper, &txd_lower);
+
+ i = adapter->next_avail_tx_desc;
+ if (adapter->pcix_82544)
+ txd_saved = i;
+
+ /* Set up our transmit descriptors */
+ for (j = 0; j < nsegs; j++) {
+ bus_size_t seg_len;
+ bus_addr_t seg_addr;
+ /* If adapter is 82544 and on PCIX bus */
+ if(adapter->pcix_82544) {
+ DESC_ARRAY desc_array;
+ u32 array_elements, counter;
+ /*
+ * Check the Address and Length combination and
+ * split the data accordingly
+ */
+ array_elements = lem_fill_descriptors(segs[j].ds_addr,
+ segs[j].ds_len, &desc_array);
+ for (counter = 0; counter < array_elements; counter++) {
+ if (txd_used == adapter->num_tx_desc_avail) {
+ adapter->next_avail_tx_desc = txd_saved;
+ adapter->no_tx_desc_avail2++;
+ bus_dmamap_unload(adapter->txtag, map);
+ return (ENOBUFS);
+ }
+ tx_buffer = &adapter->tx_buffer_area[i];
+ ctxd = &adapter->tx_desc_base[i];
+ ctxd->buffer_addr = htole64(
+ desc_array.descriptor[counter].address);
+ ctxd->lower.data = htole32(
+ (adapter->txd_cmd | txd_lower | (u16)
+ desc_array.descriptor[counter].length));
+ ctxd->upper.data =
+ htole32((txd_upper));
+ last = i;
+ if (++i == adapter->num_tx_desc)
+ i = 0;
+ tx_buffer->m_head = NULL;
+ tx_buffer->next_eop = -1;
+ txd_used++;
+ }
+ } else {
+ tx_buffer = &adapter->tx_buffer_area[i];
+ ctxd = &adapter->tx_desc_base[i];
+ seg_addr = segs[j].ds_addr;
+ seg_len = segs[j].ds_len;
+ ctxd->buffer_addr = htole64(seg_addr);
+ ctxd->lower.data = htole32(
+ adapter->txd_cmd | txd_lower | seg_len);
+ ctxd->upper.data =
+ htole32(txd_upper);
+ last = i;
+ if (++i == adapter->num_tx_desc)
+ i = 0;
+ tx_buffer->m_head = NULL;
+ tx_buffer->next_eop = -1;
+ }
+ }
+
+ adapter->next_avail_tx_desc = i;
+
+ if (adapter->pcix_82544)
+ adapter->num_tx_desc_avail -= txd_used;
+ else
+ adapter->num_tx_desc_avail -= nsegs;
+
+ /*
+ ** Handle VLAN tag, this is the
+ ** biggest difference between
+ ** 6.x and 7
+ */
+#if __FreeBSD_version < 700000
+ /* Find out if we are in vlan mode. */
+ mtag = VLAN_OUTPUT_TAG(ifp, m_head);
+ if (mtag != NULL) {
+ ctxd->upper.fields.special =
+ htole16(VLAN_TAG_VALUE(mtag));
+#else /* FreeBSD 7 */
+ if (m_head->m_flags & M_VLANTAG) {
+ /* Set the vlan id. */
+ ctxd->upper.fields.special =
+ htole16(m_head->m_pkthdr.ether_vtag);
+#endif
+ /* Tell hardware to add tag */
+ ctxd->lower.data |= htole32(E1000_TXD_CMD_VLE);
+ }
+
+ tx_buffer->m_head = m_head;
+ tx_buffer_mapped->map = tx_buffer->map;
+ tx_buffer->map = map;
+ bus_dmamap_sync(adapter->txtag, map, BUS_DMASYNC_PREWRITE);
+
+ /*
+ * Last Descriptor of Packet
+ * needs End Of Packet (EOP)
+ * and Report Status (RS)
+ */
+ ctxd->lower.data |=
+ htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS);
+ /*
+ * Keep track in the first buffer which
+ * descriptor will be written back
+ */
+ tx_buffer = &adapter->tx_buffer_area[first];
+ tx_buffer->next_eop = last;
+
+ /*
+ * Advance the Transmit Descriptor Tail (TDT), this tells the E1000
+ * that this frame is available to transmit.
+ */
+ bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ if (adapter->hw.mac.type == e1000_82547 &&
+ adapter->link_duplex == HALF_DUPLEX)
+ lem_82547_move_tail(adapter);
+ else {
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), i);
+ if (adapter->hw.mac.type == e1000_82547)
+ lem_82547_update_fifo_head(adapter,
+ m_head->m_pkthdr.len);
+ }
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * 82547 workaround to avoid controller hang in half-duplex environment.
+ * The workaround is to avoid queuing a large packet that would span
+ * the internal Tx FIFO ring boundary. We need to reset the FIFO pointers
+ * in this case. We do that only when FIFO is quiescent.
+ *
+ **********************************************************************/
+static void
+lem_82547_move_tail(void *arg)
+{
+ struct adapter *adapter = arg;
+ struct e1000_tx_desc *tx_desc;
+ u16 hw_tdt, sw_tdt, length = 0;
+ bool eop = 0;
+
+ EM_TX_LOCK_ASSERT(adapter);
+
+ hw_tdt = E1000_READ_REG(&adapter->hw, E1000_TDT(0));
+ sw_tdt = adapter->next_avail_tx_desc;
+
+ while (hw_tdt != sw_tdt) {
+ tx_desc = &adapter->tx_desc_base[hw_tdt];
+ length += tx_desc->lower.flags.length;
+ eop = tx_desc->lower.data & E1000_TXD_CMD_EOP;
+ if (++hw_tdt == adapter->num_tx_desc)
+ hw_tdt = 0;
+
+ if (eop) {
+ if (lem_82547_fifo_workaround(adapter, length)) {
+ adapter->tx_fifo_wrk_cnt++;
+ callout_reset(&adapter->tx_fifo_timer, 1,
+ lem_82547_move_tail, adapter);
+ break;
+ }
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), hw_tdt);
+ lem_82547_update_fifo_head(adapter, length);
+ length = 0;
+ }
+ }
+}
+
+static int
+lem_82547_fifo_workaround(struct adapter *adapter, int len)
+{
+ int fifo_space, fifo_pkt_len;
+
+ fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR);
+
+ if (adapter->link_duplex == HALF_DUPLEX) {
+ fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head;
+
+ if (fifo_pkt_len >= (EM_82547_PKT_THRESH + fifo_space)) {
+ if (lem_82547_tx_fifo_reset(adapter))
+ return (0);
+ else
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+lem_82547_update_fifo_head(struct adapter *adapter, int len)
+{
+ int fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR);
+
+ /* tx_fifo_head is always 16 byte aligned */
+ adapter->tx_fifo_head += fifo_pkt_len;
+ if (adapter->tx_fifo_head >= adapter->tx_fifo_size) {
+ adapter->tx_fifo_head -= adapter->tx_fifo_size;
+ }
+}
+
+
+static int
+lem_82547_tx_fifo_reset(struct adapter *adapter)
+{
+ u32 tctl;
+
+ if ((E1000_READ_REG(&adapter->hw, E1000_TDT(0)) ==
+ E1000_READ_REG(&adapter->hw, E1000_TDH(0))) &&
+ (E1000_READ_REG(&adapter->hw, E1000_TDFT) ==
+ E1000_READ_REG(&adapter->hw, E1000_TDFH)) &&
+ (E1000_READ_REG(&adapter->hw, E1000_TDFTS) ==
+ E1000_READ_REG(&adapter->hw, E1000_TDFHS)) &&
+ (E1000_READ_REG(&adapter->hw, E1000_TDFPC) == 0)) {
+ /* Disable TX unit */
+ tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL);
+ E1000_WRITE_REG(&adapter->hw, E1000_TCTL,
+ tctl & ~E1000_TCTL_EN);
+
+ /* Reset FIFO pointers */
+ E1000_WRITE_REG(&adapter->hw, E1000_TDFT,
+ adapter->tx_head_addr);
+ E1000_WRITE_REG(&adapter->hw, E1000_TDFH,
+ adapter->tx_head_addr);
+ E1000_WRITE_REG(&adapter->hw, E1000_TDFTS,
+ adapter->tx_head_addr);
+ E1000_WRITE_REG(&adapter->hw, E1000_TDFHS,
+ adapter->tx_head_addr);
+
+ /* Re-enable TX unit */
+ E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
+ E1000_WRITE_FLUSH(&adapter->hw);
+
+ adapter->tx_fifo_head = 0;
+ adapter->tx_fifo_reset_cnt++;
+
+ return (TRUE);
+ }
+ else {
+ return (FALSE);
+ }
+}
+
+static void
+lem_set_promisc(struct adapter *adapter)
+{
+ struct ifnet *ifp = adapter->ifp;
+ u32 reg_rctl;
+
+ reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
+ /* Turn this on if you want to see bad packets */
+ if (lem_debug_sbp)
+ reg_rctl |= E1000_RCTL_SBP;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+ } else if (ifp->if_flags & IFF_ALLMULTI) {
+ reg_rctl |= E1000_RCTL_MPE;
+ reg_rctl &= ~E1000_RCTL_UPE;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+ }
+}
+
+static void
+lem_disable_promisc(struct adapter *adapter)
+{
+ u32 reg_rctl;
+
+ reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+
+ reg_rctl &= (~E1000_RCTL_UPE);
+ reg_rctl &= (~E1000_RCTL_MPE);
+ reg_rctl &= (~E1000_RCTL_SBP);
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+}
+
+
+/*********************************************************************
+ * Multicast Update
+ *
+ * This routine is called whenever multicast address list is updated.
+ *
+ **********************************************************************/
+
+static void
+lem_set_multi(struct adapter *adapter)
+{
+ struct ifnet *ifp = adapter->ifp;
+ struct ifmultiaddr *ifma;
+ u32 reg_rctl = 0;
+ u8 *mta; /* Multicast array memory */
+ int mcnt = 0;
+
+ IOCTL_DEBUGOUT("lem_set_multi: begin");
+
+ if (adapter->hw.mac.type == e1000_82542 &&
+ adapter->hw.revision_id == E1000_REVISION_2) {
+ reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ if (adapter->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+ e1000_pci_clear_mwi(&adapter->hw);
+ reg_rctl |= E1000_RCTL_RST;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+ msec_delay(5);
+ }
+
+ /* Allocate temporary memory to setup array */
+ mta = malloc(sizeof(u8) *
+ (ETH_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (mta == NULL)
+ panic("lem_set_multi memory failure\n");
+
+#if __FreeBSD_version < 800000
+ IF_ADDR_LOCK(ifp);
+#else
+ if_maddr_rlock(ifp);
+#endif
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ if (mcnt == MAX_NUM_MULTICAST_ADDRESSES)
+ break;
+
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ &mta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN);
+ mcnt++;
+ }
+#if __FreeBSD_version < 800000
+ IF_ADDR_UNLOCK(ifp);
+#else
+ if_maddr_runlock(ifp);
+#endif
+ if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) {
+ reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ reg_rctl |= E1000_RCTL_MPE;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+ } else
+ e1000_update_mc_addr_list(&adapter->hw, mta, mcnt);
+
+ if (adapter->hw.mac.type == e1000_82542 &&
+ adapter->hw.revision_id == E1000_REVISION_2) {
+ reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ reg_rctl &= ~E1000_RCTL_RST;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl);
+ msec_delay(5);
+ if (adapter->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+ e1000_pci_set_mwi(&adapter->hw);
+ }
+ free(mta, M_DEVBUF);
+}
+
+
+/*********************************************************************
+ * Timer routine
+ *
+ * This routine checks for link status and updates statistics.
+ *
+ **********************************************************************/
+
+static void
+lem_local_timer(void *arg)
+{
+ struct adapter *adapter = arg;
+ struct ifnet *ifp = adapter->ifp;
+
+ EM_CORE_LOCK_ASSERT(adapter);
+
+ taskqueue_enqueue(adapter->tq,
+ &adapter->rxtx_task);
+ lem_update_link_status(adapter);
+ lem_update_stats_counters(adapter);
+
+ if (lem_display_debug_stats && ifp->if_drv_flags & IFF_DRV_RUNNING)
+ lem_print_hw_stats(adapter);
+
+ lem_smartspeed(adapter);
+
+ /*
+ * We check the watchdog: the time since
+ * the last TX descriptor was cleaned.
+ * This implies a functional TX engine.
+ */
+ if ((adapter->watchdog_check == TRUE) &&
+ (ticks - adapter->watchdog_time > EM_WATCHDOG))
+ goto hung;
+
+ callout_reset(&adapter->timer, hz, lem_local_timer, adapter);
+ return;
+hung:
+ device_printf(adapter->dev, "Watchdog timeout -- resetting\n");
+ adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ adapter->watchdog_events++;
+ lem_init_locked(adapter);
+}
+
+static void
+lem_update_link_status(struct adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ struct ifnet *ifp = adapter->ifp;
+ device_t dev = adapter->dev;
+ u32 link_check = 0;
+
+ /* Get the cached link value or read phy for real */
+ switch (hw->phy.media_type) {
+ case e1000_media_type_copper:
+ if (hw->mac.get_link_status) {
+ /* Do the work to read phy */
+ e1000_check_for_link(hw);
+ link_check = !hw->mac.get_link_status;
+ if (link_check) /* ESB2 fix */
+ e1000_cfg_on_link_up(hw);
+ } else
+ link_check = TRUE;
+ break;
+ case e1000_media_type_fiber:
+ e1000_check_for_link(hw);
+ link_check = (E1000_READ_REG(hw, E1000_STATUS) &
+ E1000_STATUS_LU);
+ break;
+ case e1000_media_type_internal_serdes:
+ e1000_check_for_link(hw);
+ link_check = adapter->hw.mac.serdes_has_link;
+ break;
+ default:
+ case e1000_media_type_unknown:
+ break;
+ }
+
+ /* Now check for a transition */
+ if (link_check && (adapter->link_active == 0)) {
+ e1000_get_speed_and_duplex(hw, &adapter->link_speed,
+ &adapter->link_duplex);
+ if (bootverbose)
+ device_printf(dev, "Link is up %d Mbps %s\n",
+ adapter->link_speed,
+ ((adapter->link_duplex == FULL_DUPLEX) ?
+ "Full Duplex" : "Half Duplex"));
+ adapter->link_active = 1;
+ adapter->smartspeed = 0;
+ ifp->if_baudrate = adapter->link_speed * 1000000;
+ if_link_state_change(ifp, LINK_STATE_UP);
+ } else if (!link_check && (adapter->link_active == 1)) {
+ ifp->if_baudrate = adapter->link_speed = 0;
+ adapter->link_duplex = 0;
+ if (bootverbose)
+ device_printf(dev, "Link is Down\n");
+ adapter->link_active = 0;
+ /* Link down, disable watchdog */
+ adapter->watchdog_check = FALSE;
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ }
+}
+
+/*********************************************************************
+ *
+ * This routine disables all traffic on the adapter by issuing a
+ * global reset on the MAC and deallocates TX/RX buffers.
+ *
+ * This routine should always be called with BOTH the CORE
+ * and TX locks.
+ **********************************************************************/
+
+static void
+lem_stop(void *arg)
+{
+ struct adapter *adapter = arg;
+ struct ifnet *ifp = adapter->ifp;
+
+ EM_CORE_LOCK_ASSERT(adapter);
+ EM_TX_LOCK_ASSERT(adapter);
+
+ INIT_DEBUGOUT("lem_stop: begin");
+
+ lem_disable_intr(adapter);
+ callout_stop(&adapter->timer);
+ callout_stop(&adapter->tx_fifo_timer);
+
+ /* Tell the stack that the interface is no longer active */
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ e1000_reset_hw(&adapter->hw);
+ if (adapter->hw.mac.type >= e1000_82544)
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0);
+
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
+}
+
+
+/*********************************************************************
+ *
+ * Determine hardware revision.
+ *
+ **********************************************************************/
+static void
+lem_identify_hardware(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+
+ /* Make sure our PCI config space has the necessary stuff set */
+ adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (!((adapter->hw.bus.pci_cmd_word & PCIM_CMD_BUSMASTEREN) &&
+ (adapter->hw.bus.pci_cmd_word & PCIM_CMD_MEMEN))) {
+ device_printf(dev, "Memory Access and/or Bus Master bits "
+ "were not set!\n");
+ adapter->hw.bus.pci_cmd_word |=
+ (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN);
+ pci_write_config(dev, PCIR_COMMAND,
+ adapter->hw.bus.pci_cmd_word, 2);
+ }
+
+ /* Save off the information about this board */
+ adapter->hw.vendor_id = pci_get_vendor(dev);
+ adapter->hw.device_id = pci_get_device(dev);
+ adapter->hw.revision_id = pci_read_config(dev, PCIR_REVID, 1);
+ adapter->hw.subsystem_vendor_id =
+ pci_read_config(dev, PCIR_SUBVEND_0, 2);
+ adapter->hw.subsystem_device_id =
+ pci_read_config(dev, PCIR_SUBDEV_0, 2);
+
+ /* Do Shared Code Init and Setup */
+ if (e1000_set_mac_type(&adapter->hw)) {
+ device_printf(dev, "Setup init failure\n");
+ return;
+ }
+}
+
+static int
+lem_allocate_pci_resources(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ int val, rid, error = E1000_SUCCESS;
+
+ rid = PCIR_BAR(0);
+ adapter->memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (adapter->memory == NULL) {
+ device_printf(dev, "Unable to allocate bus resource: memory\n");
+ return (ENXIO);
+ }
+ adapter->osdep.mem_bus_space_tag =
+ rman_get_bustag(adapter->memory);
+ adapter->osdep.mem_bus_space_handle =
+ rman_get_bushandle(adapter->memory);
+ adapter->hw.hw_addr = (u8 *)&adapter->osdep.mem_bus_space_handle;
+
+ /* Only older adapters use IO mapping */
+ if (adapter->hw.mac.type > e1000_82543) {
+ /* Figure our where our IO BAR is ? */
+ for (rid = PCIR_BAR(0); rid < PCIR_CIS;) {
+ val = pci_read_config(dev, rid, 4);
+ if (EM_BAR_TYPE(val) == EM_BAR_TYPE_IO) {
+ adapter->io_rid = rid;
+ break;
+ }
+ rid += 4;
+ /* check for 64bit BAR */
+ if (EM_BAR_MEM_TYPE(val) == EM_BAR_MEM_TYPE_64BIT)
+ rid += 4;
+ }
+ if (rid >= PCIR_CIS) {
+ device_printf(dev, "Unable to locate IO BAR\n");
+ return (ENXIO);
+ }
+ adapter->ioport = bus_alloc_resource_any(dev,
+ SYS_RES_IOPORT, &adapter->io_rid, RF_ACTIVE);
+ if (adapter->ioport == NULL) {
+ device_printf(dev, "Unable to allocate bus resource: "
+ "ioport\n");
+ return (ENXIO);
+ }
+ adapter->hw.io_base = 0;
+ adapter->osdep.io_bus_space_tag =
+ rman_get_bustag(adapter->ioport);
+ adapter->osdep.io_bus_space_handle =
+ rman_get_bushandle(adapter->ioport);
+ }
+
+ adapter->hw.back = &adapter->osdep;
+
+ return (error);
+}
+
+/*********************************************************************
+ *
+ * Setup the Legacy or MSI Interrupt handler
+ *
+ **********************************************************************/
+int
+lem_allocate_irq(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ int error, rid = 0;
+
+ /* Manually turn off all interrupts */
+ E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff);
+
+ /* We allocate a single interrupt resource */
+ adapter->res[0] = bus_alloc_resource_any(dev,
+ SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE);
+ if (adapter->res[0] == NULL) {
+ device_printf(dev, "Unable to allocate bus resource: "
+ "interrupt\n");
+ return (ENXIO);
+ }
+
+#ifdef EM_LEGACY_IRQ
+ /* We do Legacy setup */
+ if ((error = bus_setup_intr(dev, adapter->res[0],
+#if __FreeBSD_version > 700000
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, lem_intr, adapter,
+#else /* 6.X */
+ INTR_TYPE_NET | INTR_MPSAFE, lem_intr, adapter,
+#endif
+ &adapter->tag[0])) != 0) {
+ device_printf(dev, "Failed to register interrupt handler");
+ return (error);
+ }
+
+#else /* FAST_IRQ */
+ /*
+ * Try allocating a fast interrupt and the associated deferred
+ * processing contexts.
+ */
+ TASK_INIT(&adapter->rxtx_task, 0, lem_handle_rxtx, adapter);
+ TASK_INIT(&adapter->link_task, 0, lem_handle_link, adapter);
+ adapter->tq = taskqueue_create_fast("lem_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &adapter->tq);
+ taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(adapter->dev));
+#if __FreeBSD_version < 700000
+ if ((error = bus_setup_intr(dev, adapter->res[0],
+ INTR_TYPE_NET | INTR_FAST, lem_irq_fast, adapter,
+#else
+ if ((error = bus_setup_intr(dev, adapter->res[0],
+ INTR_TYPE_NET, lem_irq_fast, NULL, adapter,
+#endif
+ &adapter->tag[0])) != 0) {
+ device_printf(dev, "Failed to register fast interrupt "
+ "handler: %d\n", error);
+ taskqueue_free(adapter->tq);
+ adapter->tq = NULL;
+ return (error);
+ }
+#endif /* EM_LEGACY_IRQ */
+
+ return (0);
+}
+
+
+static void
+lem_free_pci_resources(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+
+
+ if (adapter->tag[0] != NULL) {
+ bus_teardown_intr(dev, adapter->res[0],
+ adapter->tag[0]);
+ adapter->tag[0] = NULL;
+ }
+
+ if (adapter->res[0] != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ 0, adapter->res[0]);
+ }
+
+ if (adapter->memory != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ PCIR_BAR(0), adapter->memory);
+
+ if (adapter->ioport != NULL)
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ adapter->io_rid, adapter->ioport);
+}
+
+
+/*********************************************************************
+ *
+ * Initialize the hardware to a configuration
+ * as specified by the adapter structure.
+ *
+ **********************************************************************/
+static int
+lem_hardware_init(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ u16 rx_buffer_size;
+
+ INIT_DEBUGOUT("lem_hardware_init: begin");
+
+ /* Issue a global reset */
+ e1000_reset_hw(&adapter->hw);
+
+ /* When hardware is reset, fifo_head is also reset */
+ adapter->tx_fifo_head = 0;
+
+ /*
+ * These parameters control the automatic generation (Tx) and
+ * response (Rx) to Ethernet PAUSE frames.
+ * - High water mark should allow for at least two frames to be
+ * received after sending an XOFF.
+ * - Low water mark works best when it is very near the high water mark.
+ * This allows the receiver to restart by sending XON when it has
+ * drained a bit. Here we use an arbitary value of 1500 which will
+ * restart after one full frame is pulled from the buffer. There
+ * could be several smaller frames in the buffer and if so they will
+ * not trigger the XON until their total number reduces the buffer
+ * by 1500.
+ * - The pause time is fairly large at 1000 x 512ns = 512 usec.
+ */
+ rx_buffer_size = ((E1000_READ_REG(&adapter->hw, E1000_PBA) &
+ 0xffff) << 10 );
+
+ adapter->hw.fc.high_water = rx_buffer_size -
+ roundup2(adapter->max_frame_size, 1024);
+ adapter->hw.fc.low_water = adapter->hw.fc.high_water - 1500;
+
+ adapter->hw.fc.pause_time = EM_FC_PAUSE_TIME;
+ adapter->hw.fc.send_xon = TRUE;
+
+ /* Set Flow control, use the tunable location if sane */
+ if ((lem_fc_setting >= 0) || (lem_fc_setting < 4))
+ adapter->hw.fc.requested_mode = lem_fc_setting;
+ else
+ adapter->hw.fc.requested_mode = e1000_fc_none;
+
+ if (e1000_init_hw(&adapter->hw) < 0) {
+ device_printf(dev, "Hardware Initialization Failed\n");
+ return (EIO);
+ }
+
+ e1000_check_for_link(&adapter->hw);
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Setup networking device structure and register an interface.
+ *
+ **********************************************************************/
+static void
+lem_setup_interface(device_t dev, struct adapter *adapter)
+{
+ struct ifnet *ifp;
+
+ INIT_DEBUGOUT("lem_setup_interface: begin");
+
+ ifp = adapter->ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL)
+ panic("%s: can not if_alloc()", device_get_nameunit(dev));
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_init = lem_init;
+ ifp->if_softc = adapter;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = lem_ioctl;
+ ifp->if_start = lem_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1);
+ ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 1;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ether_ifattach(ifp, adapter->hw.mac.addr);
+
+ ifp->if_capabilities = ifp->if_capenable = 0;
+
+#if __FreeBSD_version >= 800000
+ /* Multiqueue tx functions */
+ ifp->if_transmit = lem_mq_start;
+ ifp->if_qflush = lem_qflush;
+ adapter->br = buf_ring_alloc(4096, M_DEVBUF, M_WAITOK, &adapter->tx_mtx);
+#endif
+ if (adapter->hw.mac.type >= e1000_82543) {
+ int version_cap;
+#if __FreeBSD_version < 700000
+ version_cap = IFCAP_HWCSUM;
+#else
+ version_cap = IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM;
+#endif
+ ifp->if_capabilities |= version_cap;
+ ifp->if_capenable |= version_cap;
+ }
+
+ /*
+ * Tell the upper layer(s) we support long frames.
+ */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
+ ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
+
+#ifdef DEVICE_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+
+ /* Enable All WOL methods by default */
+ if (adapter->wol) {
+ ifp->if_capabilities |= IFCAP_WOL;
+ ifp->if_capenable |= IFCAP_WOL;
+ }
+
+ /*
+ * Specify the media types supported by this adapter and register
+ * callbacks to update media and link information
+ */
+ ifmedia_init(&adapter->media, IFM_IMASK,
+ lem_media_change, lem_media_status);
+ if ((adapter->hw.phy.media_type == e1000_media_type_fiber) ||
+ (adapter->hw.phy.media_type == e1000_media_type_internal_serdes)) {
+ u_char fiber_type = IFM_1000_SX; /* default type */
+
+ if (adapter->hw.mac.type == e1000_82545)
+ fiber_type = IFM_1000_LX;
+ ifmedia_add(&adapter->media, IFM_ETHER | fiber_type | IFM_FDX,
+ 0, NULL);
+ ifmedia_add(&adapter->media, IFM_ETHER | fiber_type, 0, NULL);
+ } else {
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_10_T, 0, NULL);
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_10_T | IFM_FDX,
+ 0, NULL);
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_100_TX,
+ 0, NULL);
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_100_TX | IFM_FDX,
+ 0, NULL);
+ if (adapter->hw.phy.type != e1000_phy_ife) {
+ ifmedia_add(&adapter->media,
+ IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&adapter->media,
+ IFM_ETHER | IFM_1000_T, 0, NULL);
+ }
+ }
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO);
+}
+
+
+/*********************************************************************
+ *
+ * Workaround for SmartSpeed on 82541 and 82547 controllers
+ *
+ **********************************************************************/
+static void
+lem_smartspeed(struct adapter *adapter)
+{
+ u16 phy_tmp;
+
+ if (adapter->link_active || (adapter->hw.phy.type != e1000_phy_igp) ||
+ adapter->hw.mac.autoneg == 0 ||
+ (adapter->hw.phy.autoneg_advertised & ADVERTISE_1000_FULL) == 0)
+ return;
+
+ if (adapter->smartspeed == 0) {
+ /* If Master/Slave config fault is asserted twice,
+ * we assume back-to-back */
+ e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp);
+ if (!(phy_tmp & SR_1000T_MS_CONFIG_FAULT))
+ return;
+ e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp);
+ if (phy_tmp & SR_1000T_MS_CONFIG_FAULT) {
+ e1000_read_phy_reg(&adapter->hw,
+ PHY_1000T_CTRL, &phy_tmp);
+ if(phy_tmp & CR_1000T_MS_ENABLE) {
+ phy_tmp &= ~CR_1000T_MS_ENABLE;
+ e1000_write_phy_reg(&adapter->hw,
+ PHY_1000T_CTRL, phy_tmp);
+ adapter->smartspeed++;
+ if(adapter->hw.mac.autoneg &&
+ !e1000_copper_link_autoneg(&adapter->hw) &&
+ !e1000_read_phy_reg(&adapter->hw,
+ PHY_CONTROL, &phy_tmp)) {
+ phy_tmp |= (MII_CR_AUTO_NEG_EN |
+ MII_CR_RESTART_AUTO_NEG);
+ e1000_write_phy_reg(&adapter->hw,
+ PHY_CONTROL, phy_tmp);
+ }
+ }
+ }
+ return;
+ } else if(adapter->smartspeed == EM_SMARTSPEED_DOWNSHIFT) {
+ /* If still no link, perhaps using 2/3 pair cable */
+ e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp);
+ phy_tmp |= CR_1000T_MS_ENABLE;
+ e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_tmp);
+ if(adapter->hw.mac.autoneg &&
+ !e1000_copper_link_autoneg(&adapter->hw) &&
+ !e1000_read_phy_reg(&adapter->hw, PHY_CONTROL, &phy_tmp)) {
+ phy_tmp |= (MII_CR_AUTO_NEG_EN |
+ MII_CR_RESTART_AUTO_NEG);
+ e1000_write_phy_reg(&adapter->hw, PHY_CONTROL, phy_tmp);
+ }
+ }
+ /* Restart process after EM_SMARTSPEED_MAX iterations */
+ if(adapter->smartspeed++ == EM_SMARTSPEED_MAX)
+ adapter->smartspeed = 0;
+}
+
+
+/*
+ * Manage DMA'able memory.
+ */
+static void
+lem_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ if (error)
+ return;
+ *(bus_addr_t *) arg = segs[0].ds_addr;
+}
+
+static int
+lem_dma_malloc(struct adapter *adapter, bus_size_t size,
+ struct em_dma_alloc *dma, int mapflags)
+{
+ int error;
+
+#if __FreeBSD_version >= 700000
+ error = bus_dma_tag_create(bus_get_dma_tag(adapter->dev), /* parent */
+#else
+ error = bus_dma_tag_create(NULL, /* parent */
+#endif
+ EM_DBA_ALIGN, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ size, /* maxsize */
+ 1, /* nsegments */
+ size, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &dma->dma_tag);
+ if (error) {
+ device_printf(adapter->dev,
+ "%s: bus_dma_tag_create failed: %d\n",
+ __func__, error);
+ goto fail_0;
+ }
+
+ error = bus_dmamem_alloc(dma->dma_tag, (void**) &dma->dma_vaddr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map);
+ if (error) {
+ device_printf(adapter->dev,
+ "%s: bus_dmamem_alloc(%ju) failed: %d\n",
+ __func__, (uintmax_t)size, error);
+ goto fail_2;
+ }
+
+ dma->dma_paddr = 0;
+ error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr,
+ size, lem_dmamap_cb, &dma->dma_paddr, mapflags | BUS_DMA_NOWAIT);
+ if (error || dma->dma_paddr == 0) {
+ device_printf(adapter->dev,
+ "%s: bus_dmamap_load failed: %d\n",
+ __func__, error);
+ goto fail_3;
+ }
+
+ return (0);
+
+fail_3:
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+fail_2:
+ bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+ bus_dma_tag_destroy(dma->dma_tag);
+fail_0:
+ dma->dma_map = NULL;
+ dma->dma_tag = NULL;
+
+ return (error);
+}
+
+static void
+lem_dma_free(struct adapter *adapter, struct em_dma_alloc *dma)
+{
+ if (dma->dma_tag == NULL)
+ return;
+ if (dma->dma_map != NULL) {
+ bus_dmamap_sync(dma->dma_tag, dma->dma_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+ bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+ dma->dma_map = NULL;
+ }
+ bus_dma_tag_destroy(dma->dma_tag);
+ dma->dma_tag = NULL;
+}
+
+
+/*********************************************************************
+ *
+ * Allocate memory for tx_buffer structures. The tx_buffer stores all
+ * the information needed to transmit a packet on the wire.
+ *
+ **********************************************************************/
+static int
+lem_allocate_transmit_structures(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ struct em_buffer *tx_buffer;
+ int error;
+
+ /*
+ * Create DMA tags for tx descriptors
+ */
+#if __FreeBSD_version >= 700000
+ if ((error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+#else
+ if ((error = bus_dma_tag_create(NULL, /* parent */
+#endif
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ EM_TSO_SIZE, /* maxsize */
+ EM_MAX_SCATTER, /* nsegments */
+ EM_TSO_SEG_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &adapter->txtag)) != 0) {
+ device_printf(dev, "Unable to allocate TX DMA tag\n");
+ goto fail;
+ }
+
+ adapter->tx_buffer_area = malloc(sizeof(struct em_buffer) *
+ adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (adapter->tx_buffer_area == NULL) {
+ device_printf(dev, "Unable to allocate tx_buffer memory\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ /* Create the descriptor buffer dma maps */
+ for (int i = 0; i < adapter->num_tx_desc; i++) {
+ tx_buffer = &adapter->tx_buffer_area[i];
+ error = bus_dmamap_create(adapter->txtag, 0, &tx_buffer->map);
+ if (error != 0) {
+ device_printf(dev, "Unable to create TX DMA map\n");
+ goto fail;
+ }
+ tx_buffer->next_eop = -1;
+ }
+
+ return (0);
+fail:
+ lem_free_transmit_structures(adapter);
+ return (error);
+}
+
+/*********************************************************************
+ *
+ * (Re)Initialize transmit structures.
+ *
+ **********************************************************************/
+static void
+lem_setup_transmit_structures(struct adapter *adapter)
+{
+ struct em_buffer *tx_buffer;
+
+ /* Clear the old ring contents */
+ bzero(adapter->tx_desc_base,
+ (sizeof(struct e1000_tx_desc)) * adapter->num_tx_desc);
+
+ /* Free any existing TX buffers */
+ for (int i = 0; i < adapter->num_tx_desc; i++, tx_buffer++) {
+ tx_buffer = &adapter->tx_buffer_area[i];
+ bus_dmamap_sync(adapter->txtag, tx_buffer->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(adapter->txtag, tx_buffer->map);
+ m_freem(tx_buffer->m_head);
+ tx_buffer->m_head = NULL;
+ tx_buffer->next_eop = -1;
+ }
+
+ /* Reset state */
+ adapter->next_avail_tx_desc = 0;
+ adapter->next_tx_to_clean = 0;
+ adapter->num_tx_desc_avail = adapter->num_tx_desc;
+
+ bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ return;
+}
+
+/*********************************************************************
+ *
+ * Enable transmit unit.
+ *
+ **********************************************************************/
+static void
+lem_initialize_transmit_unit(struct adapter *adapter)
+{
+ u32 tctl, tipg = 0;
+ u64 bus_addr;
+
+ INIT_DEBUGOUT("lem_initialize_transmit_unit: begin");
+ /* Setup the Base and Length of the Tx Descriptor Ring */
+ bus_addr = adapter->txdma.dma_paddr;
+ E1000_WRITE_REG(&adapter->hw, E1000_TDLEN(0),
+ adapter->num_tx_desc * sizeof(struct e1000_tx_desc));
+ E1000_WRITE_REG(&adapter->hw, E1000_TDBAH(0),
+ (u32)(bus_addr >> 32));
+ E1000_WRITE_REG(&adapter->hw, E1000_TDBAL(0),
+ (u32)bus_addr);
+ /* Setup the HW Tx Head and Tail descriptor pointers */
+ E1000_WRITE_REG(&adapter->hw, E1000_TDT(0), 0);
+ E1000_WRITE_REG(&adapter->hw, E1000_TDH(0), 0);
+
+ HW_DEBUGOUT2("Base = %x, Length = %x\n",
+ E1000_READ_REG(&adapter->hw, E1000_TDBAL(0)),
+ E1000_READ_REG(&adapter->hw, E1000_TDLEN(0)));
+
+ /* Set the default values for the Tx Inter Packet Gap timer */
+ switch (adapter->hw.mac.type) {
+ case e1000_82542:
+ tipg = DEFAULT_82542_TIPG_IPGT;
+ tipg |= DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT;
+ tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
+ break;
+ default:
+ if ((adapter->hw.phy.media_type == e1000_media_type_fiber) ||
+ (adapter->hw.phy.media_type ==
+ e1000_media_type_internal_serdes))
+ tipg = DEFAULT_82543_TIPG_IPGT_FIBER;
+ else
+ tipg = DEFAULT_82543_TIPG_IPGT_COPPER;
+ tipg |= DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT;
+ tipg |= DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
+ }
+
+ E1000_WRITE_REG(&adapter->hw, E1000_TIPG, tipg);
+ E1000_WRITE_REG(&adapter->hw, E1000_TIDV, adapter->tx_int_delay.value);
+ if(adapter->hw.mac.type >= e1000_82540)
+ E1000_WRITE_REG(&adapter->hw, E1000_TADV,
+ adapter->tx_abs_int_delay.value);
+
+ /* Program the Transmit Control Register */
+ tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL);
+ tctl &= ~E1000_TCTL_CT;
+ tctl |= (E1000_TCTL_PSP | E1000_TCTL_RTLC | E1000_TCTL_EN |
+ (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT));
+
+ /* This write will effectively turn on the transmit unit. */
+ E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl);
+
+ /* Setup Transmit Descriptor Base Settings */
+ adapter->txd_cmd = E1000_TXD_CMD_IFCS;
+
+ if (adapter->tx_int_delay.value > 0)
+ adapter->txd_cmd |= E1000_TXD_CMD_IDE;
+}
+
+/*********************************************************************
+ *
+ * Free all transmit related data structures.
+ *
+ **********************************************************************/
+static void
+lem_free_transmit_structures(struct adapter *adapter)
+{
+ struct em_buffer *tx_buffer;
+
+ INIT_DEBUGOUT("free_transmit_structures: begin");
+
+ if (adapter->tx_buffer_area != NULL) {
+ for (int i = 0; i < adapter->num_tx_desc; i++) {
+ tx_buffer = &adapter->tx_buffer_area[i];
+ if (tx_buffer->m_head != NULL) {
+ bus_dmamap_sync(adapter->txtag, tx_buffer->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(adapter->txtag,
+ tx_buffer->map);
+ m_freem(tx_buffer->m_head);
+ tx_buffer->m_head = NULL;
+ } else if (tx_buffer->map != NULL)
+ bus_dmamap_unload(adapter->txtag,
+ tx_buffer->map);
+ if (tx_buffer->map != NULL) {
+ bus_dmamap_destroy(adapter->txtag,
+ tx_buffer->map);
+ tx_buffer->map = NULL;
+ }
+ }
+ }
+ if (adapter->tx_buffer_area != NULL) {
+ free(adapter->tx_buffer_area, M_DEVBUF);
+ adapter->tx_buffer_area = NULL;
+ }
+ if (adapter->txtag != NULL) {
+ bus_dma_tag_destroy(adapter->txtag);
+ adapter->txtag = NULL;
+ }
+#if __FreeBSD_version >= 800000
+ if (adapter->br != NULL)
+ buf_ring_free(adapter->br, M_DEVBUF);
+#endif
+}
+
+/*********************************************************************
+ *
+ * The offload context needs to be set when we transfer the first
+ * packet of a particular protocol (TCP/UDP). This routine has been
+ * enhanced to deal with inserted VLAN headers, and IPV6 (not complete)
+ *
+ * Added back the old method of keeping the current context type
+ * and not setting if unnecessary, as this is reported to be a
+ * big performance win. -jfv
+ **********************************************************************/
+static void
+lem_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp,
+ u32 *txd_upper, u32 *txd_lower)
+{
+ struct e1000_context_desc *TXD = NULL;
+ struct em_buffer *tx_buffer;
+ struct ether_vlan_header *eh;
+ struct ip *ip = NULL;
+ struct ip6_hdr *ip6;
+ int curr_txd, ehdrlen;
+ u32 cmd, hdr_len, ip_hlen;
+ u16 etype;
+ u8 ipproto;
+
+
+ cmd = hdr_len = ipproto = 0;
+ curr_txd = adapter->next_avail_tx_desc;
+
+ /*
+ * Determine where frame payload starts.
+ * Jump over vlan headers if already present,
+ * helpful for QinQ too.
+ */
+ eh = mtod(mp, struct ether_vlan_header *);
+ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+ etype = ntohs(eh->evl_proto);
+ ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ } else {
+ etype = ntohs(eh->evl_encap_proto);
+ ehdrlen = ETHER_HDR_LEN;
+ }
+
+ /*
+ * We only support TCP/UDP for IPv4 and IPv6 for the moment.
+ * TODO: Support SCTP too when it hits the tree.
+ */
+ switch (etype) {
+ case ETHERTYPE_IP:
+ ip = (struct ip *)(mp->m_data + ehdrlen);
+ ip_hlen = ip->ip_hl << 2;
+
+ /* Setup of IP header checksum. */
+ if (mp->m_pkthdr.csum_flags & CSUM_IP) {
+ /*
+ * Start offset for header checksum calculation.
+ * End offset for header checksum calculation.
+ * Offset of place to put the checksum.
+ */
+ TXD = (struct e1000_context_desc *)
+ &adapter->tx_desc_base[curr_txd];
+ TXD->lower_setup.ip_fields.ipcss = ehdrlen;
+ TXD->lower_setup.ip_fields.ipcse =
+ htole16(ehdrlen + ip_hlen);
+ TXD->lower_setup.ip_fields.ipcso =
+ ehdrlen + offsetof(struct ip, ip_sum);
+ cmd |= E1000_TXD_CMD_IP;
+ *txd_upper |= E1000_TXD_POPTS_IXSM << 8;
+ }
+
+ if (mp->m_len < ehdrlen + ip_hlen)
+ return; /* failure */
+
+ hdr_len = ehdrlen + ip_hlen;
+ ipproto = ip->ip_p;
+
+ break;
+ case ETHERTYPE_IPV6:
+ ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
+ ip_hlen = sizeof(struct ip6_hdr); /* XXX: No header stacking. */
+
+ if (mp->m_len < ehdrlen + ip_hlen)
+ return; /* failure */
+
+ /* IPv6 doesn't have a header checksum. */
+
+ hdr_len = ehdrlen + ip_hlen;
+ ipproto = ip6->ip6_nxt;
+
+ break;
+ default:
+ *txd_upper = 0;
+ *txd_lower = 0;
+ return;
+ }
+
+ switch (ipproto) {
+ case IPPROTO_TCP:
+ if (mp->m_pkthdr.csum_flags & CSUM_TCP) {
+ *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
+ *txd_upper |= E1000_TXD_POPTS_TXSM << 8;
+ /* no need for context if already set */
+ if (adapter->last_hw_offload == CSUM_TCP)
+ return;
+ adapter->last_hw_offload = CSUM_TCP;
+ /*
+ * Start offset for payload checksum calculation.
+ * End offset for payload checksum calculation.
+ * Offset of place to put the checksum.
+ */
+ TXD = (struct e1000_context_desc *)
+ &adapter->tx_desc_base[curr_txd];
+ TXD->upper_setup.tcp_fields.tucss = hdr_len;
+ TXD->upper_setup.tcp_fields.tucse = htole16(0);
+ TXD->upper_setup.tcp_fields.tucso =
+ hdr_len + offsetof(struct tcphdr, th_sum);
+ cmd |= E1000_TXD_CMD_TCP;
+ }
+ break;
+ case IPPROTO_UDP:
+ {
+ if (mp->m_pkthdr.csum_flags & CSUM_UDP) {
+ *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
+ *txd_upper |= E1000_TXD_POPTS_TXSM << 8;
+ /* no need for context if already set */
+ if (adapter->last_hw_offload == CSUM_UDP)
+ return;
+ adapter->last_hw_offload = CSUM_UDP;
+ /*
+ * Start offset for header checksum calculation.
+ * End offset for header checksum calculation.
+ * Offset of place to put the checksum.
+ */
+ TXD = (struct e1000_context_desc *)
+ &adapter->tx_desc_base[curr_txd];
+ TXD->upper_setup.tcp_fields.tucss = hdr_len;
+ TXD->upper_setup.tcp_fields.tucse = htole16(0);
+ TXD->upper_setup.tcp_fields.tucso =
+ hdr_len + offsetof(struct udphdr, uh_sum);
+ }
+ /* Fall Thru */
+ }
+ default:
+ break;
+ }
+
+ TXD->tcp_seg_setup.data = htole32(0);
+ TXD->cmd_and_length =
+ htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT | cmd);
+ tx_buffer = &adapter->tx_buffer_area[curr_txd];
+ tx_buffer->m_head = NULL;
+ tx_buffer->next_eop = -1;
+
+ if (++curr_txd == adapter->num_tx_desc)
+ curr_txd = 0;
+
+ adapter->num_tx_desc_avail--;
+ adapter->next_avail_tx_desc = curr_txd;
+}
+
+
+/**********************************************************************
+ *
+ * Examine each tx_buffer in the used queue. If the hardware is done
+ * processing the packet then free associated resources. The
+ * tx_buffer is put back on the free queue.
+ *
+ **********************************************************************/
+static void
+lem_txeof(struct adapter *adapter)
+{
+ int first, last, done, num_avail;
+ struct em_buffer *tx_buffer;
+ struct e1000_tx_desc *tx_desc, *eop_desc;
+ struct ifnet *ifp = adapter->ifp;
+
+ EM_TX_LOCK_ASSERT(adapter);
+
+ if (adapter->num_tx_desc_avail == adapter->num_tx_desc)
+ return;
+
+ num_avail = adapter->num_tx_desc_avail;
+ first = adapter->next_tx_to_clean;
+ tx_desc = &adapter->tx_desc_base[first];
+ tx_buffer = &adapter->tx_buffer_area[first];
+ last = tx_buffer->next_eop;
+ eop_desc = &adapter->tx_desc_base[last];
+
+ /*
+ * What this does is get the index of the
+ * first descriptor AFTER the EOP of the
+ * first packet, that way we can do the
+ * simple comparison on the inner while loop.
+ */
+ if (++last == adapter->num_tx_desc)
+ last = 0;
+ done = last;
+
+ bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ BUS_DMASYNC_POSTREAD);
+
+ while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) {
+ /* We clean the range of the packet */
+ while (first != done) {
+ tx_desc->upper.data = 0;
+ tx_desc->lower.data = 0;
+ tx_desc->buffer_addr = 0;
+ ++num_avail;
+
+ if (tx_buffer->m_head) {
+ ifp->if_opackets++;
+ bus_dmamap_sync(adapter->txtag,
+ tx_buffer->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(adapter->txtag,
+ tx_buffer->map);
+
+ m_freem(tx_buffer->m_head);
+ tx_buffer->m_head = NULL;
+ }
+ tx_buffer->next_eop = -1;
+ adapter->watchdog_time = ticks;
+
+ if (++first == adapter->num_tx_desc)
+ first = 0;
+
+ tx_buffer = &adapter->tx_buffer_area[first];
+ tx_desc = &adapter->tx_desc_base[first];
+ }
+ /* See if we can continue to the next packet */
+ last = tx_buffer->next_eop;
+ if (last != -1) {
+ eop_desc = &adapter->tx_desc_base[last];
+ /* Get new done point */
+ if (++last == adapter->num_tx_desc) last = 0;
+ done = last;
+ } else
+ break;
+ }
+ bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ adapter->next_tx_to_clean = first;
+
+ /*
+ * If we have enough room, clear IFF_DRV_OACTIVE to
+ * tell the stack that it is OK to send packets.
+ * If there are no pending descriptors, clear the watchdog.
+ */
+ if (num_avail > EM_TX_CLEANUP_THRESHOLD) {
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ if (num_avail == adapter->num_tx_desc) {
+ adapter->watchdog_check = FALSE;
+ adapter->num_tx_desc_avail = num_avail;
+ return;
+ }
+ }
+
+ adapter->num_tx_desc_avail = num_avail;
+ return;
+}
+
+/*********************************************************************
+ *
+ * When Link is lost sometimes there is work still in the TX ring
+ * which may result in a watchdog, rather than allow that we do an
+ * attempted cleanup and then reinit here. Note that this has been
+ * seens mostly with fiber adapters.
+ *
+ **********************************************************************/
+static void
+lem_tx_purge(struct adapter *adapter)
+{
+ if ((!adapter->link_active) && (adapter->watchdog_check)) {
+ EM_TX_LOCK(adapter);
+ lem_txeof(adapter);
+ EM_TX_UNLOCK(adapter);
+ if (adapter->watchdog_check) /* Still outstanding? */
+ lem_init_locked(adapter);
+ }
+}
+
+/*********************************************************************
+ *
+ * Get a buffer from system mbuf buffer pool.
+ *
+ **********************************************************************/
+static int
+lem_get_buf(struct adapter *adapter, int i)
+{
+ struct mbuf *m;
+ bus_dma_segment_t segs[1];
+ bus_dmamap_t map;
+ struct em_buffer *rx_buffer;
+ int error, nsegs;
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ adapter->mbuf_cluster_failed++;
+ return (ENOBUFS);
+ }
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+
+ if (adapter->max_frame_size <= (MCLBYTES - ETHER_ALIGN))
+ m_adj(m, ETHER_ALIGN);
+
+ /*
+ * Using memory from the mbuf cluster pool, invoke the
+ * bus_dma machinery to arrange the memory mapping.
+ */
+ error = bus_dmamap_load_mbuf_sg(adapter->rxtag,
+ adapter->rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_free(m);
+ return (error);
+ }
+
+ /* If nsegs is wrong then the stack is corrupt. */
+ KASSERT(nsegs == 1, ("Too many segments returned!"));
+
+ rx_buffer = &adapter->rx_buffer_area[i];
+ if (rx_buffer->m_head != NULL)
+ bus_dmamap_unload(adapter->rxtag, rx_buffer->map);
+
+ map = rx_buffer->map;
+ rx_buffer->map = adapter->rx_sparemap;
+ adapter->rx_sparemap = map;
+ bus_dmamap_sync(adapter->rxtag, rx_buffer->map, BUS_DMASYNC_PREREAD);
+ rx_buffer->m_head = m;
+
+ adapter->rx_desc_base[i].buffer_addr = htole64(segs[0].ds_addr);
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Allocate memory for rx_buffer structures. Since we use one
+ * rx_buffer per received packet, the maximum number of rx_buffer's
+ * that we'll need is equal to the number of receive descriptors
+ * that we've allocated.
+ *
+ **********************************************************************/
+static int
+lem_allocate_receive_structures(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ struct em_buffer *rx_buffer;
+ int i, error;
+
+ adapter->rx_buffer_area = malloc(sizeof(struct em_buffer) *
+ adapter->num_rx_desc, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (adapter->rx_buffer_area == NULL) {
+ device_printf(dev, "Unable to allocate rx_buffer memory\n");
+ return (ENOMEM);
+ }
+
+#if __FreeBSD_version >= 700000
+ error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */
+#else
+ error = bus_dma_tag_create(NULL, /* parent */
+#endif
+ 1, 0, /* alignment, bounds */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, /* maxsize */
+ 1, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &adapter->rxtag);
+ if (error) {
+ device_printf(dev, "%s: bus_dma_tag_create failed %d\n",
+ __func__, error);
+ goto fail;
+ }
+
+ /* Create the spare map (used by getbuf) */
+ error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT,
+ &adapter->rx_sparemap);
+ if (error) {
+ device_printf(dev, "%s: bus_dmamap_create failed: %d\n",
+ __func__, error);
+ goto fail;
+ }
+
+ rx_buffer = adapter->rx_buffer_area;
+ for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
+ error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT,
+ &rx_buffer->map);
+ if (error) {
+ device_printf(dev, "%s: bus_dmamap_create failed: %d\n",
+ __func__, error);
+ goto fail;
+ }
+ }
+
+ return (0);
+
+fail:
+ lem_free_receive_structures(adapter);
+ return (error);
+}
+
+/*********************************************************************
+ *
+ * (Re)initialize receive structures.
+ *
+ **********************************************************************/
+static int
+lem_setup_receive_structures(struct adapter *adapter)
+{
+ struct em_buffer *rx_buffer;
+ int i, error;
+
+ /* Reset descriptor ring */
+ bzero(adapter->rx_desc_base,
+ (sizeof(struct e1000_rx_desc)) * adapter->num_rx_desc);
+
+ /* Free current RX buffers. */
+ rx_buffer = adapter->rx_buffer_area;
+ for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
+ if (rx_buffer->m_head != NULL) {
+ bus_dmamap_sync(adapter->rxtag, rx_buffer->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(adapter->rxtag, rx_buffer->map);
+ m_freem(rx_buffer->m_head);
+ rx_buffer->m_head = NULL;
+ }
+ }
+
+ /* Allocate new ones. */
+ for (i = 0; i < adapter->num_rx_desc; i++) {
+ error = lem_get_buf(adapter, i);
+ if (error)
+ return (error);
+ }
+
+ /* Setup our descriptor pointers */
+ adapter->next_rx_desc_to_check = 0;
+ bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ return (0);
+}
+
+/*********************************************************************
+ *
+ * Enable receive unit.
+ *
+ **********************************************************************/
+#define MAX_INTS_PER_SEC 8000
+#define DEFAULT_ITR 1000000000/(MAX_INTS_PER_SEC * 256)
+
+static void
+lem_initialize_receive_unit(struct adapter *adapter)
+{
+ struct ifnet *ifp = adapter->ifp;
+ u64 bus_addr;
+ u32 rctl, rxcsum;
+
+ INIT_DEBUGOUT("lem_initialize_receive_unit: begin");
+
+ /*
+ * Make sure receives are disabled while setting
+ * up the descriptor ring
+ */
+ rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl & ~E1000_RCTL_EN);
+
+ if (adapter->hw.mac.type >= e1000_82540) {
+ E1000_WRITE_REG(&adapter->hw, E1000_RADV,
+ adapter->rx_abs_int_delay.value);
+ /*
+ * Set the interrupt throttling rate. Value is calculated
+ * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns)
+ */
+ E1000_WRITE_REG(&adapter->hw, E1000_ITR, DEFAULT_ITR);
+ }
+
+ /*
+ ** When using MSIX interrupts we need to throttle
+ ** using the EITR register (82574 only)
+ */
+ if (adapter->msix)
+ for (int i = 0; i < 4; i++)
+ E1000_WRITE_REG(&adapter->hw,
+ E1000_EITR_82574(i), DEFAULT_ITR);
+
+ /* Disable accelerated ackknowledge */
+ if (adapter->hw.mac.type == e1000_82574)
+ E1000_WRITE_REG(&adapter->hw,
+ E1000_RFCTL, E1000_RFCTL_ACK_DIS);
+
+ /* Setup the Base and Length of the Rx Descriptor Ring */
+ bus_addr = adapter->rxdma.dma_paddr;
+ E1000_WRITE_REG(&adapter->hw, E1000_RDLEN(0),
+ adapter->num_rx_desc * sizeof(struct e1000_rx_desc));
+ E1000_WRITE_REG(&adapter->hw, E1000_RDBAH(0),
+ (u32)(bus_addr >> 32));
+ E1000_WRITE_REG(&adapter->hw, E1000_RDBAL(0),
+ (u32)bus_addr);
+
+ /* Setup the Receive Control Register */
+ rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
+ rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO |
+ E1000_RCTL_RDMTS_HALF |
+ (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
+
+ /* Make sure VLAN Filters are off */
+ rctl &= ~E1000_RCTL_VFE;
+
+ if (e1000_tbi_sbp_enabled_82543(&adapter->hw))
+ rctl |= E1000_RCTL_SBP;
+ else
+ rctl &= ~E1000_RCTL_SBP;
+
+ switch (adapter->rx_buffer_len) {
+ default:
+ case 2048:
+ rctl |= E1000_RCTL_SZ_2048;
+ break;
+ case 4096:
+ rctl |= E1000_RCTL_SZ_4096 |
+ E1000_RCTL_BSEX | E1000_RCTL_LPE;
+ break;
+ case 8192:
+ rctl |= E1000_RCTL_SZ_8192 |
+ E1000_RCTL_BSEX | E1000_RCTL_LPE;
+ break;
+ case 16384:
+ rctl |= E1000_RCTL_SZ_16384 |
+ E1000_RCTL_BSEX | E1000_RCTL_LPE;
+ break;
+ }
+
+ if (ifp->if_mtu > ETHERMTU)
+ rctl |= E1000_RCTL_LPE;
+ else
+ rctl &= ~E1000_RCTL_LPE;
+
+ /* Enable 82543 Receive Checksum Offload for TCP and UDP */
+ if ((adapter->hw.mac.type >= e1000_82543) &&
+ (ifp->if_capenable & IFCAP_RXCSUM)) {
+ rxcsum = E1000_READ_REG(&adapter->hw, E1000_RXCSUM);
+ rxcsum |= (E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
+ E1000_WRITE_REG(&adapter->hw, E1000_RXCSUM, rxcsum);
+ }
+
+ /* Enable Receives */
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl);
+
+ /*
+ * Setup the HW Rx Head and
+ * Tail Descriptor Pointers
+ */
+ E1000_WRITE_REG(&adapter->hw, E1000_RDH(0), 0);
+ E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), adapter->num_rx_desc - 1);
+
+ return;
+}
+
+/*********************************************************************
+ *
+ * Free receive related data structures.
+ *
+ **********************************************************************/
+static void
+lem_free_receive_structures(struct adapter *adapter)
+{
+ struct em_buffer *rx_buffer;
+ int i;
+
+ INIT_DEBUGOUT("free_receive_structures: begin");
+
+ if (adapter->rx_sparemap) {
+ bus_dmamap_destroy(adapter->rxtag, adapter->rx_sparemap);
+ adapter->rx_sparemap = NULL;
+ }
+
+ /* Cleanup any existing buffers */
+ if (adapter->rx_buffer_area != NULL) {
+ rx_buffer = adapter->rx_buffer_area;
+ for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) {
+ if (rx_buffer->m_head != NULL) {
+ bus_dmamap_sync(adapter->rxtag, rx_buffer->map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(adapter->rxtag,
+ rx_buffer->map);
+ m_freem(rx_buffer->m_head);
+ rx_buffer->m_head = NULL;
+ } else if (rx_buffer->map != NULL)
+ bus_dmamap_unload(adapter->rxtag,
+ rx_buffer->map);
+ if (rx_buffer->map != NULL) {
+ bus_dmamap_destroy(adapter->rxtag,
+ rx_buffer->map);
+ rx_buffer->map = NULL;
+ }
+ }
+ }
+
+ if (adapter->rx_buffer_area != NULL) {
+ free(adapter->rx_buffer_area, M_DEVBUF);
+ adapter->rx_buffer_area = NULL;
+ }
+
+ if (adapter->rxtag != NULL) {
+ bus_dma_tag_destroy(adapter->rxtag);
+ adapter->rxtag = NULL;
+ }
+}
+
+/*********************************************************************
+ *
+ * This routine executes in interrupt context. It replenishes
+ * the mbufs in the descriptor and sends data which has been
+ * dma'ed into host memory to upper layer.
+ *
+ * We loop at most count times if count is > 0, or until done if
+ * count < 0.
+ *
+ * For polling we also now return the number of cleaned packets
+ *********************************************************************/
+static int
+lem_rxeof(struct adapter *adapter, int count)
+{
+ struct ifnet *ifp = adapter->ifp;;
+ struct mbuf *mp;
+ u8 status, accept_frame = 0, eop = 0;
+ u16 len, desc_len, prev_len_adj;
+ int i, rx_sent = 0;
+ struct e1000_rx_desc *current_desc;
+
+ EM_RX_LOCK(adapter);
+ i = adapter->next_rx_desc_to_check;
+ current_desc = &adapter->rx_desc_base[i];
+ bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
+ BUS_DMASYNC_POSTREAD);
+
+ if (!((current_desc->status) & E1000_RXD_STAT_DD)) {
+ EM_RX_UNLOCK(adapter);
+ return (rx_sent);
+ }
+
+ while ((current_desc->status & E1000_RXD_STAT_DD) &&
+ (count != 0) &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ struct mbuf *m = NULL;
+
+ mp = adapter->rx_buffer_area[i].m_head;
+ /*
+ * Can't defer bus_dmamap_sync(9) because TBI_ACCEPT
+ * needs to access the last received byte in the mbuf.
+ */
+ bus_dmamap_sync(adapter->rxtag, adapter->rx_buffer_area[i].map,
+ BUS_DMASYNC_POSTREAD);
+
+ accept_frame = 1;
+ prev_len_adj = 0;
+ desc_len = le16toh(current_desc->length);
+ status = current_desc->status;
+ if (status & E1000_RXD_STAT_EOP) {
+ count--;
+ eop = 1;
+ if (desc_len < ETHER_CRC_LEN) {
+ len = 0;
+ prev_len_adj = ETHER_CRC_LEN - desc_len;
+ } else
+ len = desc_len - ETHER_CRC_LEN;
+ } else {
+ eop = 0;
+ len = desc_len;
+ }
+
+ if (current_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
+ u8 last_byte;
+ u32 pkt_len = desc_len;
+
+ if (adapter->fmp != NULL)
+ pkt_len += adapter->fmp->m_pkthdr.len;
+
+ last_byte = *(mtod(mp, caddr_t) + desc_len - 1);
+ if (TBI_ACCEPT(&adapter->hw, status,
+ current_desc->errors, pkt_len, last_byte,
+ adapter->min_frame_size, adapter->max_frame_size)) {
+ e1000_tbi_adjust_stats_82543(&adapter->hw,
+ &adapter->stats, pkt_len,
+ adapter->hw.mac.addr,
+ adapter->max_frame_size);
+ if (len > 0)
+ len--;
+ } else
+ accept_frame = 0;
+ }
+
+ if (accept_frame) {
+ if (lem_get_buf(adapter, i) != 0) {
+ ifp->if_iqdrops++;
+ goto discard;
+ }
+
+ /* Assign correct length to the current fragment */
+ mp->m_len = len;
+
+ if (adapter->fmp == NULL) {
+ mp->m_pkthdr.len = len;
+ adapter->fmp = mp; /* Store the first mbuf */
+ adapter->lmp = mp;
+ } else {
+ /* Chain mbuf's together */
+ mp->m_flags &= ~M_PKTHDR;
+ /*
+ * Adjust length of previous mbuf in chain if
+ * we received less than 4 bytes in the last
+ * descriptor.
+ */
+ if (prev_len_adj > 0) {
+ adapter->lmp->m_len -= prev_len_adj;
+ adapter->fmp->m_pkthdr.len -=
+ prev_len_adj;
+ }
+ adapter->lmp->m_next = mp;
+ adapter->lmp = adapter->lmp->m_next;
+ adapter->fmp->m_pkthdr.len += len;
+ }
+
+ if (eop) {
+ adapter->fmp->m_pkthdr.rcvif = ifp;
+ ifp->if_ipackets++;
+ lem_receive_checksum(adapter, current_desc,
+ adapter->fmp);
+#ifndef __NO_STRICT_ALIGNMENT
+ if (adapter->max_frame_size >
+ (MCLBYTES - ETHER_ALIGN) &&
+ lem_fixup_rx(adapter) != 0)
+ goto skip;
+#endif
+ if (status & E1000_RXD_STAT_VP) {
+#if __FreeBSD_version < 700000
+ VLAN_INPUT_TAG_NEW(ifp, adapter->fmp,
+ (le16toh(current_desc->special) &
+ E1000_RXD_SPC_VLAN_MASK));
+#else
+ adapter->fmp->m_pkthdr.ether_vtag =
+ (le16toh(current_desc->special) &
+ E1000_RXD_SPC_VLAN_MASK);
+ adapter->fmp->m_flags |= M_VLANTAG;
+#endif
+ }
+#ifndef __NO_STRICT_ALIGNMENT
+skip:
+#endif
+ m = adapter->fmp;
+ adapter->fmp = NULL;
+ adapter->lmp = NULL;
+ }
+ } else {
+ ifp->if_ierrors++;
+discard:
+ /* Reuse loaded DMA map and just update mbuf chain */
+ mp = adapter->rx_buffer_area[i].m_head;
+ mp->m_len = mp->m_pkthdr.len = MCLBYTES;
+ mp->m_data = mp->m_ext.ext_buf;
+ mp->m_next = NULL;
+ if (adapter->max_frame_size <=
+ (MCLBYTES - ETHER_ALIGN))
+ m_adj(mp, ETHER_ALIGN);
+ if (adapter->fmp != NULL) {
+ m_freem(adapter->fmp);
+ adapter->fmp = NULL;
+ adapter->lmp = NULL;
+ }
+ m = NULL;
+ }
+
+ /* Zero out the receive descriptors status. */
+ current_desc->status = 0;
+ bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ /* Advance our pointers to the next descriptor. */
+ if (++i == adapter->num_rx_desc)
+ i = 0;
+ /* Call into the stack */
+ if (m != NULL) {
+ adapter->next_rx_desc_to_check = i;
+ EM_RX_UNLOCK(adapter);
+ (*ifp->if_input)(ifp, m);
+ EM_RX_LOCK(adapter);
+ rx_sent++;
+ i = adapter->next_rx_desc_to_check;
+ }
+ current_desc = &adapter->rx_desc_base[i];
+ }
+ adapter->next_rx_desc_to_check = i;
+
+ /* Advance the E1000's Receive Queue #0 "Tail Pointer". */
+ if (--i < 0)
+ i = adapter->num_rx_desc - 1;
+ E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), i);
+ EM_RX_UNLOCK(adapter);
+ return (rx_sent);
+}
+
+#ifndef __NO_STRICT_ALIGNMENT
+/*
+ * When jumbo frames are enabled we should realign entire payload on
+ * architecures with strict alignment. This is serious design mistake of 8254x
+ * as it nullifies DMA operations. 8254x just allows RX buffer size to be
+ * 2048/4096/8192/16384. What we really want is 2048 - ETHER_ALIGN to align its
+ * payload. On architecures without strict alignment restrictions 8254x still
+ * performs unaligned memory access which would reduce the performance too.
+ * To avoid copying over an entire frame to align, we allocate a new mbuf and
+ * copy ethernet header to the new mbuf. The new mbuf is prepended into the
+ * existing mbuf chain.
+ *
+ * Be aware, best performance of the 8254x is achived only when jumbo frame is
+ * not used at all on architectures with strict alignment.
+ */
+static int
+lem_fixup_rx(struct adapter *adapter)
+{
+ struct mbuf *m, *n;
+ int error;
+
+ error = 0;
+ m = adapter->fmp;
+ if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) {
+ bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
+ m->m_data += ETHER_HDR_LEN;
+ } else {
+ MGETHDR(n, M_DONTWAIT, MT_DATA);
+ if (n != NULL) {
+ bcopy(m->m_data, n->m_data, ETHER_HDR_LEN);
+ m->m_data += ETHER_HDR_LEN;
+ m->m_len -= ETHER_HDR_LEN;
+ n->m_len = ETHER_HDR_LEN;
+ M_MOVE_PKTHDR(n, m);
+ n->m_next = m;
+ adapter->fmp = n;
+ } else {
+ adapter->dropped_pkts++;
+ m_freem(adapter->fmp);
+ adapter->fmp = NULL;
+ error = ENOMEM;
+ }
+ }
+
+ return (error);
+}
+#endif
+
+/*********************************************************************
+ *
+ * Verify that the hardware indicated that the checksum is valid.
+ * Inform the stack about the status of checksum so that stack
+ * doesn't spend time verifying the checksum.
+ *
+ *********************************************************************/
+static void
+lem_receive_checksum(struct adapter *adapter,
+ struct e1000_rx_desc *rx_desc, struct mbuf *mp)
+{
+ /* 82543 or newer only */
+ if ((adapter->hw.mac.type < e1000_82543) ||
+ /* Ignore Checksum bit is set */
+ (rx_desc->status & E1000_RXD_STAT_IXSM)) {
+ mp->m_pkthdr.csum_flags = 0;
+ return;
+ }
+
+ if (rx_desc->status & E1000_RXD_STAT_IPCS) {
+ /* Did it pass? */
+ if (!(rx_desc->errors & E1000_RXD_ERR_IPE)) {
+ /* IP Checksum Good */
+ mp->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
+ mp->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+
+ } else {
+ mp->m_pkthdr.csum_flags = 0;
+ }
+ }
+
+ if (rx_desc->status & E1000_RXD_STAT_TCPCS) {
+ /* Did it pass? */
+ if (!(rx_desc->errors & E1000_RXD_ERR_TCPE)) {
+ mp->m_pkthdr.csum_flags |=
+ (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
+ mp->m_pkthdr.csum_data = htons(0xffff);
+ }
+ }
+}
+
+#if __FreeBSD_version >= 700029
+/*
+ * This routine is run via an vlan
+ * config EVENT
+ */
+static void
+lem_register_vlan(void *arg, struct ifnet *ifp, u16 vtag)
+{
+ struct adapter *adapter = ifp->if_softc;
+ u32 index, bit;
+
+ if (ifp->if_softc != arg) /* Not our event */
+ return;
+
+ if ((vtag == 0) || (vtag > 4095)) /* Invalid ID */
+ return;
+
+ index = (vtag >> 5) & 0x7F;
+ bit = vtag & 0x1F;
+ lem_shadow_vfta[index] |= (1 << bit);
+ ++adapter->num_vlans;
+ /* Re-init to load the changes */
+ lem_init(adapter);
+}
+
+/*
+ * This routine is run via an vlan
+ * unconfig EVENT
+ */
+static void
+lem_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag)
+{
+ struct adapter *adapter = ifp->if_softc;
+ u32 index, bit;
+
+ if (ifp->if_softc != arg)
+ return;
+
+ if ((vtag == 0) || (vtag > 4095)) /* Invalid */
+ return;
+
+ index = (vtag >> 5) & 0x7F;
+ bit = vtag & 0x1F;
+ lem_shadow_vfta[index] &= ~(1 << bit);
+ --adapter->num_vlans;
+ /* Re-init to load the changes */
+ lem_init(adapter);
+}
+
+static void
+lem_setup_vlan_hw_support(struct adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 reg;
+
+ /*
+ ** We get here thru init_locked, meaning
+ ** a soft reset, this has already cleared
+ ** the VFTA and other state, so if there
+ ** have been no vlan's registered do nothing.
+ */
+ if (adapter->num_vlans == 0)
+ return;
+
+ /*
+ ** A soft reset zero's out the VFTA, so
+ ** we need to repopulate it now.
+ */
+ for (int i = 0; i < EM_VFTA_SIZE; i++)
+ if (lem_shadow_vfta[i] != 0)
+ E1000_WRITE_REG_ARRAY(hw, E1000_VFTA,
+ i, lem_shadow_vfta[i]);
+
+ reg = E1000_READ_REG(hw, E1000_CTRL);
+ reg |= E1000_CTRL_VME;
+ E1000_WRITE_REG(hw, E1000_CTRL, reg);
+
+ /* Enable the Filter Table */
+ reg = E1000_READ_REG(hw, E1000_RCTL);
+ reg &= ~E1000_RCTL_CFIEN;
+ reg |= E1000_RCTL_VFE;
+ E1000_WRITE_REG(hw, E1000_RCTL, reg);
+
+ /* Update the frame size */
+ E1000_WRITE_REG(&adapter->hw, E1000_RLPML,
+ adapter->max_frame_size + VLAN_TAG_SIZE);
+}
+#endif
+
+static void
+lem_enable_intr(struct adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 ims_mask = IMS_ENABLE_MASK;
+
+ if (adapter->msix) {
+ E1000_WRITE_REG(hw, EM_EIAC, EM_MSIX_MASK);
+ ims_mask |= EM_MSIX_MASK;
+ }
+ E1000_WRITE_REG(hw, E1000_IMS, ims_mask);
+}
+
+static void
+lem_disable_intr(struct adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+
+ if (adapter->msix)
+ E1000_WRITE_REG(hw, EM_EIAC, 0);
+ E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff);
+}
+
+/*
+ * Bit of a misnomer, what this really means is
+ * to enable OS management of the system... aka
+ * to disable special hardware management features
+ */
+static void
+lem_init_manageability(struct adapter *adapter)
+{
+ /* A shared code workaround */
+ if (adapter->has_manage) {
+ int manc = E1000_READ_REG(&adapter->hw, E1000_MANC);
+ /* disable hardware interception of ARP */
+ manc &= ~(E1000_MANC_ARP_EN);
+ E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc);
+ }
+}
+
+/*
+ * Give control back to hardware management
+ * controller if there is one.
+ */
+static void
+lem_release_manageability(struct adapter *adapter)
+{
+ if (adapter->has_manage) {
+ int manc = E1000_READ_REG(&adapter->hw, E1000_MANC);
+
+ /* re-enable hardware interception of ARP */
+ manc |= E1000_MANC_ARP_EN;
+ E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc);
+ }
+}
+
+/*
+ * lem_get_hw_control sets the {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means
+ * that the driver is loaded. For AMT version type f/w
+ * this means that the network i/f is open.
+ */
+static void
+lem_get_hw_control(struct adapter *adapter)
+{
+ u32 ctrl_ext;
+
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
+ ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
+ return;
+}
+
+/*
+ * lem_release_hw_control resets {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means that
+ * the driver is no longer loaded. For AMT versions of the
+ * f/w this means that the network i/f is closed.
+ */
+static void
+lem_release_hw_control(struct adapter *adapter)
+{
+ u32 ctrl_ext;
+
+ if (!adapter->has_manage)
+ return;
+
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT,
+ ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
+ return;
+}
+
+static int
+lem_is_valid_ether_addr(u8 *addr)
+{
+ char zero_addr[6] = { 0, 0, 0, 0, 0, 0 };
+
+ if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*
+** Parse the interface capabilities with regard
+** to both system management and wake-on-lan for
+** later use.
+*/
+static void
+lem_get_wakeup(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+ u16 eeprom_data = 0, device_id, apme_mask;
+
+ adapter->has_manage = e1000_enable_mng_pass_thru(&adapter->hw);
+ apme_mask = EM_EEPROM_APME;
+
+ switch (adapter->hw.mac.type) {
+ case e1000_82542:
+ case e1000_82543:
+ break;
+ case e1000_82544:
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL2_REG, 1, &eeprom_data);
+ apme_mask = EM_82544_APME;
+ break;
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ if (adapter->hw.bus.func == 1) {
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+ break;
+ } else
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ break;
+ default:
+ e1000_read_nvm(&adapter->hw,
+ NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ break;
+ }
+ if (eeprom_data & apme_mask)
+ adapter->wol = (E1000_WUFC_MAG | E1000_WUFC_MC);
+ /*
+ * We have the eeprom settings, now apply the special cases
+ * where the eeprom may be wrong or the board won't support
+ * wake on lan on a particular port
+ */
+ device_id = pci_get_device(dev);
+ switch (device_id) {
+ case E1000_DEV_ID_82546GB_PCIE:
+ adapter->wol = 0;
+ break;
+ case E1000_DEV_ID_82546EB_FIBER:
+ case E1000_DEV_ID_82546GB_FIBER:
+ /* Wake events only supported on port A for dual fiber
+ * regardless of eeprom setting */
+ if (E1000_READ_REG(&adapter->hw, E1000_STATUS) &
+ E1000_STATUS_FUNC_1)
+ adapter->wol = 0;
+ break;
+ case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
+ /* if quad port adapter, disable WoL on all but port A */
+ if (global_quad_port_a != 0)
+ adapter->wol = 0;
+ /* Reset for multiple quad port adapters */
+ if (++global_quad_port_a == 4)
+ global_quad_port_a = 0;
+ break;
+ }
+ return;
+}
+
+
+/*
+ * Enable PCI Wake On Lan capability
+ */
+static void
+lem_enable_wakeup(device_t dev)
+{
+ struct adapter *adapter = device_get_softc(dev);
+ struct ifnet *ifp = adapter->ifp;
+ u32 pmc, ctrl, ctrl_ext, rctl;
+ u16 status;
+
+ if ((pci_find_extcap(dev, PCIY_PMG, &pmc) != 0))
+ return;
+
+ /* Advertise the wakeup capability */
+ ctrl = E1000_READ_REG(&adapter->hw, E1000_CTRL);
+ ctrl |= (E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN3);
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL, ctrl);
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
+
+ /* Keep the laser running on Fiber adapters */
+ if (adapter->hw.phy.media_type == e1000_media_type_fiber ||
+ adapter->hw.phy.media_type == e1000_media_type_internal_serdes) {
+ ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_SDP3_DATA;
+ E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, ctrl_ext);
+ }
+
+ /*
+ ** Determine type of Wakeup: note that wol
+ ** is set with all bits on by default.
+ */
+ if ((ifp->if_capenable & IFCAP_WOL_MAGIC) == 0)
+ adapter->wol &= ~E1000_WUFC_MAG;
+
+ if ((ifp->if_capenable & IFCAP_WOL_MCAST) == 0)
+ adapter->wol &= ~E1000_WUFC_MC;
+ else {
+ rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL);
+ rctl |= E1000_RCTL_MPE;
+ E1000_WRITE_REG(&adapter->hw, E1000_RCTL, rctl);
+ }
+
+ if (adapter->hw.mac.type == e1000_pchlan) {
+ if (lem_enable_phy_wakeup(adapter))
+ return;
+ } else {
+ E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN);
+ E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol);
+ }
+
+
+ /* Request PME */
+ status = pci_read_config(dev, pmc + PCIR_POWER_STATUS, 2);
+ status &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
+ if (ifp->if_capenable & IFCAP_WOL)
+ status |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
+ pci_write_config(dev, pmc + PCIR_POWER_STATUS, status, 2);
+
+ return;
+}
+
+/*
+** WOL in the newer chipset interfaces (pchlan)
+** require thing to be copied into the phy
+*/
+static int
+lem_enable_phy_wakeup(struct adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 mreg, ret = 0;
+ u16 preg;
+
+ /* copy MAC RARs to PHY RARs */
+ for (int i = 0; i < adapter->hw.mac.rar_entry_count; i++) {
+ mreg = E1000_READ_REG(hw, E1000_RAL(i));
+ e1000_write_phy_reg(hw, BM_RAR_L(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_RAR_M(i),
+ (u16)((mreg >> 16) & 0xFFFF));
+ mreg = E1000_READ_REG(hw, E1000_RAH(i));
+ e1000_write_phy_reg(hw, BM_RAR_H(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_RAR_CTRL(i),
+ (u16)((mreg >> 16) & 0xFFFF));
+ }
+
+ /* copy MAC MTA to PHY MTA */
+ for (int i = 0; i < adapter->hw.mac.mta_reg_count; i++) {
+ mreg = E1000_READ_REG_ARRAY(hw, E1000_MTA, i);
+ e1000_write_phy_reg(hw, BM_MTA(i), (u16)(mreg & 0xFFFF));
+ e1000_write_phy_reg(hw, BM_MTA(i) + 1,
+ (u16)((mreg >> 16) & 0xFFFF));
+ }
+
+ /* configure PHY Rx Control register */
+ e1000_read_phy_reg(&adapter->hw, BM_RCTL, &preg);
+ mreg = E1000_READ_REG(hw, E1000_RCTL);
+ if (mreg & E1000_RCTL_UPE)
+ preg |= BM_RCTL_UPE;
+ if (mreg & E1000_RCTL_MPE)
+ preg |= BM_RCTL_MPE;
+ preg &= ~(BM_RCTL_MO_MASK);
+ if (mreg & E1000_RCTL_MO_3)
+ preg |= (((mreg & E1000_RCTL_MO_3) >> E1000_RCTL_MO_SHIFT)
+ << BM_RCTL_MO_SHIFT);
+ if (mreg & E1000_RCTL_BAM)
+ preg |= BM_RCTL_BAM;
+ if (mreg & E1000_RCTL_PMCF)
+ preg |= BM_RCTL_PMCF;
+ mreg = E1000_READ_REG(hw, E1000_CTRL);
+ if (mreg & E1000_CTRL_RFCE)
+ preg |= BM_RCTL_RFCE;
+ e1000_write_phy_reg(&adapter->hw, BM_RCTL, preg);
+
+ /* enable PHY wakeup in MAC register */
+ E1000_WRITE_REG(hw, E1000_WUC,
+ E1000_WUC_PHY_WAKE | E1000_WUC_PME_EN);
+ E1000_WRITE_REG(hw, E1000_WUFC, adapter->wol);
+
+ /* configure and enable PHY wakeup in PHY registers */
+ e1000_write_phy_reg(&adapter->hw, BM_WUFC, adapter->wol);
+ e1000_write_phy_reg(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
+
+ /* activate PHY wakeup */
+ ret = hw->phy.ops.acquire(hw);
+ if (ret) {
+ printf("Could not acquire PHY\n");
+ return ret;
+ }
+ e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
+ ret = e1000_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &preg);
+ if (ret) {
+ printf("Could not read PHY page 769\n");
+ goto out;
+ }
+ preg |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
+ ret = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, preg);
+ if (ret)
+ printf("Could not set PHY Host Wakeup bit\n");
+out:
+ hw->phy.ops.release(hw);
+
+ return ret;
+}
+
+static void
+lem_led_func(void *arg, int onoff)
+{
+ struct adapter *adapter = arg;
+
+ EM_CORE_LOCK(adapter);
+ if (onoff) {
+ e1000_setup_led(&adapter->hw);
+ e1000_led_on(&adapter->hw);
+ } else {
+ e1000_led_off(&adapter->hw);
+ e1000_cleanup_led(&adapter->hw);
+ }
+ EM_CORE_UNLOCK(adapter);
+}
+
+/*********************************************************************
+* 82544 Coexistence issue workaround.
+* There are 2 issues.
+* 1. Transmit Hang issue.
+* To detect this issue, following equation can be used...
+* SIZE[3:0] + ADDR[2:0] = SUM[3:0].
+* If SUM[3:0] is in between 1 to 4, we will have this issue.
+*
+* 2. DAC issue.
+* To detect this issue, following equation can be used...
+* SIZE[3:0] + ADDR[2:0] = SUM[3:0].
+* If SUM[3:0] is in between 9 to c, we will have this issue.
+*
+*
+* WORKAROUND:
+* Make sure we do not have ending address
+* as 1,2,3,4(Hang) or 9,a,b,c (DAC)
+*
+*************************************************************************/
+static u32
+lem_fill_descriptors (bus_addr_t address, u32 length,
+ PDESC_ARRAY desc_array)
+{
+ u32 safe_terminator;
+
+ /* Since issue is sensitive to length and address.*/
+ /* Let us first check the address...*/
+ if (length <= 4) {
+ desc_array->descriptor[0].address = address;
+ desc_array->descriptor[0].length = length;
+ desc_array->elements = 1;
+ return (desc_array->elements);
+ }
+ safe_terminator = (u32)((((u32)address & 0x7) +
+ (length & 0xF)) & 0xF);
+ /* if it does not fall between 0x1 to 0x4 and 0x9 to 0xC then return */
+ if (safe_terminator == 0 ||
+ (safe_terminator > 4 &&
+ safe_terminator < 9) ||
+ (safe_terminator > 0xC &&
+ safe_terminator <= 0xF)) {
+ desc_array->descriptor[0].address = address;
+ desc_array->descriptor[0].length = length;
+ desc_array->elements = 1;
+ return (desc_array->elements);
+ }
+
+ desc_array->descriptor[0].address = address;
+ desc_array->descriptor[0].length = length - 4;
+ desc_array->descriptor[1].address = address + (length - 4);
+ desc_array->descriptor[1].length = 4;
+ desc_array->elements = 2;
+ return (desc_array->elements);
+}
+
+/**********************************************************************
+ *
+ * Update the board statistics counters.
+ *
+ **********************************************************************/
+static void
+lem_update_stats_counters(struct adapter *adapter)
+{
+ struct ifnet *ifp;
+
+ if(adapter->hw.phy.media_type == e1000_media_type_copper ||
+ (E1000_READ_REG(&adapter->hw, E1000_STATUS) & E1000_STATUS_LU)) {
+ adapter->stats.symerrs += E1000_READ_REG(&adapter->hw, E1000_SYMERRS);
+ adapter->stats.sec += E1000_READ_REG(&adapter->hw, E1000_SEC);
+ }
+ adapter->stats.crcerrs += E1000_READ_REG(&adapter->hw, E1000_CRCERRS);
+ adapter->stats.mpc += E1000_READ_REG(&adapter->hw, E1000_MPC);
+ adapter->stats.scc += E1000_READ_REG(&adapter->hw, E1000_SCC);
+ adapter->stats.ecol += E1000_READ_REG(&adapter->hw, E1000_ECOL);
+
+ adapter->stats.mcc += E1000_READ_REG(&adapter->hw, E1000_MCC);
+ adapter->stats.latecol += E1000_READ_REG(&adapter->hw, E1000_LATECOL);
+ adapter->stats.colc += E1000_READ_REG(&adapter->hw, E1000_COLC);
+ adapter->stats.dc += E1000_READ_REG(&adapter->hw, E1000_DC);
+ adapter->stats.rlec += E1000_READ_REG(&adapter->hw, E1000_RLEC);
+ adapter->stats.xonrxc += E1000_READ_REG(&adapter->hw, E1000_XONRXC);
+ adapter->stats.xontxc += E1000_READ_REG(&adapter->hw, E1000_XONTXC);
+ adapter->stats.xoffrxc += E1000_READ_REG(&adapter->hw, E1000_XOFFRXC);
+ adapter->stats.xofftxc += E1000_READ_REG(&adapter->hw, E1000_XOFFTXC);
+ adapter->stats.fcruc += E1000_READ_REG(&adapter->hw, E1000_FCRUC);
+ adapter->stats.prc64 += E1000_READ_REG(&adapter->hw, E1000_PRC64);
+ adapter->stats.prc127 += E1000_READ_REG(&adapter->hw, E1000_PRC127);
+ adapter->stats.prc255 += E1000_READ_REG(&adapter->hw, E1000_PRC255);
+ adapter->stats.prc511 += E1000_READ_REG(&adapter->hw, E1000_PRC511);
+ adapter->stats.prc1023 += E1000_READ_REG(&adapter->hw, E1000_PRC1023);
+ adapter->stats.prc1522 += E1000_READ_REG(&adapter->hw, E1000_PRC1522);
+ adapter->stats.gprc += E1000_READ_REG(&adapter->hw, E1000_GPRC);
+ adapter->stats.bprc += E1000_READ_REG(&adapter->hw, E1000_BPRC);
+ adapter->stats.mprc += E1000_READ_REG(&adapter->hw, E1000_MPRC);
+ adapter->stats.gptc += E1000_READ_REG(&adapter->hw, E1000_GPTC);
+
+ /* For the 64-bit byte counters the low dword must be read first. */
+ /* Both registers clear on the read of the high dword */
+
+ adapter->stats.gorc += E1000_READ_REG(&adapter->hw, E1000_GORCH);
+ adapter->stats.gotc += E1000_READ_REG(&adapter->hw, E1000_GOTCH);
+
+ adapter->stats.rnbc += E1000_READ_REG(&adapter->hw, E1000_RNBC);
+ adapter->stats.ruc += E1000_READ_REG(&adapter->hw, E1000_RUC);
+ adapter->stats.rfc += E1000_READ_REG(&adapter->hw, E1000_RFC);
+ adapter->stats.roc += E1000_READ_REG(&adapter->hw, E1000_ROC);
+ adapter->stats.rjc += E1000_READ_REG(&adapter->hw, E1000_RJC);
+
+ adapter->stats.tor += E1000_READ_REG(&adapter->hw, E1000_TORH);
+ adapter->stats.tot += E1000_READ_REG(&adapter->hw, E1000_TOTH);
+
+ adapter->stats.tpr += E1000_READ_REG(&adapter->hw, E1000_TPR);
+ adapter->stats.tpt += E1000_READ_REG(&adapter->hw, E1000_TPT);
+ adapter->stats.ptc64 += E1000_READ_REG(&adapter->hw, E1000_PTC64);
+ adapter->stats.ptc127 += E1000_READ_REG(&adapter->hw, E1000_PTC127);
+ adapter->stats.ptc255 += E1000_READ_REG(&adapter->hw, E1000_PTC255);
+ adapter->stats.ptc511 += E1000_READ_REG(&adapter->hw, E1000_PTC511);
+ adapter->stats.ptc1023 += E1000_READ_REG(&adapter->hw, E1000_PTC1023);
+ adapter->stats.ptc1522 += E1000_READ_REG(&adapter->hw, E1000_PTC1522);
+ adapter->stats.mptc += E1000_READ_REG(&adapter->hw, E1000_MPTC);
+ adapter->stats.bptc += E1000_READ_REG(&adapter->hw, E1000_BPTC);
+
+ if (adapter->hw.mac.type >= e1000_82543) {
+ adapter->stats.algnerrc +=
+ E1000_READ_REG(&adapter->hw, E1000_ALGNERRC);
+ adapter->stats.rxerrc +=
+ E1000_READ_REG(&adapter->hw, E1000_RXERRC);
+ adapter->stats.tncrs +=
+ E1000_READ_REG(&adapter->hw, E1000_TNCRS);
+ adapter->stats.cexterr +=
+ E1000_READ_REG(&adapter->hw, E1000_CEXTERR);
+ adapter->stats.tsctc +=
+ E1000_READ_REG(&adapter->hw, E1000_TSCTC);
+ adapter->stats.tsctfc +=
+ E1000_READ_REG(&adapter->hw, E1000_TSCTFC);
+ }
+ ifp = adapter->ifp;
+
+ ifp->if_collisions = adapter->stats.colc;
+
+ /* Rx Errors */
+ ifp->if_ierrors = adapter->dropped_pkts + adapter->stats.rxerrc +
+ adapter->stats.crcerrs + adapter->stats.algnerrc +
+ adapter->stats.ruc + adapter->stats.roc +
+ adapter->stats.mpc + adapter->stats.cexterr;
+
+ /* Tx Errors */
+ ifp->if_oerrors = adapter->stats.ecol +
+ adapter->stats.latecol + adapter->watchdog_events;
+}
+
+
+/**********************************************************************
+ *
+ * This routine is called only when lem_display_debug_stats is enabled.
+ * This routine provides a way to take a look at important statistics
+ * maintained by the driver and hardware.
+ *
+ **********************************************************************/
+static void
+lem_print_debug_info(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+ u8 *hw_addr = adapter->hw.hw_addr;
+
+ device_printf(dev, "Adapter hardware address = %p \n", hw_addr);
+ device_printf(dev, "CTRL = 0x%x RCTL = 0x%x \n",
+ E1000_READ_REG(&adapter->hw, E1000_CTRL),
+ E1000_READ_REG(&adapter->hw, E1000_RCTL));
+ device_printf(dev, "Packet buffer = Tx=%dk Rx=%dk \n",
+ ((E1000_READ_REG(&adapter->hw, E1000_PBA) & 0xffff0000) >> 16),\
+ (E1000_READ_REG(&adapter->hw, E1000_PBA) & 0xffff) );
+ device_printf(dev, "Flow control watermarks high = %d low = %d\n",
+ adapter->hw.fc.high_water,
+ adapter->hw.fc.low_water);
+ device_printf(dev, "tx_int_delay = %d, tx_abs_int_delay = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_TIDV),
+ E1000_READ_REG(&adapter->hw, E1000_TADV));
+ device_printf(dev, "rx_int_delay = %d, rx_abs_int_delay = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_RDTR),
+ E1000_READ_REG(&adapter->hw, E1000_RADV));
+ device_printf(dev, "fifo workaround = %lld, fifo_reset_count = %lld\n",
+ (long long)adapter->tx_fifo_wrk_cnt,
+ (long long)adapter->tx_fifo_reset_cnt);
+ device_printf(dev, "hw tdh = %d, hw tdt = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_TDH(0)),
+ E1000_READ_REG(&adapter->hw, E1000_TDT(0)));
+ device_printf(dev, "hw rdh = %d, hw rdt = %d\n",
+ E1000_READ_REG(&adapter->hw, E1000_RDH(0)),
+ E1000_READ_REG(&adapter->hw, E1000_RDT(0)));
+ device_printf(dev, "Num Tx descriptors avail = %d\n",
+ adapter->num_tx_desc_avail);
+ device_printf(dev, "Tx Descriptors not avail1 = %ld\n",
+ adapter->no_tx_desc_avail1);
+ device_printf(dev, "Tx Descriptors not avail2 = %ld\n",
+ adapter->no_tx_desc_avail2);
+ device_printf(dev, "Std mbuf failed = %ld\n",
+ adapter->mbuf_alloc_failed);
+ device_printf(dev, "Std mbuf cluster failed = %ld\n",
+ adapter->mbuf_cluster_failed);
+ device_printf(dev, "Driver dropped packets = %ld\n",
+ adapter->dropped_pkts);
+ device_printf(dev, "Driver tx dma failure in encap = %ld\n",
+ adapter->no_tx_dma_setup);
+}
+
+static void
+lem_print_hw_stats(struct adapter *adapter)
+{
+ device_t dev = adapter->dev;
+
+ device_printf(dev, "Excessive collisions = %lld\n",
+ (long long)adapter->stats.ecol);
+#if (DEBUG_HW > 0) /* Dont output these errors normally */
+ device_printf(dev, "Symbol errors = %lld\n",
+ (long long)adapter->stats.symerrs);
+#endif
+ device_printf(dev, "Sequence errors = %lld\n",
+ (long long)adapter->stats.sec);
+ device_printf(dev, "Defer count = %lld\n",
+ (long long)adapter->stats.dc);
+ device_printf(dev, "Missed Packets = %lld\n",
+ (long long)adapter->stats.mpc);
+ device_printf(dev, "Receive No Buffers = %lld\n",
+ (long long)adapter->stats.rnbc);
+ /* RLEC is inaccurate on some hardware, calculate our own. */
+ device_printf(dev, "Receive Length Errors = %lld\n",
+ ((long long)adapter->stats.roc + (long long)adapter->stats.ruc));
+ device_printf(dev, "Receive errors = %lld\n",
+ (long long)adapter->stats.rxerrc);
+ device_printf(dev, "Crc errors = %lld\n",
+ (long long)adapter->stats.crcerrs);
+ device_printf(dev, "Alignment errors = %lld\n",
+ (long long)adapter->stats.algnerrc);
+ device_printf(dev, "Collision/Carrier extension errors = %lld\n",
+ (long long)adapter->stats.cexterr);
+ device_printf(dev, "RX overruns = %ld\n", adapter->rx_overruns);
+ device_printf(dev, "watchdog timeouts = %ld\n",
+ adapter->watchdog_events);
+ device_printf(dev, "RX MSIX IRQ = %ld TX MSIX IRQ = %ld"
+ " LINK MSIX IRQ = %ld\n", adapter->rx_irq,
+ adapter->tx_irq , adapter->link_irq);
+ device_printf(dev, "XON Rcvd = %lld\n",
+ (long long)adapter->stats.xonrxc);
+ device_printf(dev, "XON Xmtd = %lld\n",
+ (long long)adapter->stats.xontxc);
+ device_printf(dev, "XOFF Rcvd = %lld\n",
+ (long long)adapter->stats.xoffrxc);
+ device_printf(dev, "XOFF Xmtd = %lld\n",
+ (long long)adapter->stats.xofftxc);
+ device_printf(dev, "Good Packets Rcvd = %lld\n",
+ (long long)adapter->stats.gprc);
+ device_printf(dev, "Good Packets Xmtd = %lld\n",
+ (long long)adapter->stats.gptc);
+ device_printf(dev, "TSO Contexts Xmtd = %lld\n",
+ (long long)adapter->stats.tsctc);
+ device_printf(dev, "TSO Contexts Failed = %lld\n",
+ (long long)adapter->stats.tsctfc);
+}
+
+/**********************************************************************
+ *
+ * This routine provides a way to dump out the adapter eeprom,
+ * often a useful debug/service tool. This only dumps the first
+ * 32 words, stuff that matters is in that extent.
+ *
+ **********************************************************************/
+static void
+lem_print_nvm_info(struct adapter *adapter)
+{
+ u16 eeprom_data;
+ int i, j, row = 0;
+
+ /* Its a bit crude, but it gets the job done */
+ printf("\nInterface EEPROM Dump:\n");
+ printf("Offset\n0x0000 ");
+ for (i = 0, j = 0; i < 32; i++, j++) {
+ if (j == 8) { /* Make the offset block */
+ j = 0; ++row;
+ printf("\n0x00%x0 ",row);
+ }
+ e1000_read_nvm(&adapter->hw, i, 1, &eeprom_data);
+ printf("%04x ", eeprom_data);
+ }
+ printf("\n");
+}
+
+static int
+lem_sysctl_debug_info(SYSCTL_HANDLER_ARGS)
+{
+ struct adapter *adapter;
+ int error;
+ int result;
+
+ result = -1;
+ error = sysctl_handle_int(oidp, &result, 0, req);
+
+ if (error || !req->newptr)
+ return (error);
+
+ if (result == 1) {
+ adapter = (struct adapter *)arg1;
+ lem_print_debug_info(adapter);
+ }
+ /*
+ * This value will cause a hex dump of the
+ * first 32 16-bit words of the EEPROM to
+ * the screen.
+ */
+ if (result == 2) {
+ adapter = (struct adapter *)arg1;
+ lem_print_nvm_info(adapter);
+ }
+
+ return (error);
+}
+
+
+static int
+lem_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct adapter *adapter;
+ int error;
+ int result;
+
+ result = -1;
+ error = sysctl_handle_int(oidp, &result, 0, req);
+
+ if (error || !req->newptr)
+ return (error);
+
+ if (result == 1) {
+ adapter = (struct adapter *)arg1;
+ lem_print_hw_stats(adapter);
+ }
+
+ return (error);
+}
+
+static int
+lem_sysctl_int_delay(SYSCTL_HANDLER_ARGS)
+{
+ struct em_int_delay_info *info;
+ struct adapter *adapter;
+ u32 regval;
+ int error;
+ int usecs;
+ int ticks;
+
+ info = (struct em_int_delay_info *)arg1;
+ usecs = info->value;
+ error = sysctl_handle_int(oidp, &usecs, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (usecs < 0 || usecs > EM_TICKS_TO_USECS(65535))
+ return (EINVAL);
+ info->value = usecs;
+ ticks = EM_USECS_TO_TICKS(usecs);
+
+ adapter = info->adapter;
+
+ EM_CORE_LOCK(adapter);
+ regval = E1000_READ_OFFSET(&adapter->hw, info->offset);
+ regval = (regval & ~0xffff) | (ticks & 0xffff);
+ /* Handle a few special cases. */
+ switch (info->offset) {
+ case E1000_RDTR:
+ break;
+ case E1000_TIDV:
+ if (ticks == 0) {
+ adapter->txd_cmd &= ~E1000_TXD_CMD_IDE;
+ /* Don't write 0 into the TIDV register. */
+ regval++;
+ } else
+ adapter->txd_cmd |= E1000_TXD_CMD_IDE;
+ break;
+ }
+ E1000_WRITE_OFFSET(&adapter->hw, info->offset, regval);
+ EM_CORE_UNLOCK(adapter);
+ return (0);
+}
+
+static void
+lem_add_int_delay_sysctl(struct adapter *adapter, const char *name,
+ const char *description, struct em_int_delay_info *info,
+ int offset, int value)
+{
+ info->adapter = adapter;
+ info->offset = offset;
+ info->value = value;
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(adapter->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)),
+ OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW,
+ info, 0, lem_sysctl_int_delay, "I", description);
+}
+
+#ifndef EM_LEGACY_IRQ
+static void
+lem_add_rx_process_limit(struct adapter *adapter, const char *name,
+ const char *description, int *limit, int value)
+{
+ *limit = value;
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(adapter->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)),
+ OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description);
+}
+#endif
+
+
diff --git a/sys/dev/e1000/if_lem.h b/sys/dev/e1000/if_lem.h
new file mode 100644
index 000000000000..13c2cbc4dd3c
--- /dev/null
+++ b/sys/dev/e1000/if_lem.h
@@ -0,0 +1,481 @@
+/******************************************************************************
+
+ Copyright (c) 2001-2010, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+/*$FreeBSD$*/
+
+
+#ifndef _LEM_H_DEFINED_
+#define _LEM_H_DEFINED_
+
+
+/* Tunables */
+
+/*
+ * EM_TXD: Maximum number of Transmit Descriptors
+ * Valid Range: 80-256 for 82542 and 82543-based adapters
+ * 80-4096 for others
+ * Default Value: 256
+ * This value is the number of transmit descriptors allocated by the driver.
+ * Increasing this value allows the driver to queue more transmits. Each
+ * descriptor is 16 bytes.
+ * Since TDLEN should be multiple of 128bytes, the number of transmit
+ * desscriptors should meet the following condition.
+ * (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
+ */
+#define EM_MIN_TXD 80
+#define EM_MAX_TXD_82543 256
+#define EM_MAX_TXD 4096
+#define EM_DEFAULT_TXD EM_MAX_TXD_82543
+
+/*
+ * EM_RXD - Maximum number of receive Descriptors
+ * Valid Range: 80-256 for 82542 and 82543-based adapters
+ * 80-4096 for others
+ * Default Value: 256
+ * This value is the number of receive descriptors allocated by the driver.
+ * Increasing this value allows the driver to buffer more incoming packets.
+ * Each descriptor is 16 bytes. A receive buffer is also allocated for each
+ * descriptor. The maximum MTU size is 16110.
+ * Since TDLEN should be multiple of 128bytes, the number of transmit
+ * desscriptors should meet the following condition.
+ * (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0
+ */
+#define EM_MIN_RXD 80
+#define EM_MAX_RXD_82543 256
+#define EM_MAX_RXD 4096
+#define EM_DEFAULT_RXD EM_MAX_RXD_82543
+
+/*
+ * EM_TIDV - Transmit Interrupt Delay Value
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value delays the generation of transmit interrupts in units of
+ * 1.024 microseconds. Transmit interrupt reduction can improve CPU
+ * efficiency if properly tuned for specific network traffic. If the
+ * system is reporting dropped transmits, this value may be set too high
+ * causing the driver to run out of available transmit descriptors.
+ */
+#define EM_TIDV 64
+
+/*
+ * EM_TADV - Transmit Absolute Interrupt Delay Value
+ * (Not valid for 82542/82543/82544)
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value, in units of 1.024 microseconds, limits the delay in which a
+ * transmit interrupt is generated. Useful only if EM_TIDV is non-zero,
+ * this value ensures that an interrupt is generated after the initial
+ * packet is sent on the wire within the set amount of time. Proper tuning,
+ * along with EM_TIDV, may improve traffic throughput in specific
+ * network conditions.
+ */
+#define EM_TADV 64
+
+/*
+ * EM_RDTR - Receive Interrupt Delay Timer (Packet Timer)
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 0
+ * This value delays the generation of receive interrupts in units of 1.024
+ * microseconds. Receive interrupt reduction can improve CPU efficiency if
+ * properly tuned for specific network traffic. Increasing this value adds
+ * extra latency to frame reception and can end up decreasing the throughput
+ * of TCP traffic. If the system is reporting dropped receives, this value
+ * may be set too high, causing the driver to run out of available receive
+ * descriptors.
+ *
+ * CAUTION: When setting EM_RDTR to a value other than 0, adapters
+ * may hang (stop transmitting) under certain network conditions.
+ * If this occurs a WATCHDOG message is logged in the system
+ * event log. In addition, the controller is automatically reset,
+ * restoring the network connection. To eliminate the potential
+ * for the hang ensure that EM_RDTR is set to 0.
+ */
+#define EM_RDTR 0
+
+/*
+ * Receive Interrupt Absolute Delay Timer (Not valid for 82542/82543/82544)
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value, in units of 1.024 microseconds, limits the delay in which a
+ * receive interrupt is generated. Useful only if EM_RDTR is non-zero,
+ * this value ensures that an interrupt is generated after the initial
+ * packet is received within the set amount of time. Proper tuning,
+ * along with EM_RDTR, may improve traffic throughput in specific network
+ * conditions.
+ */
+#define EM_RADV 64
+
+/*
+ * This parameter controls the max duration of transmit watchdog.
+ */
+#define EM_WATCHDOG (10 * hz)
+
+/*
+ * This parameter controls when the driver calls the routine to reclaim
+ * transmit descriptors.
+ */
+#define EM_TX_CLEANUP_THRESHOLD (adapter->num_tx_desc / 8)
+#define EM_TX_OP_THRESHOLD (adapter->num_tx_desc / 32)
+
+/*
+ * This parameter controls whether or not autonegotation is enabled.
+ * 0 - Disable autonegotiation
+ * 1 - Enable autonegotiation
+ */
+#define DO_AUTO_NEG 1
+
+/*
+ * This parameter control whether or not the driver will wait for
+ * autonegotiation to complete.
+ * 1 - Wait for autonegotiation to complete
+ * 0 - Don't wait for autonegotiation to complete
+ */
+#define WAIT_FOR_AUTO_NEG_DEFAULT 0
+
+/* Tunables -- End */
+
+#define AUTONEG_ADV_DEFAULT (ADVERTISE_10_HALF | ADVERTISE_10_FULL | \
+ ADVERTISE_100_HALF | ADVERTISE_100_FULL | \
+ ADVERTISE_1000_FULL)
+
+#define AUTO_ALL_MODES 0
+
+/* PHY master/slave setting */
+#define EM_MASTER_SLAVE e1000_ms_hw_default
+
+/*
+ * Micellaneous constants
+ */
+#define EM_VENDOR_ID 0x8086
+#define EM_FLASH 0x0014
+
+#define EM_JUMBO_PBA 0x00000028
+#define EM_DEFAULT_PBA 0x00000030
+#define EM_SMARTSPEED_DOWNSHIFT 3
+#define EM_SMARTSPEED_MAX 15
+#define EM_MAX_LOOP 10
+
+#define MAX_NUM_MULTICAST_ADDRESSES 128
+#define PCI_ANY_ID (~0U)
+#define ETHER_ALIGN 2
+#define EM_FC_PAUSE_TIME 0x0680
+#define EM_EEPROM_APME 0x400;
+#define EM_82544_APME 0x0004;
+
+/* Code compatilbility between 6 and 7 */
+#ifndef ETHER_BPF_MTAP
+#define ETHER_BPF_MTAP BPF_MTAP
+#endif
+
+/*
+ * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be
+ * multiple of 128 bytes. So we align TDBA/RDBA on 128 byte boundary. This will
+ * also optimize cache line size effect. H/W supports up to cache line size 128.
+ */
+#define EM_DBA_ALIGN 128
+
+#define SPEED_MODE_BIT (1<<21) /* On PCI-E MACs only */
+
+/* PCI Config defines */
+#define EM_BAR_TYPE(v) ((v) & EM_BAR_TYPE_MASK)
+#define EM_BAR_TYPE_MASK 0x00000001
+#define EM_BAR_TYPE_MMEM 0x00000000
+#define EM_BAR_TYPE_IO 0x00000001
+#define EM_BAR_TYPE_FLASH 0x0014
+#define EM_BAR_MEM_TYPE(v) ((v) & EM_BAR_MEM_TYPE_MASK)
+#define EM_BAR_MEM_TYPE_MASK 0x00000006
+#define EM_BAR_MEM_TYPE_32BIT 0x00000000
+#define EM_BAR_MEM_TYPE_64BIT 0x00000004
+#define EM_MSIX_BAR 3 /* On 82575 */
+
+/* Defines for printing debug information */
+#define DEBUG_INIT 0
+#define DEBUG_IOCTL 0
+#define DEBUG_HW 0
+
+#define INIT_DEBUGOUT(S) if (DEBUG_INIT) printf(S "\n")
+#define INIT_DEBUGOUT1(S, A) if (DEBUG_INIT) printf(S "\n", A)
+#define INIT_DEBUGOUT2(S, A, B) if (DEBUG_INIT) printf(S "\n", A, B)
+#define IOCTL_DEBUGOUT(S) if (DEBUG_IOCTL) printf(S "\n")
+#define IOCTL_DEBUGOUT1(S, A) if (DEBUG_IOCTL) printf(S "\n", A)
+#define IOCTL_DEBUGOUT2(S, A, B) if (DEBUG_IOCTL) printf(S "\n", A, B)
+#define HW_DEBUGOUT(S) if (DEBUG_HW) printf(S "\n")
+#define HW_DEBUGOUT1(S, A) if (DEBUG_HW) printf(S "\n", A)
+#define HW_DEBUGOUT2(S, A, B) if (DEBUG_HW) printf(S "\n", A, B)
+
+#define EM_MAX_SCATTER 64
+#define EM_VFTA_SIZE 128
+#define EM_TSO_SIZE (65535 + sizeof(struct ether_vlan_header))
+#define EM_TSO_SEG_SIZE 4096 /* Max dma segment size */
+#define EM_MSIX_MASK 0x01F00000 /* For 82574 use */
+#define ETH_ZLEN 60
+#define ETH_ADDR_LEN 6
+#define CSUM_OFFLOAD 7 /* Offload bits in mbuf flag */
+
+/*
+ * 82574 has a nonstandard address for EIAC
+ * and since its only used in MSIX, and in
+ * the em driver only 82574 uses MSIX we can
+ * solve it just using this define.
+ */
+#define EM_EIAC 0x000DC
+
+/* Used in for 82547 10Mb Half workaround */
+#define EM_PBA_BYTES_SHIFT 0xA
+#define EM_TX_HEAD_ADDR_SHIFT 7
+#define EM_PBA_TX_MASK 0xFFFF0000
+#define EM_FIFO_HDR 0x10
+#define EM_82547_PKT_THRESH 0x3e0
+
+/* Precision Time Sync (IEEE 1588) defines */
+#define ETHERTYPE_IEEE1588 0x88F7
+#define PICOSECS_PER_TICK 20833
+#define TSYNC_PORT 319 /* UDP port for the protocol */
+
+/*
+ * Bus dma allocation structure used by
+ * e1000_dma_malloc and e1000_dma_free.
+ */
+struct em_dma_alloc {
+ bus_addr_t dma_paddr;
+ caddr_t dma_vaddr;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_dma_segment_t dma_seg;
+ int dma_nseg;
+};
+
+struct adapter;
+
+struct em_int_delay_info {
+ struct adapter *adapter; /* Back-pointer to the adapter struct */
+ int offset; /* Register offset to read/write */
+ int value; /* Current value in usecs */
+};
+
+/* Our adapter structure */
+struct adapter {
+ struct ifnet *ifp;
+#if __FreeBSD_version >= 800000
+ struct buf_ring *br;
+#endif
+ struct e1000_hw hw;
+
+ /* FreeBSD operating-system-specific structures. */
+ struct e1000_osdep osdep;
+ struct device *dev;
+ struct cdev *led_dev;
+
+ struct resource *memory;
+ struct resource *flash;
+ struct resource *msix;
+
+ struct resource *ioport;
+ int io_rid;
+
+ /* 82574 may use 3 int vectors */
+ struct resource *res[3];
+ void *tag[3];
+ int rid[3];
+
+ struct ifmedia media;
+ struct callout timer;
+ struct callout tx_fifo_timer;
+ bool watchdog_check;
+ int watchdog_time;
+ int msi;
+ int if_flags;
+ int max_frame_size;
+ int min_frame_size;
+ struct mtx core_mtx;
+ struct mtx tx_mtx;
+ struct mtx rx_mtx;
+ int em_insert_vlan_header;
+
+ /* Task for FAST handling */
+ struct task link_task;
+ struct task rxtx_task;
+ struct task rx_task;
+ struct task tx_task;
+ struct taskqueue *tq; /* private task queue */
+
+#if __FreeBSD_version >= 700029
+ eventhandler_tag vlan_attach;
+ eventhandler_tag vlan_detach;
+ u32 num_vlans;
+#endif
+
+ /* Management and WOL features */
+ u32 wol;
+ bool has_manage;
+ bool has_amt;
+
+ /* Info about the board itself */
+ uint8_t link_active;
+ uint16_t link_speed;
+ uint16_t link_duplex;
+ uint32_t smartspeed;
+ struct em_int_delay_info tx_int_delay;
+ struct em_int_delay_info tx_abs_int_delay;
+ struct em_int_delay_info rx_int_delay;
+ struct em_int_delay_info rx_abs_int_delay;
+
+ /*
+ * Transmit definitions
+ *
+ * We have an array of num_tx_desc descriptors (handled
+ * by the controller) paired with an array of tx_buffers
+ * (at tx_buffer_area).
+ * The index of the next available descriptor is next_avail_tx_desc.
+ * The number of remaining tx_desc is num_tx_desc_avail.
+ */
+ struct em_dma_alloc txdma; /* bus_dma glue for tx desc */
+ struct e1000_tx_desc *tx_desc_base;
+ uint32_t next_avail_tx_desc;
+ uint32_t next_tx_to_clean;
+ volatile uint16_t num_tx_desc_avail;
+ uint16_t num_tx_desc;
+ uint16_t last_hw_offload;
+ uint32_t txd_cmd;
+ struct em_buffer *tx_buffer_area;
+ bus_dma_tag_t txtag; /* dma tag for tx */
+ uint32_t tx_tso; /* last tx was tso */
+
+ /*
+ * Receive definitions
+ *
+ * we have an array of num_rx_desc rx_desc (handled by the
+ * controller), and paired with an array of rx_buffers
+ * (at rx_buffer_area).
+ * The next pair to check on receive is at offset next_rx_desc_to_check
+ */
+ struct em_dma_alloc rxdma; /* bus_dma glue for rx desc */
+ struct e1000_rx_desc *rx_desc_base;
+ uint32_t next_rx_desc_to_check;
+ uint32_t rx_buffer_len;
+ uint16_t num_rx_desc;
+ int rx_process_limit;
+ struct em_buffer *rx_buffer_area;
+ bus_dma_tag_t rxtag;
+ bus_dmamap_t rx_sparemap;
+
+ /*
+ * First/last mbuf pointers, for
+ * collecting multisegment RX packets.
+ */
+ struct mbuf *fmp;
+ struct mbuf *lmp;
+
+ /* Misc stats maintained by the driver */
+ unsigned long dropped_pkts;
+ unsigned long mbuf_alloc_failed;
+ unsigned long mbuf_cluster_failed;
+ unsigned long no_tx_desc_avail1;
+ unsigned long no_tx_desc_avail2;
+ unsigned long no_tx_map_avail;
+ unsigned long no_tx_dma_setup;
+ unsigned long watchdog_events;
+ unsigned long rx_overruns;
+ unsigned long rx_irq;
+ unsigned long tx_irq;
+ unsigned long link_irq;
+
+ /* 82547 workaround */
+ uint32_t tx_fifo_size;
+ uint32_t tx_fifo_head;
+ uint32_t tx_fifo_head_addr;
+ uint64_t tx_fifo_reset_cnt;
+ uint64_t tx_fifo_wrk_cnt;
+ uint32_t tx_head_addr;
+
+ /* For 82544 PCIX Workaround */
+ boolean_t pcix_82544;
+ boolean_t in_detach;
+
+
+ struct e1000_hw_stats stats;
+};
+
+/* ******************************************************************************
+ * vendor_info_array
+ *
+ * This array contains the list of Subvendor/Subdevice IDs on which the driver
+ * should load.
+ *
+ * ******************************************************************************/
+typedef struct _em_vendor_info_t {
+ unsigned int vendor_id;
+ unsigned int device_id;
+ unsigned int subvendor_id;
+ unsigned int subdevice_id;
+ unsigned int index;
+} em_vendor_info_t;
+
+struct em_buffer {
+ int next_eop; /* Index of the desc to watch */
+ struct mbuf *m_head;
+ bus_dmamap_t map; /* bus_dma map for packet */
+};
+
+/* For 82544 PCIX Workaround */
+typedef struct _ADDRESS_LENGTH_PAIR
+{
+ uint64_t address;
+ uint32_t length;
+} ADDRESS_LENGTH_PAIR, *PADDRESS_LENGTH_PAIR;
+
+typedef struct _DESCRIPTOR_PAIR
+{
+ ADDRESS_LENGTH_PAIR descriptor[4];
+ uint32_t elements;
+} DESC_ARRAY, *PDESC_ARRAY;
+
+#define EM_CORE_LOCK_INIT(_sc, _name) \
+ mtx_init(&(_sc)->core_mtx, _name, "EM Core Lock", MTX_DEF)
+#define EM_TX_LOCK_INIT(_sc, _name) \
+ mtx_init(&(_sc)->tx_mtx, _name, "EM TX Lock", MTX_DEF)
+#define EM_RX_LOCK_INIT(_sc, _name) \
+ mtx_init(&(_sc)->rx_mtx, _name, "EM RX Lock", MTX_DEF)
+#define EM_CORE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->core_mtx)
+#define EM_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx)
+#define EM_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rx_mtx)
+#define EM_CORE_LOCK(_sc) mtx_lock(&(_sc)->core_mtx)
+#define EM_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx)
+#define EM_TX_TRYLOCK(_sc) mtx_trylock(&(_sc)->tx_mtx)
+#define EM_RX_LOCK(_sc) mtx_lock(&(_sc)->rx_mtx)
+#define EM_CORE_UNLOCK(_sc) mtx_unlock(&(_sc)->core_mtx)
+#define EM_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->tx_mtx)
+#define EM_RX_UNLOCK(_sc) mtx_unlock(&(_sc)->rx_mtx)
+#define EM_CORE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->core_mtx, MA_OWNED)
+#define EM_TX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->tx_mtx, MA_OWNED)
+
+#endif /* _LEM_H_DEFINED_ */