diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 94e651d..8d8d88e 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -32,6 +32,7 @@ ident GENERIC makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols +options DEVICE_POLLING options SCHED_ULE # ULE scheduler options PREEMPTION # Enable kernel thread preemption options INET # InterNETworking diff --git a/sys/conf/files b/sys/conf/files index 94c5a5f..a901dcb 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -920,6 +920,8 @@ dev/e1000/e1000_82575.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_ich8lan.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" +dev/e1000/e1000_i210.c optional em | igb \ + compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_api.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_mac.c optional em | igb \ @@ -2164,7 +2166,6 @@ kern/kern_ntptime.c standard kern/kern_osd.c standard kern/kern_physio.c standard kern/kern_pmc.c standard -kern/kern_poll.c optional device_polling kern/kern_priv.c standard kern/kern_proc.c standard kern/kern_prot.c standard @@ -2428,6 +2429,7 @@ net/if_vlan.c optional vlan net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression net/netisr.c standard +net/netisr_pollif.c optional device_polling net/pfil.c optional ether | inet net/radix.c standard net/radix_mpath.c standard diff --git a/sys/dev/e1000/e1000_82541.c b/sys/dev/e1000/e1000_82541.c index d13611e..781aa93 100644 --- a/sys/dev/e1000/e1000_82541.c +++ b/sys/dev/e1000/e1000_82541.c @@ -642,7 +642,7 @@ static s32 e1000_check_for_link_82541(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. diff --git a/sys/dev/e1000/e1000_82543.c b/sys/dev/e1000/e1000_82543.c index 1507da5..1c01658 100644 --- a/sys/dev/e1000/e1000_82543.c +++ b/sys/dev/e1000/e1000_82543.c @@ -1126,7 +1126,7 @@ static s32 e1000_setup_copper_link_82543(struct e1000_hw *hw) DEBUGOUT("Valid link established!!!\n"); /* Config the MAC and PHY after link is up */ if (hw->mac.type == e1000_82544) { - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); } else { ret_val = e1000_config_mac_to_phy_82543(hw); if (ret_val) @@ -1160,7 +1160,7 @@ static s32 e1000_setup_fiber_link_82543(struct e1000_hw *hw) /* Take the link out of reset */ ctrl &= ~E1000_CTRL_LRST; - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); ret_val = e1000_commit_fc_settings_generic(hw); if (ret_val) @@ -1259,7 +1259,7 @@ static s32 e1000_check_for_copper_link_82543(struct e1000_hw *hw) * settings. */ if (mac->type == e1000_82544) - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); else { ret_val = e1000_config_mac_to_phy_82543(hw); if (ret_val) { @@ -1433,7 +1433,7 @@ static s32 e1000_config_mac_to_phy_82543(struct e1000_hw *hw) if (phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD; - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); /* * Set up speed in the Device Control register depending on diff --git a/sys/dev/e1000/e1000_82571.c b/sys/dev/e1000/e1000_82571.c index a2982b9..30015c1 100644 --- a/sys/dev/e1000/e1000_82571.c +++ b/sys/dev/e1000/e1000_82571.c @@ -1907,7 +1907,7 @@ void e1000_set_laa_state_82571(struct e1000_hw *hw, bool state) * incoming packets directed to this port are dropped. * Eventually the LAA will be in RAR[0] and RAR[14]. */ - e1000_rar_set_generic(hw, hw->mac.addr, + hw->mac.ops.rar_set(hw, hw->mac.addr, hw->mac.rar_entry_count - 1); return; } diff --git a/sys/dev/e1000/e1000_82575.c b/sys/dev/e1000/e1000_82575.c index 75fe0a4..a04e3fa 100644 --- a/sys/dev/e1000/e1000_82575.c +++ b/sys/dev/e1000/e1000_82575.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -38,9 +38,12 @@ * 82575GB Gigabit Network Connection * 82576 Gigabit Network Connection * 82576 Quad Port Gigabit Mezzanine Adapter + * 82580 Gigabit Network Connection + * I350 Gigabit Network Connection */ #include "e1000_api.h" +#include "e1000_i210.h" static s32 e1000_init_phy_params_82575(struct e1000_hw *hw); static s32 e1000_init_mac_params_82575(struct e1000_hw *hw); @@ -162,6 +165,9 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) DEBUGFUNC("e1000_init_phy_params_82575"); + phy->ops.read_i2c_byte = e1000_read_i2c_byte_generic; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_generic; + if (hw->phy.media_type != e1000_media_type_copper) { phy->type = e1000_phy_none; goto out; @@ -195,12 +201,22 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) if (e1000_sgmii_active_82575(hw) && !e1000_sgmii_uses_mdio_82575(hw)) { 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.read_reg = e1000_read_phy_reg_82580; - phy->ops.write_reg = e1000_write_phy_reg_82580; } else { - phy->ops.read_reg = e1000_read_phy_reg_igp; - phy->ops.write_reg = e1000_write_phy_reg_igp; + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + phy->ops.read_reg = e1000_read_phy_reg_82580; + phy->ops.write_reg = e1000_write_phy_reg_82580; + break; + case e1000_i210: + case e1000_i211: + phy->ops.read_reg = e1000_read_phy_reg_gs40g; + phy->ops.write_reg = e1000_write_phy_reg_gs40g; + break; + default: + phy->ops.read_reg = e1000_read_phy_reg_igp; + phy->ops.write_reg = e1000_write_phy_reg_igp; + } } /* Set phy->phy_addr and phy->id. */ @@ -245,6 +261,15 @@ static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; break; + case I210_I_PHY_ID: + phy->type = e1000_phy_i210; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.get_info = e1000_get_phy_info_m88; + phy->ops.get_cable_length = e1000_get_cable_length_m88_gen2; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + break; default: ret_val = -E1000_ERR_PHY; goto out; @@ -281,28 +306,32 @@ s32 e1000_init_nvm_params_82575(struct e1000_hw *hw) size = 15; nvm->word_size = 1 << size; - nvm->opcode_bits = 8; - nvm->delay_usec = 1; - switch (nvm->override) { - case e1000_nvm_override_spi_large: - nvm->page_size = 32; - nvm->address_bits = 16; - break; - case e1000_nvm_override_spi_small: - nvm->page_size = 8; - nvm->address_bits = 8; - break; - default: - nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; - nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; - break; - } - - nvm->type = e1000_nvm_eeprom_spi; - - if (nvm->word_size == (1 << 15)) - nvm->page_size = 128; + if (hw->mac.type < e1000_i210) { + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? + 16 : 8; + break; + } + if (nvm->word_size == (1 << 15)) + nvm->page_size = 128; + nvm->type = e1000_nvm_eeprom_spi; + } else { + nvm->type = e1000_nvm_flash_hw; + } /* Function Pointers */ nvm->ops.acquire = e1000_acquire_nvm_82575; nvm->ops.release = e1000_release_nvm_82575; @@ -316,7 +345,7 @@ s32 e1000_init_nvm_params_82575(struct e1000_hw *hw) nvm->ops.update = e1000_update_nvm_checksum_generic; nvm->ops.valid_led_default = e1000_valid_led_default_82575; - /* override genric family function pointers for specific descendants */ + /* override generic family function pointers for specific descendants */ switch (hw->mac.type) { case e1000_82580: nvm->ops.validate = e1000_validate_nvm_checksum_82580; @@ -368,8 +397,7 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) 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; + !!(E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK); /* Function pointers */ @@ -394,8 +422,6 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) 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 */ @@ -428,6 +454,13 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82575; /* link info */ mac->ops.get_link_up_info = e1000_get_link_up_info_82575; + /* acquire SW_FW sync */ + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_82575; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_82575; + if (mac->type >= e1000_i210) { + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_i210; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_i210; + } /* set lan id for port to determine which phy lock to use */ hw->mac.ops.set_lan_id(hw); @@ -470,7 +503,7 @@ static s32 e1000_acquire_phy_82575(struct e1000_hw *hw) else if (hw->bus.func == E1000_FUNC_3) mask = E1000_SWFW_PHY3_SM; - return e1000_acquire_swfw_sync_82575(hw, mask); + return hw->mac.ops.acquire_swfw_sync(hw, mask); } /** @@ -492,7 +525,7 @@ static void e1000_release_phy_82575(struct e1000_hw *hw) else if (hw->bus.func == E1000_FUNC_3) mask = E1000_SWFW_PHY3_SM; - e1000_release_swfw_sync_82575(hw, mask); + hw->mac.ops.release_swfw_sync(hw, mask); } /** @@ -796,7 +829,7 @@ static s32 e1000_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val = E1000_SUCCESS; - u16 data; + u32 data; DEBUGFUNC("e1000_set_d0_lplu_state_82580"); @@ -844,7 +877,7 @@ s32 e1000_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active) { struct e1000_phy_info *phy = &hw->phy; s32 ret_val = E1000_SUCCESS; - u16 data; + u32 data; DEBUGFUNC("e1000_set_d3_lplu_state_82580"); @@ -918,11 +951,7 @@ static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw) } - switch (hw->mac.type) { - default: - ret_val = e1000_acquire_nvm_generic(hw); - } - + ret_val = e1000_acquire_nvm_generic(hw); if (ret_val) e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); @@ -941,10 +970,8 @@ static void e1000_release_nvm_82575(struct e1000_hw *hw) { DEBUGFUNC("e1000_release_nvm_82575"); - switch (hw->mac.type) { - default: - e1000_release_nvm_generic(hw); - } + e1000_release_nvm_generic(hw); + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); } @@ -1058,7 +1085,7 @@ static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw) 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) && + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) && (hw->phy.type == e1000_phy_igp_3)) e1000_phy_init_script_igp3(hw); @@ -1115,6 +1142,7 @@ static s32 e1000_check_for_link_82575(struct e1000_hw *hw) * continue to check for link. */ hw->mac.get_link_status = !hw->mac.serdes_has_link; + } else { ret_val = e1000_check_for_copper_link_generic(hw); } @@ -1168,11 +1196,6 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, DEBUGFUNC("e1000_get_pcs_speed_and_duplex_82575"); - /* Set up defaults for the return values of this function */ - mac->serdes_has_link = FALSE; - *speed = 0; - *duplex = 0; - /* * Read the PCS Status register for link state. For non-copper mode, * the status register is not accurate. The PCS status register is @@ -1181,11 +1204,9 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, pcs = E1000_READ_REG(hw, E1000_PCS_LSTAT); /* - * The link up bit determines when link is up on autoneg. The sync ok - * gets set once both sides sync up and agree upon link. Stable link - * can be determined by checking for both link up and link sync ok + * The link up bit determines when link is up on autoneg. */ - if ((pcs & E1000_PCS_LSTS_LINK_OK) && (pcs & E1000_PCS_LSTS_SYNK_OK)) { + if (pcs & E1000_PCS_LSTS_LINK_OK) { mac->serdes_has_link = TRUE; /* Detect and store PCS speed */ @@ -1201,6 +1222,10 @@ static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, *duplex = FULL_DUPLEX; else *duplex = HALF_DUPLEX; + } else { + mac->serdes_has_link = FALSE; + *speed = 0; + *duplex = 0; } return E1000_SUCCESS; @@ -1293,7 +1318,7 @@ static s32 e1000_reset_hw_82575(struct e1000_hw *hw) } /* If EEPROM is not present, run manual init scripts */ - if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES)) e1000_reset_init_script_82575(hw); /* Clear any pending interrupt events. */ @@ -1396,6 +1421,7 @@ static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw) } } switch (hw->phy.type) { + case e1000_phy_i210: case e1000_phy_m88: if (hw->phy.id == I347AT4_E_PHY_ID || hw->phy.id == M88E1112_E_PHY_ID || @@ -1605,31 +1631,28 @@ static s32 e1000_get_media_type_82575(struct e1000_hw *hw) } /* Read Init Control Word #3*/ hw->nvm.ops.read(hw, init_ctrl_wd_3_offset, 1, &init_ctrl_wd_3); + + /* + * Align link mode bits to + * their CTRL_EXT location. + */ current_link_mode = init_ctrl_wd_3; + current_link_mode <<= (E1000_CTRL_EXT_LINK_MODE_OFFSET - + init_ctrl_wd_3_bit_offset); + current_link_mode &= E1000_CTRL_EXT_LINK_MODE_MASK; + /* * Switch to CSR for all but internal PHY. */ - if ((init_ctrl_wd_3 << (E1000_CTRL_EXT_LINK_MODE_OFFSET - - init_ctrl_wd_3_bit_offset)) != - E1000_CTRL_EXT_LINK_MODE_GMII) { - current_link_mode = ctrl_ext; - init_ctrl_wd_3_bit_offset = - E1000_CTRL_EXT_LINK_MODE_OFFSET; - } + if (current_link_mode != E1000_CTRL_EXT_LINK_MODE_GMII) + /* Take link mode from CSR */ + current_link_mode = ctrl_ext & + E1000_CTRL_EXT_LINK_MODE_MASK; } else { /* Take link mode from CSR */ - current_link_mode = ctrl_ext; - init_ctrl_wd_3_bit_offset = E1000_CTRL_EXT_LINK_MODE_OFFSET; + current_link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK; } - /* - * Align link mode bits to - * their CTRL_EXT location. - */ - current_link_mode <<= (E1000_CTRL_EXT_LINK_MODE_OFFSET - - init_ctrl_wd_3_bit_offset); - current_link_mode &= E1000_CTRL_EXT_LINK_MODE_MASK; - switch (current_link_mode) { case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: @@ -2331,7 +2354,7 @@ static s32 e1000_reset_hw_82580(struct e1000_hw *hw) msec_delay(10); /* Determine whether or not a global dev reset is requested */ - if (global_device_reset && e1000_acquire_swfw_sync_82575(hw, + if (global_device_reset && hw->mac.ops.acquire_swfw_sync(hw, swmbsw_mask)) global_device_reset = FALSE; @@ -2359,7 +2382,7 @@ static s32 e1000_reset_hw_82580(struct e1000_hw *hw) } /* If EEPROM is not present, run manual init scripts */ - if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES)) e1000_reset_init_script_82575(hw); /* clear global device reset status bit */ @@ -2378,7 +2401,7 @@ static s32 e1000_reset_hw_82580(struct e1000_hw *hw) /* Release semaphore */ if (global_device_reset) - e1000_release_swfw_sync_82575(hw, swmbsw_mask); + hw->mac.ops.release_swfw_sync(hw, swmbsw_mask); return ret_val; } @@ -2538,7 +2561,7 @@ static s32 e1000_update_nvm_checksum_82580(struct e1000_hw *hw) goto out; } - if ((nvm_data & NVM_COMPATIBILITY_BIT_MASK) == 0) { + if (!(nvm_data & NVM_COMPATIBILITY_BIT_MASK)) { /* set compatibility bit to validate checksums appropriately */ nvm_data = nvm_data | NVM_COMPATIBILITY_BIT_MASK; ret_val = hw->nvm.ops.write(hw, NVM_COMPATIBILITY_REG_3, 1, @@ -2737,6 +2760,7 @@ s32 e1000_set_i2c_bb(struct e1000_hw *hw) * e1000_read_i2c_byte_generic - Reads 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to read + * @dev_addr: device address * @data: value read * * Performs byte read operation over I2C interface at @@ -2750,14 +2774,14 @@ s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, u32 retry = 1; u16 swfw_mask = 0; - bool nack = 1; + bool nack = TRUE; DEBUGFUNC("e1000_read_i2c_byte_generic"); swfw_mask = E1000_SWFW_PHY0_SM; do { - if (e1000_acquire_swfw_sync_82575(hw, swfw_mask) + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS) { status = E1000_ERR_SWFW_SYNC; goto read_byte_out; @@ -2805,7 +2829,7 @@ s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, break; fail: - e1000_release_swfw_sync_82575(hw, swfw_mask); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); msec_delay(100); e1000_i2c_bus_clear(hw); retry++; @@ -2816,7 +2840,7 @@ fail: } while (retry < max_retry); - e1000_release_swfw_sync_82575(hw, swfw_mask); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); read_byte_out: @@ -2827,6 +2851,7 @@ read_byte_out: * e1000_write_i2c_byte_generic - Writes 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to write + * @dev_addr: device address * @data: value to write * * Performs byte write operation over I2C interface at @@ -2844,7 +2869,7 @@ s32 e1000_write_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, swfw_mask = E1000_SWFW_PHY0_SM; - if (e1000_acquire_swfw_sync_82575(hw, swfw_mask) != E1000_SUCCESS) { + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS) { status = E1000_ERR_SWFW_SYNC; goto write_byte_out; } @@ -2888,7 +2913,7 @@ fail: DEBUGOUT("I2C byte write error.\n"); } while (retry < max_retry); - e1000_release_swfw_sync_82575(hw, swfw_mask); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); write_byte_out: @@ -3020,7 +3045,7 @@ static s32 e1000_get_i2c_ack(struct e1000_hw *hw) u32 i = 0; u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); u32 timeout = 10; - bool ack = 1; + bool ack = TRUE; DEBUGFUNC("e1000_get_i2c_ack"); @@ -3040,7 +3065,7 @@ static s32 e1000_get_i2c_ack(struct e1000_hw *hw) return E1000_ERR_I2C; ack = e1000_get_i2c_data(&i2cctl); - if (ack == 1) { + if (ack) { DEBUGOUT("I2C ack was not received.\n"); status = E1000_ERR_I2C; } diff --git a/sys/dev/e1000/e1000_api.c b/sys/dev/e1000/e1000_api.c index fdfc4bb..4b2a43d 100644 --- a/sys/dev/e1000/e1000_api.c +++ b/sys/dev/e1000/e1000_api.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -323,6 +323,17 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_I350_DA4: mac->type = e1000_i350; break; + case E1000_DEV_ID_I210_COPPER: + case E1000_DEV_ID_I210_COPPER_OEM1: + case E1000_DEV_ID_I210_COPPER_IT: + case E1000_DEV_ID_I210_FIBER: + case E1000_DEV_ID_I210_SERDES: + case E1000_DEV_ID_I210_SGMII: + mac->type = e1000_i210; + break; + case E1000_DEV_ID_I211_COPPER: + mac->type = e1000_i211; + break; case E1000_DEV_ID_82576_VF: mac->type = e1000_vfadapt; break; @@ -425,6 +436,10 @@ s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device) case e1000_i350: e1000_init_function_pointers_82575(hw); break; + case e1000_i210: + case e1000_i211: + e1000_init_function_pointers_i210(hw); + break; case e1000_vfadapt: e1000_init_function_pointers_vf(hw); break; diff --git a/sys/dev/e1000/e1000_api.h b/sys/dev/e1000/e1000_api.h index 70aaa15..4c061c0 100644 --- a/sys/dev/e1000/e1000_api.h +++ b/sys/dev/e1000/e1000_api.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -49,6 +49,7 @@ 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); +extern void e1000_init_function_pointers_i210(struct e1000_hw *hw); s32 e1000_set_mac_type(struct e1000_hw *hw); s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device); @@ -118,6 +119,7 @@ s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length); u32 e1000_translate_register_82542(u32 reg); + /* * TBI_ACCEPT macro definition: * diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h index 69ef386..a4a0ed0 100644 --- a/sys/dev/e1000/e1000_defines.h +++ b/sys/dev/e1000/e1000_defines.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -1344,6 +1344,16 @@ #define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ #define E1000_EECD_SECVAL_SHIFT 22 #define E1000_EECD_SEC1VAL_VALID_MASK (E1000_EECD_AUTO_RD | E1000_EECD_PRES) +#define E1000_EECD_FLUPD_I210 0x00800000 /* Update FLASH */ +#define E1000_EECD_FLUDONE_I210 0x04000000 /* Update FLASH done */ +#define E1000_EECD_FLASH_DETECTED_I210 0x00080000 /* FLASH detected */ +#define E1000_FLUDONE_ATTEMPTS 20000 +#define E1000_EERD_EEWR_MAX_COUNT 512 /* buffered EEPROM words rw */ +#define E1000_I210_FIFO_SEL_RX 0x00 +#define E1000_I210_FIFO_SEL_TX_QAV(_i) (0x02 + (_i)) +#define E1000_I210_FIFO_SEL_TX_LEGACY E1000_I210_FIFO_SEL_TX_QAV(0) +#define E1000_I210_FIFO_SEL_BMC2OS_TX 0x06 +#define E1000_I210_FIFO_SEL_BMC2OS_RX 0x01 #define E1000_NVM_SWDPIN0 0x0001 /* SWDPIN 0 NVM Value */ #define E1000_NVM_LED_LOGIC 0x0020 /* Led Logic Word */ @@ -1361,6 +1371,20 @@ #define NVM_VERSION 0x0005 #define NVM_SERDES_AMPLITUDE 0x0006 /* SERDES output amplitude */ #define NVM_PHY_CLASS_WORD 0x0007 +#define NVM_ETRACK_WORD 0x0042 +#define NVM_COMB_VER_OFF 0x0083 +#define NVM_COMB_VER_PTR 0x003d + +#define NVM_MAC_ADDR 0x0000 +#define NVM_SUB_DEV_ID 0x000B +#define NVM_SUB_VEN_ID 0x000C +#define NVM_DEV_ID 0x000D +#define NVM_VEN_ID 0x000E +#define NVM_INIT_CTRL_2 0x000F +#define NVM_INIT_CTRL_4 0x0013 +#define NVM_LED_1_CFG 0x001C +#define NVM_LED_0_2_CFG 0x001F + #define NVM_INIT_CONTROL1_REG 0x000A #define NVM_INIT_CONTROL2_REG 0x000F #define NVM_SWDEF_PINS_CTRL_PORT_1 0x0010 @@ -1380,12 +1404,12 @@ #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) +#define NVM_82580_LAN_FUNC_OFFSET(a) ((a) ? (0x40 + (0x40 * (a))) : 0) /* Mask bits for fields in Word 0x24 of the NVM */ #define NVM_WORD24_COM_MDIO 0x0008 /* MDIO interface shared */ #define NVM_WORD24_EXT_MDIO 0x0004 /* MDIO accesses routed extrnl */ -/* Offset of Link Mode bits for 82575 up to Kawela */ +/* Offset of Link Mode bits for 82575/82576 */ #define NVM_WORD24_LNK_MODE_OFFSET 8 /* Offset of Link Mode bits for 82580 up */ #define NVM_WORD24_82580_LNK_MODE_OFFSET 4 @@ -1525,6 +1549,7 @@ #define I82579_E_PHY_ID 0x01540090 #define I82580_I_PHY_ID 0x015403A0 #define I350_I_PHY_ID 0x015403B0 +#define I210_I_PHY_ID 0x01410C00 #define IGP04E1000_E_PHY_ID 0x02A80391 #define M88_VENDOR 0x0141 @@ -1787,6 +1812,8 @@ #define E1000_DMACR_DMAC_LX_MASK 0x30000000 #define E1000_DMACR_DMAC_LX_SHIFT 28 #define E1000_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */ +/* DMA Coalescing BMC-to-OS Watchdog Enable */ +#define E1000_DMACR_DC_BMC2OSW_EN 0x00008000 /* DMA Coalescing Transmit Threshold */ #define E1000_DMCTXTH_DMCTTHR_MASK 0x00000FFF @@ -1807,8 +1834,9 @@ /* Lx power decision based on DMA coal */ #define E1000_PCIEMISC_LX_DECISION 0x00000080 -#define E1000_LTRC_EEEMS_EN 0x00000005 /* Enable EEE LTR max send */ #define E1000_RXPBS_SIZE_I210_MASK 0x0000003F /* Rx packet buffer size */ +#define E1000_TXPB0S_SIZE_I210_MASK 0x0000003F /* Tx packet buffer 0 size */ +#define E1000_LTRC_EEEMS_EN 0x00000020 /* Enable EEE LTR max send */ /* Minimum time for 1000BASE-T where no data will be transmit following move out * of EEE LPI Tx state */ @@ -1826,12 +1854,14 @@ #define E1000_LTRMINV_SCALE_1024 2 /* Reg val to set scale to 32768 nsec */ #define E1000_LTRMINV_SCALE_32768 3 +#define E1000_LTRMINV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */ #define E1000_LTRMAXV_SCALE_MASK 0x00001C00 /* LTR maximum scale */ #define E1000_LTRMAXV_SCALE_SHIFT 10 /* Reg val to set scale to 1024 nsec */ #define E1000_LTRMAXV_SCALE_1024 2 /* Reg val to set scale to 32768 nsec */ #define E1000_LTRMAXV_SCALE_32768 3 +#define E1000_LTRMAXV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */ #define E1000_DOBFFCTL_OBFFTHR_MASK 0x000000FF /* OBFF threshold */ #define E1000_DOBFFCTL_EXIT_ACT_MASK 0x01000000 /* Exit active CB */ diff --git a/sys/dev/e1000/e1000_hw.h b/sys/dev/e1000/e1000_hw.h index 16cfa8f..50a5ce2 100644 --- a/sys/dev/e1000/e1000_hw.h +++ b/sys/dev/e1000/e1000_hw.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -154,6 +154,13 @@ struct e1000_hw; #define E1000_DEV_ID_I350_SERDES 0x1523 #define E1000_DEV_ID_I350_SGMII 0x1524 #define E1000_DEV_ID_I350_DA4 0x1546 +#define E1000_DEV_ID_I210_COPPER 0x1533 +#define E1000_DEV_ID_I210_COPPER_OEM1 0x1534 +#define E1000_DEV_ID_I210_COPPER_IT 0x1535 +#define E1000_DEV_ID_I210_FIBER 0x1536 +#define E1000_DEV_ID_I210_SERDES 0x1537 +#define E1000_DEV_ID_I210_SGMII 0x1538 +#define E1000_DEV_ID_I211_COPPER 0x1539 #define E1000_DEV_ID_DH89XXCC_SGMII 0x0438 #define E1000_DEV_ID_DH89XXCC_SERDES 0x043A #define E1000_DEV_ID_DH89XXCC_BACKPLANE 0x043C @@ -203,6 +210,8 @@ enum e1000_mac_type { e1000_82576, e1000_82580, e1000_i350, + e1000_i210, + e1000_i211, e1000_vfadapt, e1000_vfadapt_i350, e1000_num_macs /* List is 1-based, so subtract 1 for TRUE count. */ @@ -248,6 +257,7 @@ enum e1000_phy_type { e1000_phy_82579, e1000_phy_82580, e1000_phy_vf, + e1000_phy_i210, }; enum e1000_bus_type { @@ -674,6 +684,8 @@ struct e1000_mac_operations { struct e1000_host_mng_command_header*); s32 (*mng_enable_host_if)(struct e1000_hw *); s32 (*wait_autoneg)(struct e1000_hw *); + s32 (*acquire_swfw_sync)(struct e1000_hw *, u16); + void (*release_swfw_sync)(struct e1000_hw *, u16); }; /* @@ -911,13 +923,13 @@ struct e1000_dev_spec_ich8lan { E1000_MUTEX nvm_mutex; E1000_MUTEX swflag_mutex; bool nvm_k1_enabled; - int eee_disable; + bool eee_disable; }; struct e1000_dev_spec_82575 { bool sgmii_active; bool global_device_reset; - int eee_disable; + bool eee_disable; bool module_plugged; u32 mtu; }; @@ -967,6 +979,7 @@ struct e1000_hw { #include "e1000_80003es2lan.h" #include "e1000_ich8lan.h" #include "e1000_82575.h" +#include "e1000_i210.h" /* These functions must be implemented by drivers */ void e1000_pci_clear_mwi(struct e1000_hw *hw); diff --git a/sys/dev/e1000/e1000_i210.c b/sys/dev/e1000/e1000_i210.c new file mode 100644 index 0000000..f8e8bad --- /dev/null +++ b/sys/dev/e1000/e1000_i210.c @@ -0,0 +1,740 @@ +/****************************************************************************** + + Copyright (c) 2001-2012, 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$*/ + +#include "e1000_api.h" + + +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw); +static void e1000_release_nvm_i210(struct e1000_hw *hw); +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw); +static void e1000_put_hw_semaphore_i210(struct e1000_hw *hw); +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw); +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data); +static s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); + +/** + * e1000_acquire_nvm_i210 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclusive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_i210"); + + ret_val = e1000_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); + + return ret_val; +} + +/** + * e1000_release_nvm_i210 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +static void e1000_release_nvm_i210(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_i210"); + + e1000_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_i210 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ + + DEBUGFUNC("e1000_acquire_swfw_sync_i210"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_i210(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & fwmask)) + break; + + /* + * Firmware currently using resource (fwmask) + */ + e1000_put_hw_semaphore_i210(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_i210(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_i210 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_i210"); + + while (e1000_get_hw_semaphore_i210(hw) != E1000_SUCCESS) + ; /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_i210(hw); +} + +/** + * e1000_get_hw_semaphore_i210 - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + s32 ret_val = E1000_SUCCESS; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_i210"); + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_put_hw_semaphore_i210 - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + **/ +static void e1000_put_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + + DEBUGFUNC("e1000_put_hw_semaphore_i210"); + + swsm = E1000_READ_REG(hw, E1000_SWSM); + + swsm &= ~E1000_SWSM_SWESMBI; + + E1000_WRITE_REG(hw, E1000_SWSM, swsm); +} + +/** + * e1000_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the Shadow Ram to read + * @words: number of words to read + * @data: word read from the Shadow Ram + * + * Reads a 16 bit word from the Shadow Ram using the EERD register. + * Uses necessary synchronization semaphores. + **/ +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_read_nvm_srrd_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to read in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_read_nvm_eerd(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow RAM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow RAM + * + * Writes data to Shadow RAM at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * data will not be committed to FLASH and also Shadow RAM will most likely + * contain an invalid checksum. + * + * If error code is returned, data and Shadow RAM may be inconsistent - buffer + * partially written. + **/ +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_write_nvm_srwr_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to write in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_write_nvm_srwr(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr - Write to Shadow Ram using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow Ram to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow Ram + * + * Writes data to Shadow Ram at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * Shadow Ram will most likely contain an invalid checksum. + **/ +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, k, eewr = 0; + u32 attempts = 100000; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_nvm_srwr"); + + /* + * A check for invalid values: offset too large, too many words, + * too many words for the offset, and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + for (i = 0; i < words; i++) { + eewr = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | + (data[i] << E1000_NVM_RW_REG_DATA) | + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_SRWR, eewr); + + for (k = 0; k < attempts; k++) { + if (E1000_NVM_RW_REG_DONE & + E1000_READ_REG(hw, E1000_SRWR)) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("Shadow RAM write EEWR timed out\n"); + break; + } + } + +out: + return ret_val; +} + +/** + * e1000_read_nvm_i211 - Read NVM wrapper function for I211 + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Wrapper function to return data formerly found in the NVM. + **/ +static s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_nvm_i211"); + + /* Only the MAC addr is required to be present in the iNVM */ + switch (offset) { + case NVM_MAC_ADDR: + ret_val = e1000_read_invm_i211(hw, (u8)offset, &data[0]); + ret_val |= e1000_read_invm_i211(hw, (u8)offset+1, &data[1]); + ret_val |= e1000_read_invm_i211(hw, (u8)offset+2, &data[2]); + if (ret_val != E1000_SUCCESS) + DEBUGOUT("MAC Addr not found in iNVM\n"); + break; + case NVM_ID_LED_SETTINGS: + case NVM_INIT_CTRL_2: + case NVM_INIT_CTRL_4: + case NVM_LED_1_CFG: + case NVM_LED_0_2_CFG: + e1000_read_invm_i211(hw, (u8)offset, data); + break; + case NVM_COMPAT: + *data = ID_LED_DEFAULT_I210; + break; + case NVM_SUB_DEV_ID: + *data = hw->subsystem_device_id; + break; + case NVM_SUB_VEN_ID: + *data = hw->subsystem_vendor_id; + break; + case NVM_DEV_ID: + *data = hw->device_id; + break; + case NVM_VEN_ID: + *data = hw->vendor_id; + break; + default: + DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset); + *data = NVM_RESERVED_WORD; + break; + } + return ret_val; +} + +/** + * e1000_read_invm_i211 - Reads OTP + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Reads 16-bit words from the OTP. Return error when the word is not + * stored in OTP. + **/ +s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data) +{ + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u32 invm_dword; + u16 i; + u8 record_type, word_address; + + DEBUGFUNC("e1000_read_invm_i211"); + + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); + /* Get record type */ + record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword); + if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE) + break; + if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE) + i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE) + i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) { + word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword); + if (word_address == address) { + *data = INVM_DWORD_TO_WORD_DATA(invm_dword); + DEBUGOUT2("Read INVM Word 0x%02x = %x", + address, *data); + status = E1000_SUCCESS; + break; + } + } + } + if (status != E1000_SUCCESS) + DEBUGOUT1("Requested word 0x%02x not found in OTP\n", address); + return status; +} + +/** + * e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 status = E1000_SUCCESS; + s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *); + + DEBUGFUNC("e1000_validate_nvm_checksum_i210"); + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + + /* + * Replace the read function with semaphore grabbing with + * the one that skips this for a while. + * We have semaphore taken already here. + */ + read_op_ptr = hw->nvm.ops.read; + hw->nvm.ops.read = e1000_read_nvm_eerd; + + status = e1000_validate_nvm_checksum_generic(hw); + + /* Revert original read operation. */ + hw->nvm.ops.read = read_op_ptr; + + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + return status; +} + + +/** + * e1000_update_nvm_checksum_i210 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. Next commit EEPROM data onto the Flash. + **/ +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum_i210"); + + /* + * Read the first word from the EEPROM. If this times out or fails, do + * not continue or we could be in for a very long wait while every + * EEPROM read fails + */ + ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data); + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("EEPROM read failed\n"); + goto out; + } + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + /* + * Do not use hw->nvm.ops.write, hw->nvm.ops.read + * because we do not want to take the synchronization + * semaphores twice here. + */ + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data); + if (ret_val) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, + &checksum); + if (ret_val != E1000_SUCCESS) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Write Error while updating checksum.\n"); + goto out; + } + + hw->nvm.ops.release(hw); + + ret_val = e1000_update_flash_i210(hw); + } else { + ret_val = E1000_ERR_SWFW_SYNC; + } +out: + return ret_val; +} + +/** + * e1000_get_flash_presence_i210 - Check if flash device is detected. + * @hw: pointer to the HW structure + * + **/ +static bool e1000_get_flash_presence_i210(struct e1000_hw *hw) +{ + u32 eec = 0; + bool ret_val = FALSE; + + DEBUGFUNC("e1000_get_flash_presence_i210"); + + eec = E1000_READ_REG(hw, E1000_EECD); + + if (eec & E1000_EECD_FLASH_DETECTED_I210) + ret_val = TRUE; + + return ret_val; +} + +/** + * e1000_update_flash_i210 - Commit EEPROM to the flash + * @hw: pointer to the HW structure + * + **/ +s32 e1000_update_flash_i210(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u32 flup; + + DEBUGFUNC("e1000_update_flash_i210"); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == -E1000_ERR_NVM) { + DEBUGOUT("Flash update time out\n"); + goto out; + } + + flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I210; + E1000_WRITE_REG(hw, E1000_EECD, flup); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == E1000_SUCCESS) + DEBUGOUT("Flash update complete\n"); + else + DEBUGOUT("Flash update time out\n"); + +out: + return ret_val; +} + +/** + * e1000_pool_flash_update_done_i210 - Pool FLUDONE status. + * @hw: pointer to the HW structure + * + **/ +s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw) +{ + s32 ret_val = -E1000_ERR_NVM; + u32 i, reg; + + DEBUGFUNC("e1000_pool_flash_update_done_i210"); + + for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { + reg = E1000_READ_REG(hw, E1000_EECD); + if (reg & E1000_EECD_FLUDONE_I210) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + return ret_val; +} + +/** + * e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers + * @hw: pointer to the HW structure + * + * Initialize the i210 NVM parameters and function pointers. + **/ +static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + struct e1000_nvm_info *nvm = &hw->nvm; + + DEBUGFUNC("e1000_init_nvm_params_i210"); + + ret_val = e1000_init_nvm_params_82575(hw); + + nvm->ops.acquire = e1000_acquire_nvm_i210; + nvm->ops.release = e1000_release_nvm_i210; + nvm->ops.read = e1000_read_nvm_srrd_i210; + nvm->ops.write = e1000_write_nvm_srwr_i210; + nvm->ops.valid_led_default = e1000_valid_led_default_i210; + nvm->ops.validate = e1000_validate_nvm_checksum_i210; + nvm->ops.update = e1000_update_nvm_checksum_i210; + + return ret_val; +} + +/** + * e1000_init_nvm_params_i211 - Initialize i211 NVM function pointers + * @hw: pointer to the HW structure + * + * Initialize the NVM parameters and function pointers for i211. + **/ +static s32 e1000_init_nvm_params_i211(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + + DEBUGFUNC("e1000_init_nvm_params_i211"); + + nvm->ops.acquire = e1000_acquire_nvm_i210; + nvm->ops.release = e1000_release_nvm_i210; + nvm->ops.read = e1000_read_nvm_i211; + nvm->ops.valid_led_default = e1000_valid_led_default_i210; + nvm->ops.write = e1000_null_write_nvm; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.update = e1000_null_ops_generic; + + return E1000_SUCCESS; +} + +/** + * e1000_init_function_pointers_i210 - Init func ptrs. + * @hw: pointer to the HW structure + * + * Called to initialize all function pointers and parameters. + **/ +void e1000_init_function_pointers_i210(struct e1000_hw *hw) +{ + e1000_init_function_pointers_82575(hw); + + switch (hw->mac.type) { + case e1000_i210: + if (e1000_get_flash_presence_i210(hw)) + hw->nvm.ops.init_params = e1000_init_nvm_params_i210; + else + hw->nvm.ops.init_params = e1000_init_nvm_params_i211; + break; + case e1000_i211: + hw->nvm.ops.init_params = e1000_init_nvm_params_i211; + break; + default: + break; + } + return; +} + +/** + * e1000_valid_led_default_i210 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_i210"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { + switch (hw->phy.media_type) { + case e1000_media_type_internal_serdes: + *data = ID_LED_DEFAULT_I210_SERDES; + break; + case e1000_media_type_copper: + default: + *data = ID_LED_DEFAULT_I210; + break; + } + } +out: + return ret_val; +} diff --git a/sys/dev/e1000/e1000_i210.h b/sys/dev/e1000/e1000_i210.h new file mode 100644 index 0000000..a0cd935 --- /dev/null +++ b/sys/dev/e1000/e1000_i210.h @@ -0,0 +1,80 @@ +/****************************************************************************** + + Copyright (c) 2001-2012, 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 _E1000_I210_H_ +#define _E1000_I210_H_ + +s32 e1000_update_flash_i210(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data); +s32 e1000_check_for_copper_link_i210(struct e1000_hw *hw); +s32 e1000_set_ltr_i210(struct e1000_hw *hw, bool link); +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); + +#define E1000_STM_OPCODE 0xDB00 +#define E1000_EEPROM_FLASH_SIZE_WORD 0x11 + +#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \ + (u8)((invm_dword) & 0x7) +#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \ + (u8)(((invm_dword) & 0x0000FE00) >> 9) +#define INVM_DWORD_TO_WORD_DATA(invm_dword) \ + (u16)(((invm_dword) & 0xFFFF0000) >> 16) + +enum E1000_INVM_STRUCTURE_TYPE { + E1000_INVM_UNINITIALIZED_STRUCTURE = 0x00, + E1000_INVM_WORD_AUTOLOAD_STRUCTURE = 0x01, + E1000_INVM_CSR_AUTOLOAD_STRUCTURE = 0x02, + E1000_INVM_PHY_REGISTER_AUTOLOAD_STRUCTURE = 0x03, + E1000_INVM_RSA_KEY_SHA256_STRUCTURE = 0x04, + E1000_INVM_INVALIDATED_STRUCTURE = 0x0F, +}; + +#define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS 8 +#define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS 1 + +#define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_OFF2)) +#define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) + +#endif diff --git a/sys/dev/e1000/e1000_ich8lan.c b/sys/dev/e1000/e1000_ich8lan.c index aa4802c..98aefc4 100644 --- a/sys/dev/e1000/e1000_ich8lan.c +++ b/sys/dev/e1000/e1000_ich8lan.c @@ -738,7 +738,7 @@ static s32 e1000_check_for_copper_link_ich8lan(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); + hw->mac.ops.config_collision_dist(hw); /* * Configure Flow Control now that Auto-Neg has completed. diff --git a/sys/dev/e1000/e1000_mac.c b/sys/dev/e1000/e1000_mac.c index 4fc2a7c..95d6873 100644 --- a/sys/dev/e1000/e1000_mac.c +++ b/sys/dev/e1000/e1000_mac.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -36,6 +36,8 @@ static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw); static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw); +static void e1000_config_collision_dist_generic(struct e1000_hw *hw); +static void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); /** * e1000_init_mac_ops_generic - Initialize MAC function pointers @@ -395,30 +397,30 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &nvm_data); if (ret_val) - goto out; + return ret_val; /* not supported on older hardware or 82573 */ if ((hw->mac.type < e1000_82571) || (hw->mac.type == e1000_82573)) - goto out; + return E1000_SUCCESS; /* * Alternate MAC address is handled by the option ROM for 82580 * and newer. SW support not required. */ if (hw->mac.type >= e1000_82580) - goto out; + return E1000_SUCCESS; ret_val = hw->nvm.ops.read(hw, NVM_ALT_MAC_ADDR_PTR, 1, &nvm_alt_mac_addr_offset); if (ret_val) { DEBUGOUT("NVM Read Error\n"); - goto out; + return ret_val; } if ((nvm_alt_mac_addr_offset == 0xFFFF) || (nvm_alt_mac_addr_offset == 0x0000)) /* There is no Alternate MAC Address */ - goto out; + return E1000_SUCCESS; if (hw->bus.func == E1000_FUNC_1) nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1; @@ -432,7 +434,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data); if (ret_val) { DEBUGOUT("NVM Read Error\n"); - goto out; + return ret_val; } alt_mac_addr[i] = (u8)(nvm_data & 0xFF); @@ -442,7 +444,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) /* if multicast bit is set, the alternate address will not be used */ if (alt_mac_addr[0] & 0x01) { DEBUGOUT("Ignoring Alternate Mac Address with MC bit set\n"); - goto out; + return E1000_SUCCESS; } /* @@ -452,8 +454,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) */ hw->mac.ops.rar_set(hw, alt_mac_addr, 0); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -465,7 +466,7 @@ out: * Sets the receive address array register at index to the address passed * in by addr. **/ -void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) +static void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; @@ -496,43 +497,6 @@ void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) } /** - * e1000_update_mc_addr_list_generic - Update Multicast addresses - * @hw: pointer to the HW structure - * @mc_addr_list: array of multicast addresses to program - * @mc_addr_count: number of multicast addresses to program - * - * Updates entire Multicast Table Array. - * The caller must have a packed mc_addr_list of multicast addresses. - **/ -void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, - u8 *mc_addr_list, u32 mc_addr_count) -{ - u32 hash_value, hash_bit, hash_reg; - int i; - - DEBUGFUNC("e1000_update_mc_addr_list_generic"); - - /* clear mta_shadow */ - memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); - - /* update mta_shadow from mc_addr_list */ - for (i = 0; (u32) i < mc_addr_count; i++) { - hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list); - - hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); - hash_bit = hash_value & 0x1F; - - hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); - mc_addr_list += (ETH_ADDR_LEN); - } - - /* replace the entire MTA table */ - for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) - E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]); - E1000_WRITE_FLUSH(hw); -} - -/** * e1000_hash_mc_addr_generic - Generate a multicast hash value * @hw: pointer to the HW structure * @mc_addr: pointer to a multicast address @@ -605,6 +569,43 @@ u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr) } /** + * e1000_update_mc_addr_list_generic - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates entire Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 hash_value, hash_bit, hash_reg; + int i; + + DEBUGFUNC("e1000_update_mc_addr_list_generic"); + + /* clear mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* update mta_shadow from mc_addr_list */ + for (i = 0; (u32) i < mc_addr_count; i++) { + hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list); + + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + mc_addr_list += (ETH_ADDR_LEN); + } + + /* replace the entire MTA table */ + for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]); + E1000_WRITE_FLUSH(hw); +} + +/** * e1000_pcix_mmrbc_workaround_generic - Fix incorrect MMRBC value * @hw: pointer to the HW structure * @@ -712,10 +713,8 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw) * 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; - } + if (!mac->get_link_status) + return E1000_SUCCESS; /* * First we want to see if the MII Status Register reports @@ -724,10 +723,10 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw) */ ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) - goto out; + return ret_val; if (!link) - goto out; /* No link detected */ + return E1000_SUCCESS; /* No link detected */ mac->get_link_status = FALSE; @@ -741,10 +740,8 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *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; - } + if (!mac->autoneg) + return -E1000_ERR_CONFIG; /* * Auto-Neg is enabled. Auto Speed Detection takes care @@ -763,7 +760,6 @@ s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw) if (ret_val) DEBUGOUT("Error configuring flow control\n"); -out: return ret_val; } @@ -780,7 +776,7 @@ s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) u32 rxcw; u32 ctrl; u32 status; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_check_for_fiber_link_generic"); @@ -797,11 +793,11 @@ s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) * was just plugged in. The autoneg_failed flag does this. */ /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ - if ((ctrl & E1000_CTRL_SWDPIN1) && (!(status & E1000_STATUS_LU)) && - (!(rxcw & E1000_RXCW_C))) { - if (mac->autoneg_failed == 0) { - mac->autoneg_failed = 1; - goto out; + if ((ctrl & E1000_CTRL_SWDPIN1) && !(status & E1000_STATUS_LU) && + !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = TRUE; + return E1000_SUCCESS; } DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); @@ -817,7 +813,7 @@ s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) ret_val = e1000_config_fc_after_link_up_generic(hw); if (ret_val) { DEBUGOUT("Error configuring flow control\n"); - goto out; + return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { /* @@ -833,8 +829,7 @@ s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) mac->serdes_has_link = TRUE; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -850,7 +845,7 @@ s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) u32 rxcw; u32 ctrl; u32 status; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_check_for_serdes_link_generic"); @@ -866,10 +861,10 @@ s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) * time to complete. */ /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ - if ((!(status & E1000_STATUS_LU)) && (!(rxcw & E1000_RXCW_C))) { - if (mac->autoneg_failed == 0) { - mac->autoneg_failed = 1; - goto out; + if (!(status & E1000_STATUS_LU) && !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = TRUE; + return E1000_SUCCESS; } DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); @@ -885,7 +880,7 @@ s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) ret_val = e1000_config_fc_after_link_up_generic(hw); if (ret_val) { DEBUGOUT("Error configuring flow control\n"); - goto out; + return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { /* @@ -943,8 +938,48 @@ s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) } } -out: - return ret_val; + return E1000_SUCCESS; +} + +/** + * e1000_set_default_fc_generic - Set flow control default values + * @hw: pointer to the HW structure + * + * Read the EEPROM for the default values for flow control and store the + * values. + **/ +s32 e1000_set_default_fc_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 nvm_data; + + DEBUGFUNC("e1000_set_default_fc_generic"); + + /* + * Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data); + + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (!(nvm_data & NVM_WORD0F_PAUSE_MASK)) + hw->fc.requested_mode = e1000_fc_none; + else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == + NVM_WORD0F_ASM_DIR) + hw->fc.requested_mode = e1000_fc_tx_pause; + else + hw->fc.requested_mode = e1000_fc_full; + + return E1000_SUCCESS; } /** @@ -959,7 +994,7 @@ out: **/ s32 e1000_setup_link_generic(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_setup_link_generic"); @@ -967,8 +1002,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 (e1000_check_reset_block(hw)) - goto out; + if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) + return E1000_SUCCESS; /* * If requested flow control is set to default, set flow control @@ -977,7 +1012,7 @@ s32 e1000_setup_link_generic(struct e1000_hw *hw) if (hw->fc.requested_mode == e1000_fc_default) { ret_val = e1000_set_default_fc_generic(hw); if (ret_val) - goto out; + return ret_val; } /* @@ -992,7 +1027,7 @@ s32 e1000_setup_link_generic(struct e1000_hw *hw) /* Call the necessary media_type subroutine to configure the link. */ ret_val = hw->mac.ops.setup_physical_interface(hw); if (ret_val) - goto out; + return ret_val; /* * Initialize the flow control address, type, and PAUSE timer @@ -1007,87 +1042,80 @@ s32 e1000_setup_link_generic(struct e1000_hw *hw) E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time); - ret_val = e1000_set_fc_watermarks_generic(hw); - -out: - return ret_val; + return e1000_set_fc_watermarks_generic(hw); } /** - * e1000_setup_fiber_serdes_link_generic - Setup link for fiber/serdes + * e1000_commit_fc_settings_generic - Configure flow control * @hw: pointer to the HW structure * - * Configures collision distance and flow control for fiber and serdes - * links. Upon successful setup, poll for link. + * Write the flow control settings to the Transmit Config Word Register (TXCW) + * base on the flow control settings in e1000_mac_info. **/ -s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw) +s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) { struct e1000_mac_info *mac = &hw->mac; - u32 ctrl; - s32 ret_val = E1000_SUCCESS; - - DEBUGFUNC("e1000_setup_fiber_serdes_link_generic"); - - ctrl = E1000_READ_REG(hw, E1000_CTRL); - - /* Take the link out of reset */ - ctrl &= ~E1000_CTRL_LRST; - - mac->ops.config_collision_dist(hw); - - ret_val = e1000_commit_fc_settings_generic(hw); - if (ret_val) - goto out; - - /* - * Since auto-negotiation is enabled, take the link out of reset (the - * link will be in reset, because we previously reset the chip). This - * will restart auto-negotiation. If auto-negotiation is successful - * then the link-up status bit will be set and the flow control enable - * bits (RFCE and TFCE) will be set according to their negotiated value. - */ - DEBUGOUT("Auto-negotiation enabled\n"); + u32 txcw; - E1000_WRITE_REG(hw, E1000_CTRL, ctrl); - E1000_WRITE_FLUSH(hw); - msec_delay(1); + DEBUGFUNC("e1000_commit_fc_settings_generic"); /* - * For these adapters, the SW definable pin 1 is set when the optics - * detect a signal. If we have a signal, then poll for a "Link-Up" - * indication. + * Check for a software override of the flow control settings, and + * setup the device accordingly. If auto-negotiation is enabled, then + * software will have to set the "PAUSE" bits to the correct value in + * the Transmit Config Word Register (TXCW) and re-start auto- + * negotiation. However, if auto-negotiation is disabled, then + * software will have to manually configure the two flow control enable + * bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we + * do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. */ - if (hw->phy.media_type == e1000_media_type_internal_serdes || - (E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { - ret_val = e1000_poll_fiber_serdes_link_generic(hw); - } else { - DEBUGOUT("No signal detected\n"); + switch (hw->fc.current_mode) { + case e1000_fc_none: + /* Flow control completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* + * 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 + * PAUSE. Later, we will disable the adapter's ability to send + * PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* + * Tx Flow control is enabled, and Rx Flow control is disabled, + * by a software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* + * Flow control (both Rx and Tx) is enabled by a software + * over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; } -out: - return ret_val; -} - -/** - * e1000_config_collision_dist_generic - Configure collision distance - * @hw: pointer to the HW structure - * - * Configures the collision distance to the default value and is used - * during link setup. - **/ -void e1000_config_collision_dist_generic(struct e1000_hw *hw) -{ - u32 tctl; - - DEBUGFUNC("e1000_config_collision_dist_generic"); - - tctl = E1000_READ_REG(hw, E1000_TCTL); - - tctl &= ~E1000_TCTL_COLD; - tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + E1000_WRITE_REG(hw, E1000_TXCW, txcw); + mac->txcw = txcw; - E1000_WRITE_REG(hw, E1000_TCTL, tctl); - E1000_WRITE_FLUSH(hw); + return E1000_SUCCESS; } /** @@ -1101,7 +1129,7 @@ s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) { struct e1000_mac_info *mac = &hw->mac; u32 i, status; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_poll_fiber_serdes_link_generic"); @@ -1120,7 +1148,7 @@ s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) } if (i == FIBER_LINK_UP_LIMIT) { DEBUGOUT("Never got a valid link from auto-neg!!!\n"); - mac->autoneg_failed = 1; + mac->autoneg_failed = TRUE; /* * AutoNeg failed to achieve a link, so we'll call * mac->check_for_link. This routine will force the @@ -1130,95 +1158,93 @@ s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) ret_val = mac->ops.check_for_link(hw); if (ret_val) { DEBUGOUT("Error while checking for link\n"); - goto out; + return ret_val; } - mac->autoneg_failed = 0; + mac->autoneg_failed = FALSE; } else { - mac->autoneg_failed = 0; + mac->autoneg_failed = FALSE; DEBUGOUT("Valid Link Found\n"); } -out: - return ret_val; + return E1000_SUCCESS; } /** - * e1000_commit_fc_settings_generic - Configure flow control + * e1000_setup_fiber_serdes_link_generic - Setup link for fiber/serdes * @hw: pointer to the HW structure * - * Write the flow control settings to the Transmit Config Word Register (TXCW) - * base on the flow control settings in e1000_mac_info. + * Configures collision distance and flow control for fiber and serdes + * links. Upon successful setup, poll for link. **/ -s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw) { - struct e1000_mac_info *mac = &hw->mac; - u32 txcw; - s32 ret_val = E1000_SUCCESS; + u32 ctrl; + s32 ret_val; - DEBUGFUNC("e1000_commit_fc_settings_generic"); + DEBUGFUNC("e1000_setup_fiber_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Take the link out of reset */ + ctrl &= ~E1000_CTRL_LRST; + + hw->mac.ops.config_collision_dist(hw); + + ret_val = e1000_commit_fc_settings_generic(hw); + if (ret_val) + return ret_val; /* - * Check for a software override of the flow control settings, and - * setup the device accordingly. If auto-negotiation is enabled, then - * software will have to set the "PAUSE" bits to the correct value in - * the Transmit Config Word Register (TXCW) and re-start auto- - * negotiation. However, if auto-negotiation is disabled, then - * software will have to manually configure the two flow control enable - * bits in the CTRL register. - * - * The possible values of the "fc" parameter are: - * 0: Flow control is completely disabled - * 1: Rx flow control is enabled (we can receive pause frames, - * but not send pause frames). - * 2: Tx flow control is enabled (we can send pause frames but we - * do not support receiving pause frames). - * 3: Both Rx and Tx flow control (symmetric) are enabled. + * Since auto-negotiation is enabled, take the link out of reset (the + * link will be in reset, because we previously reset the chip). This + * will restart auto-negotiation. If auto-negotiation is successful + * then the link-up status bit will be set and the flow control enable + * bits (RFCE and TFCE) will be set according to their negotiated value. */ - switch (hw->fc.current_mode) { - case e1000_fc_none: - /* Flow control completely disabled by a software over-ride. */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); - break; - case e1000_fc_rx_pause: - /* - * 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 - * PAUSE. Later, we will disable the adapter's ability to send - * PAUSE frames. - */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); - break; - case e1000_fc_tx_pause: - /* - * Tx Flow control is enabled, and Rx Flow control is disabled, - * by a software over-ride. - */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); - break; - case e1000_fc_full: - /* - * Flow control (both Rx and Tx) is enabled by a software - * over-ride. - */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); - break; - default: - DEBUGOUT("Flow control param set incorrectly\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; - break; - } + DEBUGOUT("Auto-negotiation enabled\n"); - E1000_WRITE_REG(hw, E1000_TXCW, txcw); - mac->txcw = txcw; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + + /* + * For these adapters, the SW definable pin 1 is set when the optics + * detect a signal. If we have a signal, then poll for a "Link-Up" + * indication. + */ + if (hw->phy.media_type == e1000_media_type_internal_serdes || + (E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { + ret_val = e1000_poll_fiber_serdes_link_generic(hw); + } else { + DEBUGOUT("No signal detected\n"); + } -out: return ret_val; } /** + * e1000_config_collision_dist_generic - 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_generic(struct e1000_hw *hw) +{ + u32 tctl; + + DEBUGFUNC("e1000_config_collision_dist_generic"); + + tctl = E1000_READ_REG(hw, E1000_TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/** * e1000_set_fc_watermarks_generic - Set flow control high/low watermarks * @hw: pointer to the HW structure * @@ -1258,48 +1284,6 @@ s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw) } /** - * e1000_set_default_fc_generic - Set flow control default values - * @hw: pointer to the HW structure - * - * Read the EEPROM for the default values for flow control and store the - * values. - **/ -s32 e1000_set_default_fc_generic(struct e1000_hw *hw) -{ - s32 ret_val = E1000_SUCCESS; - u16 nvm_data; - - DEBUGFUNC("e1000_set_default_fc_generic"); - - /* - * Read and store word 0x0F of the EEPROM. This word contains bits - * that determine the hardware's default PAUSE (flow control) mode, - * a bit that determines whether the HW defaults to enabling or - * disabling auto-negotiation, and the direction of the - * SW defined pins. If there is no SW over-ride of the flow - * control setting, then the variable hw->fc will - * be initialized based on a value in the EEPROM. - */ - ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data); - - if (ret_val) { - DEBUGOUT("NVM Read Error\n"); - goto out; - } - - if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == 0) - hw->fc.requested_mode = e1000_fc_none; - else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == - NVM_WORD0F_ASM_DIR) - hw->fc.requested_mode = e1000_fc_tx_pause; - else - hw->fc.requested_mode = e1000_fc_full; - -out: - return ret_val; -} - -/** * e1000_force_mac_fc_generic - Force the MAC's flow control settings * @hw: pointer to the HW structure * @@ -1312,7 +1296,6 @@ out: s32 e1000_force_mac_fc_generic(struct e1000_hw *hw) { u32 ctrl; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_force_mac_fc_generic"); @@ -1355,14 +1338,12 @@ s32 e1000_force_mac_fc_generic(struct e1000_hw *hw) break; default: DEBUGOUT("Flow control param set incorrectly\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } E1000_WRITE_REG(hw, E1000_CTRL, ctrl); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1400,7 +1381,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) if (ret_val) { DEBUGOUT("Error forcing flow control settings\n"); - goto out; + return ret_val; } /* @@ -1417,14 +1398,14 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) */ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); if (ret_val) - goto out; + return ret_val; if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); - goto out; + return ret_val; } /* @@ -1437,11 +1418,11 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg); if (ret_val) - goto out; + return ret_val; ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY, &mii_nway_lp_ability_reg); if (ret_val) - goto out; + return ret_val; /* * Two bits in the Auto Negotiation Advertisement Register @@ -1540,7 +1521,7 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex); if (ret_val) { DEBUGOUT("Error getting link speed and duplex\n"); - goto out; + return ret_val; } if (duplex == HALF_DUPLEX) @@ -1553,12 +1534,11 @@ s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) ret_val = e1000_force_mac_fc_generic(hw); if (ret_val) { DEBUGOUT("Error forcing flow control settings\n"); - goto out; + return ret_val; } } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1629,7 +1609,6 @@ s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw) { u32 swsm; - s32 ret_val = E1000_SUCCESS; s32 timeout = hw->nvm.word_size + 1; s32 i = 0; @@ -1647,8 +1626,7 @@ s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw) if (i == timeout) { DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); - ret_val = -E1000_ERR_NVM; - goto out; + return -E1000_ERR_NVM; } /* Get the FW semaphore. */ @@ -1667,12 +1645,10 @@ s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw) /* Release semaphores */ e1000_put_hw_semaphore_generic(hw); DEBUGOUT("Driver can't access the NVM\n"); - ret_val = -E1000_ERR_NVM; - goto out; + return -E1000_ERR_NVM; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1703,7 +1679,6 @@ void e1000_put_hw_semaphore_generic(struct e1000_hw *hw) s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw) { s32 i = 0; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_get_auto_rd_done_generic"); @@ -1716,12 +1691,10 @@ s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw) if (i == AUTO_READ_DONE_TIMEOUT) { DEBUGOUT("Auto read by HW from NVM has not completed.\n"); - ret_val = -E1000_ERR_RESET; - goto out; + return -E1000_ERR_RESET; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1741,14 +1714,13 @@ s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data) ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); if (ret_val) { DEBUGOUT("NVM Read Error\n"); - goto out; + return ret_val; } if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) *data = ID_LED_DEFAULT; -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1770,7 +1742,7 @@ s32 e1000_id_led_init_generic(struct e1000_hw *hw) ret_val = hw->nvm.ops.valid_led_default(hw, &data); if (ret_val) - goto out; + return ret_val; mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL); mac->ledctl_mode1 = mac->ledctl_default; @@ -1814,8 +1786,7 @@ s32 e1000_id_led_init_generic(struct e1000_hw *hw) } } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1828,14 +1799,11 @@ out: s32 e1000_setup_led_generic(struct e1000_hw *hw) { u32 ledctl; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_setup_led_generic"); - if (hw->mac.ops.setup_led != e1000_setup_led_generic) { - ret_val = -E1000_ERR_CONFIG; - goto out; - } + if (hw->mac.ops.setup_led != e1000_setup_led_generic) + return -E1000_ERR_CONFIG; if (hw->phy.media_type == e1000_media_type_fiber) { ledctl = E1000_READ_REG(hw, E1000_LEDCTL); @@ -1850,8 +1818,7 @@ s32 e1000_setup_led_generic(struct e1000_hw *hw) E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1976,7 +1943,7 @@ void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop) DEBUGFUNC("e1000_set_pcie_no_snoop_generic"); if (hw->bus.type != e1000_bus_type_pci_express) - goto out; + return; if (no_snoop) { gcr = E1000_READ_REG(hw, E1000_GCR); @@ -1984,8 +1951,6 @@ void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop) gcr |= no_snoop; E1000_WRITE_REG(hw, E1000_GCR, gcr); } -out: - return; } /** @@ -2003,12 +1968,11 @@ s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw) { u32 ctrl; s32 timeout = MASTER_DISABLE_TIMEOUT; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_disable_pcie_master_generic"); if (hw->bus.type != e1000_bus_type_pci_express) - goto out; + return E1000_SUCCESS; ctrl = E1000_READ_REG(hw, E1000_CTRL); ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; @@ -2024,11 +1988,10 @@ 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; + return -E1000_ERR_MASTER_REQUESTS_PENDING; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -2045,7 +2008,7 @@ void e1000_reset_adaptive_generic(struct e1000_hw *hw) if (!mac->adaptive_ifs) { DEBUGOUT("Not in Adaptive IFS mode!\n"); - goto out; + return; } mac->current_ifs_val = 0; @@ -2056,8 +2019,6 @@ void e1000_reset_adaptive_generic(struct e1000_hw *hw) mac->in_ifs_mode = FALSE; E1000_WRITE_REG(hw, E1000_AIT, 0); -out: - return; } /** @@ -2075,7 +2036,7 @@ void e1000_update_adaptive_generic(struct e1000_hw *hw) if (!mac->adaptive_ifs) { DEBUGOUT("Not in Adaptive IFS mode!\n"); - goto out; + return; } if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) { @@ -2099,8 +2060,6 @@ void e1000_update_adaptive_generic(struct e1000_hw *hw) E1000_WRITE_REG(hw, E1000_AIT, 0); } } -out: - return; } /** @@ -2112,19 +2071,15 @@ out: **/ static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; - DEBUGFUNC("e1000_validate_mdi_setting_generic"); if (!hw->mac.autoneg && (hw->phy.mdix == 0 || hw->phy.mdix == 3)) { DEBUGOUT("Invalid MDI setting detected\n"); hw->phy.mdix = 1; - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -2142,7 +2097,6 @@ s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, u32 offset, u8 data) { u32 i, regvalue = 0; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_write_8bit_ctrl_reg_generic"); @@ -2159,10 +2113,8 @@ s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, } if (!(regvalue & E1000_GEN_CTL_READY)) { DEBUGOUT1("Reg %08x did not indicate ready\n", reg); - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } -out: - return ret_val; + return E1000_SUCCESS; } diff --git a/sys/dev/e1000/e1000_mac.h b/sys/dev/e1000/e1000_mac.h index 11ffab7..c18a7ec 100644 --- a/sys/dev/e1000/e1000_mac.h +++ b/sys/dev/e1000/e1000_mac.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2010, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -64,31 +64,29 @@ void e1000_set_lan_id_single_port(struct e1000_hw *hw); void e1000_set_lan_id_multi_port_pci(struct e1000_hw *hw); s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw); s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, - u16 *duplex); + u16 *duplex); s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, - u16 *speed, u16 *duplex); + u16 *speed, u16 *duplex); s32 e1000_id_led_init_generic(struct e1000_hw *hw); s32 e1000_led_on_generic(struct e1000_hw *hw); s32 e1000_led_off_generic(struct e1000_hw *hw); void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, - u8 *mc_addr_list, u32 mc_addr_count); + u8 *mc_addr_list, u32 mc_addr_count); s32 e1000_set_default_fc_generic(struct e1000_hw *hw); s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw); s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw); s32 e1000_setup_led_generic(struct e1000_hw *hw); s32 e1000_setup_link_generic(struct e1000_hw *hw); s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, - u32 offset, u8 data); + u32 offset, u8 data); u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr); 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_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); s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw); void e1000_reset_adaptive_generic(struct e1000_hw *hw); void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop); diff --git a/sys/dev/e1000/e1000_manage.c b/sys/dev/e1000/e1000_manage.c index 7782739..f11b18d 100644 --- a/sys/dev/e1000/e1000_manage.c +++ b/sys/dev/e1000/e1000_manage.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2010, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -71,23 +71,20 @@ u8 e1000_calculate_checksum(u8 *buffer, u32 length) s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw) { u32 hicr; - s32 ret_val = E1000_SUCCESS; u8 i; DEBUGFUNC("e1000_mng_enable_host_if_generic"); - if (!(hw->mac.arc_subsystem_valid)) { + if (!hw->mac.arc_subsystem_valid) { DEBUGOUT("ARC subsystem not valid.\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* Check that the host interface is enabled. */ hicr = E1000_READ_REG(hw, E1000_HICR); - if ((hicr & E1000_HICR_EN) == 0) { + if (!(hicr & E1000_HICR_EN)) { DEBUGOUT("E1000_HOST_EN bit disabled.\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* check the previous command is completed */ for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { @@ -99,12 +96,10 @@ s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw) if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { DEBUGOUT("Previous command timeout failed .\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -122,7 +117,7 @@ bool e1000_check_mng_mode_generic(struct e1000_hw *hw) return (fwsm & E1000_FWSM_MODE_MASK) == - (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT); + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT); } /** @@ -147,7 +142,7 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw) /* No manageability, no filtering */ if (!hw->mac.ops.check_mng_mode(hw)) { hw->mac.tx_pkt_filtering = FALSE; - goto out; + return hw->mac.tx_pkt_filtering; } /* @@ -157,7 +152,7 @@ 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) { hw->mac.tx_pkt_filtering = FALSE; - goto out; + return hw->mac.tx_pkt_filtering; } /* Read in the header. Length and offset are in dwords. */ @@ -165,11 +160,11 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw) offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2; for (i = 0; i < len; i++) *(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, - offset + i); + offset + i); hdr_csum = hdr->checksum; hdr->checksum = 0; csum = e1000_calculate_checksum((u8 *)hdr, - E1000_MNG_DHCP_COOKIE_LENGTH); + E1000_MNG_DHCP_COOKIE_LENGTH); /* * If either the checksums or signature don't match, then * the cookie area isn't considered valid, in which case we @@ -177,67 +172,17 @@ bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw) */ if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) { hw->mac.tx_pkt_filtering = TRUE; - goto out; + return hw->mac.tx_pkt_filtering; } /* Cookie area is valid, make the final check for filtering. */ - if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) { + if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) hw->mac.tx_pkt_filtering = FALSE; - goto out; - } -out: return hw->mac.tx_pkt_filtering; } /** - * e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface - * @hw: pointer to the HW structure - * @buffer: pointer to the host interface - * @length: size of the buffer - * - * Writes the DHCP information to the host interface. - **/ -s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer, - u16 length) -{ - struct e1000_host_mng_command_header hdr; - s32 ret_val; - u32 hicr; - - DEBUGFUNC("e1000_mng_write_dhcp_info_generic"); - - hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; - hdr.command_length = length; - hdr.reserved1 = 0; - hdr.reserved2 = 0; - hdr.checksum = 0; - - /* Enable the host interface */ - ret_val = hw->mac.ops.mng_enable_host_if(hw); - if (ret_val) - goto out; - - /* Populate the host interface with the contents of "buffer". */ - ret_val = hw->mac.ops.mng_host_if_write(hw, buffer, length, - sizeof(hdr), &(hdr.checksum)); - if (ret_val) - goto out; - - /* Write the manageability command header */ - ret_val = hw->mac.ops.mng_write_cmd_header(hw, &hdr); - if (ret_val) - goto out; - - /* Tell the ARC a new command is pending. */ - hicr = E1000_READ_REG(hw, E1000_HICR); - E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); - -out: - return ret_val; -} - -/** * e1000_mng_write_cmd_header_generic - Writes manageability command header * @hw: pointer to the HW structure * @hdr: pointer to the host interface command header @@ -245,7 +190,7 @@ out: * Writes the command header after does the checksum calculation. **/ s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, - struct e1000_host_mng_command_header *hdr) + struct e1000_host_mng_command_header *hdr) { u16 i, length = sizeof(struct e1000_host_mng_command_header); @@ -259,7 +204,7 @@ s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, /* Write the relevant command block into the ram area. */ for (i = 0; i < length; i++) { E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, - *((u32 *) hdr + i)); + *((u32 *) hdr + i)); E1000_WRITE_FLUSH(hw); } @@ -279,22 +224,19 @@ s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, * way. Also fills up the sum of the buffer in *buffer parameter. **/ s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, - u16 length, u16 offset, u8 *sum) + u16 length, u16 offset, u8 *sum) { u8 *tmp; u8 *bufptr = buffer; u32 data = 0; - s32 ret_val = E1000_SUCCESS; u16 remaining, i, j, prev_bytes; DEBUGFUNC("e1000_mng_host_if_write_generic"); /* sum = only sum of the data and it is not checksum */ - if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) { - ret_val = -E1000_ERR_PARAM; - goto out; - } + if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) + return -E1000_ERR_PARAM; tmp = (u8 *)&data; prev_bytes = offset & 0x3; @@ -328,7 +270,7 @@ s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, } E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, - data); + data); } if (remaining) { for (j = 0; j < sizeof(u32); j++) { @@ -339,11 +281,57 @@ s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, *sum += *(tmp + j); } - E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, data); + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, + data); } -out: - return ret_val; + return E1000_SUCCESS; +} + +/** + * e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer, + u16 length) +{ + struct e1000_host_mng_command_header hdr; + s32 ret_val; + u32 hicr; + + DEBUGFUNC("e1000_mng_write_dhcp_info_generic"); + + hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; + hdr.command_length = length; + hdr.reserved1 = 0; + hdr.reserved2 = 0; + hdr.checksum = 0; + + /* Enable the host interface */ + ret_val = hw->mac.ops.mng_enable_host_if(hw); + if (ret_val) + return ret_val; + + /* Populate the host interface with the contents of "buffer". */ + ret_val = hw->mac.ops.mng_host_if_write(hw, buffer, length, + sizeof(hdr), &(hdr.checksum)); + if (ret_val) + return ret_val; + + /* Write the manageability command header */ + ret_val = hw->mac.ops.mng_write_cmd_header(hw, &hdr); + if (ret_val) + return ret_val; + + /* Tell the ARC a new command is pending. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + return E1000_SUCCESS; } /** @@ -357,17 +345,16 @@ bool e1000_enable_mng_pass_thru(struct e1000_hw *hw) { u32 manc; u32 fwsm, factps; - bool ret_val = FALSE; DEBUGFUNC("e1000_enable_mng_pass_thru"); if (!hw->mac.asf_firmware_present) - goto out; + return FALSE; manc = E1000_READ_REG(hw, E1000_MANC); if (!(manc & E1000_MANC_RCV_TCO_EN)) - goto out; + return FALSE; if (hw->mac.has_fwsm) { fwsm = E1000_READ_REG(hw, E1000_FWSM); @@ -375,10 +362,8 @@ bool e1000_enable_mng_pass_thru(struct e1000_hw *hw) if (!(factps & E1000_FACTPS_MNGCG) && ((fwsm & E1000_FWSM_MODE_MASK) == - (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) { - ret_val = TRUE; - goto out; - } + (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) + return TRUE; } else if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) { u16 data; @@ -388,18 +373,14 @@ bool e1000_enable_mng_pass_thru(struct e1000_hw *hw) if (!(factps & E1000_FACTPS_MNGCG) && ((data & E1000_NVM_INIT_CTRL2_MNGM) == - (e1000_mng_mode_pt << 13))) { - ret_val = TRUE; - goto out; - } + (e1000_mng_mode_pt << 13))) + return TRUE; } else if ((manc & E1000_MANC_SMBUS_EN) && - !(manc & E1000_MANC_ASF_EN)) { - ret_val = TRUE; - goto out; + !(manc & E1000_MANC_ASF_EN)) { + return TRUE; } -out: - return ret_val; + return FALSE; } /** @@ -414,33 +395,30 @@ out: s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length) { u32 hicr, i; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_host_interface_command"); if (!(hw->mac.arc_subsystem_valid)) { DEBUGOUT("Hardware doesn't support host interface command.\n"); - goto out; + return E1000_SUCCESS; } if (!hw->mac.asf_firmware_present) { DEBUGOUT("Firmware is not present.\n"); - goto out; + return E1000_SUCCESS; } if (length == 0 || length & 0x3 || length > E1000_HI_MAX_BLOCK_BYTE_LENGTH) { DEBUGOUT("Buffer length failure.\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* Check that the host interface is enabled. */ hicr = E1000_READ_REG(hw, E1000_HICR); - if ((hicr & E1000_HICR_EN) == 0) { + if (!(hicr & E1000_HICR_EN)) { DEBUGOUT("E1000_HOST_EN bit disabled.\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* Calculate length in DWORDs */ @@ -451,10 +429,8 @@ s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length) * into the ram area. */ for (i = 0; i < length; i++) - E1000_WRITE_REG_ARRAY_DWORD(hw, - E1000_HOST_IF, - i, - *((u32 *)buffer + i)); + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, + *((u32 *)buffer + i)); /* Setting this bit tells the ARC that a new command is pending. */ E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); @@ -470,16 +446,134 @@ s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length) if (i == E1000_HI_COMMAND_TIMEOUT || (!(E1000_READ_REG(hw, E1000_HICR) & E1000_HICR_SV))) { DEBUGOUT("Command has failed with no status valid.\n"); - ret_val = -E1000_ERR_HOST_INTERFACE_COMMAND; - goto out; + return -E1000_ERR_HOST_INTERFACE_COMMAND; } for (i = 0; i < length; i++) *((u32 *)buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, - E1000_HOST_IF, - i); + E1000_HOST_IF, + i); + + return E1000_SUCCESS; +} +/** + * e1000_load_firmware - Writes proxy FW code buffer to host interface + * and execute. + * @hw: pointer to the HW structure + * @buffer: contains a firmware to write + * @length: the byte length of the buffer, must be multiple of 4 bytes + * + * Upon success returns E1000_SUCCESS, returns E1000_ERR_CONFIG if not enabled + * in HW else returns E1000_ERR_HOST_INTERFACE_COMMAND. + **/ +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length) +{ + u32 hicr, hibba, fwsm, icr, i; + + DEBUGFUNC("e1000_load_firmware"); + + if (hw->mac.type < e1000_i210) { + DEBUGOUT("Hardware doesn't support loading FW by the driver\n"); + return -E1000_ERR_CONFIG; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + if (!(hicr & E1000_HICR_MEMORY_BASE_EN)) { + DEBUGOUT("E1000_HICR_MEMORY_BASE_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + + if (length == 0 || length & 0x3 || length > E1000_HI_FW_MAX_LENGTH) { + DEBUGOUT("Buffer length failure.\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + /* Clear notification from ROM-FW by reading ICR register */ + icr = E1000_READ_REG(hw, E1000_ICR_V2); + + /* Reset ROM-FW */ + hicr = E1000_READ_REG(hw, E1000_HICR); + hicr |= E1000_HICR_FW_RESET_ENABLE; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + hicr |= E1000_HICR_FW_RESET; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + E1000_WRITE_FLUSH(hw); + + /* Wait till MAC notifies about its readiness after ROM-FW reset */ + for (i = 0; i < (E1000_HI_COMMAND_TIMEOUT * 2); i++) { + icr = E1000_READ_REG(hw, E1000_ICR_V2); + if (icr & E1000_ICR_MNG) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Wait till MAC is ready to accept new FW code */ + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + if ((fwsm & E1000_FWSM_FW_VALID) && + ((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT == + E1000_FWSM_HI_EN_ONLY_MODE)) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Calculate length in DWORDs */ + length >>= 2; + + /* + * The device driver writes the relevant FW code block + * into the ram area in DWORDs via 1kB ram addressing window. + */ + for (i = 0; i < length; i++) { + if (!(i % E1000_HI_FW_BLOCK_DWORD_LENGTH)) { + /* Point to correct 1kB ram window */ + hibba = E1000_HI_FW_BASE_ADDRESS + + ((E1000_HI_FW_BLOCK_DWORD_LENGTH << 2) * + (i / E1000_HI_FW_BLOCK_DWORD_LENGTH)); + + E1000_WRITE_REG(hw, E1000_HIBBA, hibba); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, + i % E1000_HI_FW_BLOCK_DWORD_LENGTH, + *((u32 *)buffer + i)); + } -out: - return ret_val; + /* Setting this bit tells the ARC that a new FW is ready to execute. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay(1); + } + + /* Check for successful FW start. */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("New FW did not start within timeout period.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + return E1000_SUCCESS; } + diff --git a/sys/dev/e1000/e1000_manage.h b/sys/dev/e1000/e1000_manage.h index 6a0c2a0..51f1767 100644 --- a/sys/dev/e1000/e1000_manage.h +++ b/sys/dev/e1000/e1000_manage.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2010, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -39,14 +39,15 @@ bool e1000_check_mng_mode_generic(struct e1000_hw *hw); bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw); s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw); s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, - u16 length, u16 offset, u8 *sum); + u16 length, u16 offset, u8 *sum); s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, - struct e1000_host_mng_command_header *hdr); + struct e1000_host_mng_command_header *hdr); s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, - u8 *buffer, u16 length); + u8 *buffer, u16 length); bool e1000_enable_mng_pass_thru(struct e1000_hw *hw); u8 e1000_calculate_checksum(u8 *buffer, u32 length); s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length); +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length); enum e1000_mng_mode { e1000_mng_mode_none = 0, @@ -56,35 +57,40 @@ enum e1000_mng_mode { e1000_mng_mode_host_if_only }; -#define E1000_FACTPS_MNGCG 0x20000000 +#define E1000_FACTPS_MNGCG 0x20000000 -#define E1000_FWSM_MODE_MASK 0xE -#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_MODE_MASK 0xE +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 +#define E1000_FWSM_HI_EN_ONLY_MODE 0x4 -#define E1000_MNG_IAMT_MODE 0x3 -#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 -#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 -#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 -#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 -#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING 0x1 -#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 +#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING 0x1 +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 -#define E1000_VFTA_ENTRY_SHIFT 5 -#define E1000_VFTA_ENTRY_MASK 0x7F -#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F +#define E1000_VFTA_ENTRY_SHIFT 5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F -#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Num of bytes in range */ -#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Num of dwords in range */ -#define E1000_HI_COMMAND_TIMEOUT 500 /* Process HI command limit */ - -#define E1000_HICR_EN 0x01 /* Enable bit - RO */ +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Num of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Num of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Process HI cmd limit */ +#define E1000_HI_FW_BASE_ADDRESS 0x10000 +#define E1000_HI_FW_MAX_LENGTH (64 * 1024) /* Num of bytes */ +#define E1000_HI_FW_BLOCK_DWORD_LENGTH 256 /* Num of DWORDs per page */ +#define E1000_HICR_MEMORY_BASE_EN 0x200 /* MB Enable bit - RO */ +#define E1000_HICR_EN 0x01 /* Enable bit - RO */ /* Driver sets this bit when done to put command in RAM */ -#define E1000_HICR_C 0x02 -#define E1000_HICR_SV 0x04 /* Status Validity */ -#define E1000_HICR_FW_RESET_ENABLE 0x40 -#define E1000_HICR_FW_RESET 0x80 +#define E1000_HICR_C 0x02 +#define E1000_HICR_SV 0x04 /* Status Validity */ +#define E1000_HICR_FW_RESET_ENABLE 0x40 +#define E1000_HICR_FW_RESET 0x80 /* Intel(R) Active Management Technology signature */ -#define E1000_IAMT_SIGNATURE 0x544D4149 +#define E1000_IAMT_SIGNATURE 0x544D4149 #endif diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h index 7eb7bb6..e6d131a 100644 --- a/sys/dev/e1000/e1000_osdep.h +++ b/sys/dev/e1000/e1000_osdep.h @@ -84,7 +84,8 @@ /* 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) + MTX_NETWORK_LOCK, \ + MTX_DEF | MTX_DUPOK) #define E1000_MUTEX_DESTROY(mutex) mtx_destroy(mutex) #define E1000_MUTEX_LOCK(mutex) mtx_lock(mutex) #define E1000_MUTEX_TRYLOCK(mutex) mtx_trylock(mutex) diff --git a/sys/dev/e1000/e1000_phy.c b/sys/dev/e1000/e1000_phy.c index ead72690..fd253ba 100644 --- a/sys/dev/e1000/e1000_phy.c +++ b/sys/dev/e1000/e1000_phy.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -95,8 +95,8 @@ void e1000_init_phy_ops_generic(struct e1000_hw *hw) phy->ops.write_reg_page = e1000_null_write_reg; phy->ops.power_up = e1000_null_phy_generic; phy->ops.power_down = e1000_null_phy_generic; - phy->ops.read_i2c_byte = e1000_read_i2c_byte_generic; - phy->ops.write_i2c_byte = e1000_write_i2c_byte_generic; + phy->ops.read_i2c_byte = e1000_read_i2c_byte_null; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_null; phy->ops.cfg_on_link_up = e1000_null_ops_generic; } @@ -151,6 +151,36 @@ s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data) } /** + * e1000_read_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value read + * + **/ +s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + DEBUGFUNC("e1000_read_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** + * e1000_write_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value to write + * + **/ +s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + DEBUGFUNC("e1000_write_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** * e1000_check_reset_block_generic - Check if PHY reset is blocked * @hw: pointer to the HW structure * @@ -186,30 +216,30 @@ s32 e1000_get_phy_id(struct e1000_hw *hw) DEBUGFUNC("e1000_get_phy_id"); - if (!(phy->ops.read_reg)) - goto out; + if (!phy->ops.read_reg) + return E1000_SUCCESS; while (retry_count < 2) { ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); if (ret_val) - goto out; + return ret_val; phy->id = (u32)(phy_id << 16); usec_delay(20); ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); if (ret_val) - goto out; + return ret_val; phy->id |= (u32)(phy_id & PHY_REVISION_MASK); phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); if (phy->id != 0 && phy->id != PHY_REVISION_MASK) - goto out; + return E1000_SUCCESS; retry_count++; } -out: - return ret_val; + + return E1000_SUCCESS; } /** @@ -220,21 +250,18 @@ out: **/ s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_phy_reset_dsp_generic"); - if (!(hw->phy.ops.write_reg)) - goto out; + if (!hw->phy.ops.write_reg) + return E1000_SUCCESS; ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1); if (ret_val) - goto out; - - ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0); + return ret_val; -out: - return ret_val; + return hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0); } /** @@ -250,7 +277,6 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_read_phy_reg_mdic"); @@ -283,13 +309,11 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) } if (!(mdic & E1000_MDIC_READY)) { DEBUGOUT("MDI Read did not complete\n"); - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { DEBUGOUT("MDI Error\n"); - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } *data = (u16) mdic; @@ -300,8 +324,7 @@ s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) if (hw->mac.type == e1000_pch2lan) usec_delay(100); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -316,7 +339,6 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; - s32 ret_val = E1000_SUCCESS; DEBUGFUNC("e1000_write_phy_reg_mdic"); @@ -350,13 +372,11 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) } if (!(mdic & E1000_MDIC_READY)) { DEBUGOUT("MDI Write did not complete\n"); - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } if (mdic & E1000_MDIC_ERROR) { DEBUGOUT("MDI Error\n"); - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } /* @@ -366,8 +386,7 @@ s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) if (hw->mac.type == e1000_pch2lan) usec_delay(100); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -621,23 +640,22 @@ s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data) **/ s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_read_phy_reg_m88"); - if (!(hw->phy.ops.acquire)) - goto out; + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release(hw); -out: return ret_val; } @@ -652,23 +670,22 @@ out: **/ s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; DEBUGFUNC("e1000_write_phy_reg_m88"); - if (!(hw->phy.ops.acquire)) - goto out; + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release(hw); -out: return ret_val; } @@ -711,29 +728,25 @@ static s32 __e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, DEBUGFUNC("__e1000_read_phy_reg_igp"); if (!locked) { - if (!(hw->phy.ops.acquire)) - goto out; + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; } - if (offset > MAX_PHY_MULTI_PAGE_REG) { + if (offset > MAX_PHY_MULTI_PAGE_REG) ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); - if (ret_val) - goto release; - } - - ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); - -release: + if (!ret_val) + ret_val = e1000_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); if (!locked) hw->phy.ops.release(hw); -out: + return ret_val; } @@ -784,30 +797,25 @@ static s32 __e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, DEBUGFUNC("e1000_write_phy_reg_igp"); if (!locked) { - if (!(hw->phy.ops.acquire)) - goto out; + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; } - if (offset > MAX_PHY_MULTI_PAGE_REG) { + if (offset > MAX_PHY_MULTI_PAGE_REG) ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); - if (ret_val) - goto release; - } - - ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); - -release: + if (!ret_val) + ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & + offset, + data); if (!locked) hw->phy.ops.release(hw); -out: return ret_val; } @@ -854,17 +862,18 @@ 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"); if (!locked) { - if (!(hw->phy.ops.acquire)) - goto out; + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; } kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & @@ -880,8 +889,7 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, if (!locked) hw->phy.ops.release(hw); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -929,17 +937,18 @@ 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 (!locked) { - if (!(hw->phy.ops.acquire)) - goto out; + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; ret_val = hw->phy.ops.acquire(hw); if (ret_val) - goto out; + return ret_val; } kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & @@ -952,8 +961,7 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, if (!locked) hw->phy.ops.release(hw); -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -985,6 +993,46 @@ s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data) } /** + * e1000_set_master_slave_mode - Setup PHY for Master/slave mode + * @hw: pointer to the HW structure + * + * Sets up Master/slave mode + **/ +static s32 e1000_set_master_slave_mode(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + /* Resolve Master/Slave mode */ + ret_val = hw->phy.ops.read_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->phy.original_ms_type = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : e1000_ms_auto; + + switch (hw->phy.ms_type) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + /* fall-through */ + default: + break; + } + + return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data); +} + +/** * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link * @hw: pointer to the HW structure * @@ -1001,14 +1049,14 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) ret_val = hw->phy.ops.reset(hw); if (ret_val) { DEBUGOUT("Error resetting the PHY.\n"); - goto out; + return ret_val; } } /* Enable CRS on Tx. This must be set for half-duplex operation. */ ret_val = hw->phy.ops.read_reg(hw, I82577_CFG_REG, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data |= I82577_CFG_ASSERT_CRS_ON_TX; @@ -1017,39 +1065,35 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) ret_val = hw->phy.ops.write_reg(hw, I82577_CFG_REG, phy_data); if (ret_val) - goto out; + return ret_val; - /* Resolve Master/Slave mode */ - ret_val = hw->phy.ops.read_reg(hw, PHY_1000T_CTRL, &phy_data); + /* Set MDI/MDIX mode */ + ret_val = hw->phy.ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data); if (ret_val) - goto out; - - /* load defaults for future use */ - hw->phy.original_ms_type = (phy_data & CR_1000T_MS_ENABLE) ? - ((phy_data & CR_1000T_MS_VALUE) ? - e1000_ms_force_master : - e1000_ms_force_slave) : e1000_ms_auto; - - switch (hw->phy.ms_type) { - case e1000_ms_force_master: - phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + return ret_val; + phy_data &= ~I82577_PHY_CTRL2_MDIX_CFG_MASK; + /* + * Options: + * 0 - Auto (default) + * 1 - MDI mode + * 2 - MDI-X mode + */ + switch (hw->phy.mdix) { + case 1: break; - case e1000_ms_force_slave: - phy_data |= CR_1000T_MS_ENABLE; - phy_data &= ~(CR_1000T_MS_VALUE); + case 2: + phy_data |= I82577_PHY_CTRL2_MANUAL_MDIX; break; - case e1000_ms_auto: - phy_data &= ~CR_1000T_MS_ENABLE; + case 0: default: + phy_data |= I82577_PHY_CTRL2_AUTO_MDI_MDIX; break; } - - ret_val = hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data); + ret_val = hw->phy.ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data); if (ret_val) - goto out; + return ret_val; -out: - return ret_val; + return e1000_set_master_slave_mode(hw); } /** @@ -1071,7 +1115,7 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) /* 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; + return ret_val; /* For BM PHY this bit is downshift enable */ if (phy->type != e1000_phy_bm) @@ -1111,16 +1155,32 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) * 1 - Enabled */ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; - if (phy->disable_polarity_correction == 1) + if (phy->disable_polarity_correction) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; /* Enable downshift on BM (disabled by default) */ - if (phy->type == e1000_phy_bm) + if (phy->type == e1000_phy_bm) { + /* For 82574/82583, first disable then enable downshift */ + if (phy->id == BME1000_E_PHY_ID_R2) { + phy_data &= ~BME1000_PSCR_ENABLE_DOWNSHIFT; + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + /* Commit the changes. */ + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + } + phy_data |= BME1000_PSCR_ENABLE_DOWNSHIFT; + } ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; if ((phy->type == e1000_phy_m88) && (phy->revision < E1000_REVISION_4) && @@ -1132,7 +1192,7 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data |= M88E1000_EPSCR_TX_CLK_25; @@ -1151,33 +1211,33 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; } if ((phy->type == e1000_phy_bm) && (phy->id == BME1000_E_PHY_ID_R2)) { /* Set PHY page 0, register 29 to 0x0003 */ ret_val = phy->ops.write_reg(hw, 29, 0x0003); if (ret_val) - goto out; + return ret_val; /* Set PHY page 0, register 30 to 0x0000 */ ret_val = phy->ops.write_reg(hw, 30, 0x0000); if (ret_val) - goto out; + return ret_val; } /* Commit the changes. */ ret_val = phy->ops.commit(hw); if (ret_val) { DEBUGOUT("Error committing the PHY changes\n"); - goto out; + return ret_val; } if (phy->type == e1000_phy_82578) { ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; /* 82578 PHY - set the downshift count to 1x. */ phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE; @@ -1185,11 +1245,16 @@ s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; } -out: - return ret_val; + if (phy->type == e1000_phy_i210) { + ret_val = e1000_set_master_slave_mode(hw); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; } /** @@ -1211,7 +1276,7 @@ s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) /* 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; + return ret_val; /* * Options: @@ -1250,7 +1315,7 @@ s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) * 1 - Enabled */ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; - if (phy->disable_polarity_correction == 1) + if (phy->disable_polarity_correction) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; /* Enable downshift and setting it to X6 */ @@ -1260,17 +1325,16 @@ s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; /* Commit the changes. */ ret_val = phy->ops.commit(hw); if (ret_val) { DEBUGOUT("Error committing the PHY changes\n"); - goto out; + return ret_val; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -1292,7 +1356,7 @@ s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) ret_val = hw->phy.ops.reset(hw); if (ret_val) { DEBUGOUT("Error resetting the PHY.\n"); - goto out; + return ret_val; } /* @@ -1310,7 +1374,7 @@ s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) ret_val = hw->phy.ops.set_d3_lplu_state(hw, FALSE); if (ret_val) { DEBUGOUT("Error Disabling LPLU D3\n"); - goto out; + return ret_val; } } @@ -1319,13 +1383,13 @@ s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) ret_val = hw->phy.ops.set_d0_lplu_state(hw, FALSE); if (ret_val) { DEBUGOUT("Error Disabling LPLU D0\n"); - goto out; + return ret_val; } } /* Configure mdi-mdix settings */ ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data); if (ret_val) - goto out; + return ret_val; data &= ~IGP01E1000_PSCR_AUTO_MDIX; @@ -1343,7 +1407,7 @@ s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) } ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, data); if (ret_val) - goto out; + return ret_val; /* set auto-master slave resolution settings */ if (hw->mac.autoneg) { @@ -1358,125 +1422,29 @@ s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) IGP01E1000_PHY_PORT_CONFIG, &data); if (ret_val) - goto out; + return ret_val; data &= ~IGP01E1000_PSCFR_SMART_SPEED; ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, data); if (ret_val) - goto out; + return ret_val; /* Set auto Master/Slave resolution process */ ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data); if (ret_val) - goto out; + return ret_val; data &= ~CR_1000T_MS_ENABLE; ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data); if (ret_val) - goto out; + return ret_val; } - ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data); - if (ret_val) - goto out; - - /* load defaults for future use */ - phy->original_ms_type = (data & CR_1000T_MS_ENABLE) ? - ((data & CR_1000T_MS_VALUE) ? - e1000_ms_force_master : - e1000_ms_force_slave) : - e1000_ms_auto; - - switch (phy->ms_type) { - case e1000_ms_force_master: - data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); - break; - case e1000_ms_force_slave: - data |= CR_1000T_MS_ENABLE; - data &= ~(CR_1000T_MS_VALUE); - break; - case e1000_ms_auto: - data &= ~CR_1000T_MS_ENABLE; - default: - break; - } - ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data); - if (ret_val) - goto out; + ret_val = e1000_set_master_slave_mode(hw); } -out: - return ret_val; -} - -/** - * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link - * @hw: pointer to the HW structure - * - * Performs initial bounds checking on autoneg advertisement parameter, then - * configure to advertise the full capability. Setup the PHY to autoneg - * and restart the negotiation process between the link partner. If - * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. - **/ -s32 e1000_copper_link_autoneg(struct e1000_hw *hw) -{ - struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; - u16 phy_ctrl; - - DEBUGFUNC("e1000_copper_link_autoneg"); - - /* - * Perform some bounds checking on the autoneg advertisement - * parameter. - */ - phy->autoneg_advertised &= phy->autoneg_mask; - - /* - * If autoneg_advertised is zero, we assume it was not defaulted - * by the calling code so we set to advertise full capability. - */ - if (phy->autoneg_advertised == 0) - phy->autoneg_advertised = phy->autoneg_mask; - - DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); - ret_val = e1000_phy_setup_autoneg(hw); - if (ret_val) { - DEBUGOUT("Error Setting up Auto-Negotiation\n"); - goto out; - } - DEBUGOUT("Restarting Auto-Neg\n"); - - /* - * Restart auto-negotiation by setting the Auto Neg Enable bit and - * the Auto Neg Restart bit in the PHY control register. - */ - ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); - if (ret_val) - goto out; - - phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); - ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); - if (ret_val) - goto out; - - /* - * Does the user want to wait for Auto-Neg to complete here, or - * check at a later time (for example, callback routine). - */ - if (phy->autoneg_wait_to_complete) { - ret_val = hw->mac.ops.wait_autoneg(hw); - if (ret_val) { - DEBUGOUT("Error while waiting for autoneg to complete\n"); - goto out; - } - } - - hw->mac.get_link_status = TRUE; - -out: return ret_val; } @@ -1503,14 +1471,14 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) /* Read the MII Auto-Neg Advertisement Register (Address 4). */ ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); if (ret_val) - goto out; + return ret_val; if (phy->autoneg_mask & ADVERTISE_1000_FULL) { /* Read the MII 1000Base-T Control Register (Address 9). */ ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); if (ret_val) - goto out; + return ret_val; } /* @@ -1624,24 +1592,87 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) break; default: DEBUGOUT("Flow control param set incorrectly\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); if (ret_val) - goto out; + return ret_val; DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); - if (phy->autoneg_mask & ADVERTISE_1000_FULL) { + if (phy->autoneg_mask & ADVERTISE_1000_FULL) ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); - if (ret_val) - goto out; + + return ret_val; +} + +/** + * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link + * @hw: pointer to the HW structure + * + * Performs initial bounds checking on autoneg advertisement parameter, then + * configure to advertise the full capability. Setup the PHY to autoneg + * and restart the negotiation process between the link partner. If + * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. + **/ +s32 e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* + * Perform some bounds checking on the autoneg advertisement + * parameter. + */ + phy->autoneg_advertised &= phy->autoneg_mask; + + /* + * If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (!phy->autoneg_advertised) + phy->autoneg_advertised = phy->autoneg_mask; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* + * Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + /* + * Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (phy->autoneg_wait_to_complete) { + ret_val = hw->mac.ops.wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->mac.get_link_status = TRUE; -out: return ret_val; } @@ -1668,7 +1699,7 @@ s32 e1000_setup_copper_link_generic(struct e1000_hw *hw) */ ret_val = e1000_copper_link_autoneg(hw); if (ret_val) - goto out; + return ret_val; } else { /* * PHY will be set to 10H, 10F, 100H or 100F @@ -1678,7 +1709,7 @@ s32 e1000_setup_copper_link_generic(struct e1000_hw *hw) ret_val = hw->phy.ops.force_speed_duplex(hw); if (ret_val) { DEBUGOUT("Error Forcing Speed and Duplex\n"); - goto out; + return ret_val; } } @@ -1689,17 +1720,16 @@ s32 e1000_setup_copper_link_generic(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, &link); if (ret_val) - goto out; + return ret_val; if (link) { DEBUGOUT("Valid link established!!!\n"); - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); ret_val = e1000_config_fc_after_link_up_generic(hw); } else { DEBUGOUT("Unable to establish link!!!\n"); } -out: return ret_val; } @@ -1722,13 +1752,13 @@ s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); if (ret_val) - goto out; + return ret_val; e1000_phy_force_speed_duplex_setup(hw, &phy_data); ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) - goto out; + return ret_val; /* * Clear Auto-Crossover to force MDI manually. IGP requires MDI @@ -1736,14 +1766,14 @@ s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) */ ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; DEBUGOUT1("IGP PSCR: %X\n", phy_data); @@ -1755,7 +1785,7 @@ s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; if (!link) DEBUGOUT("Link taking longer than expected.\n"); @@ -1763,11 +1793,8 @@ s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) /* Try once more */ ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); - if (ret_val) - goto out; } -out: return ret_val; } @@ -1796,29 +1823,29 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) */ ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data); ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); if (ret_val) - goto out; + return ret_val; e1000_phy_force_speed_duplex_setup(hw, &phy_data); ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) - goto out; + return ret_val; /* Reset the phy to commit changes. */ ret_val = hw->phy.ops.commit(hw); if (ret_val) - goto out; + return ret_val; if (phy->autoneg_wait_to_complete) { DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n"); @@ -1826,13 +1853,25 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; if (!link) { - if (hw->phy.type != e1000_phy_m88 || - hw->phy.id == I347AT4_E_PHY_ID || - hw->phy.id == M88E1340M_E_PHY_ID || - hw->phy.id == M88E1112_E_PHY_ID) { + bool reset_dsp = TRUE; + + switch (hw->phy.id) { + case I347AT4_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1112_E_PHY_ID: + case I210_I_PHY_ID: + reset_dsp = FALSE; + break; + default: + if (hw->phy.type != e1000_phy_m88) + reset_dsp = FALSE; + break; + } + + if (!reset_dsp) { DEBUGOUT("Link taking longer than expected.\n"); } else { /* @@ -1843,10 +1882,10 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) M88E1000_PHY_PAGE_SELECT, 0x001d); if (ret_val) - goto out; + return ret_val; ret_val = e1000_phy_reset_dsp_generic(hw); if (ret_val) - goto out; + return ret_val; } } @@ -1854,18 +1893,21 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; } - if (hw->phy.type != e1000_phy_m88 || - hw->phy.id == I347AT4_E_PHY_ID || - hw->phy.id == M88E1340M_E_PHY_ID || - hw->phy.id == M88E1112_E_PHY_ID) - goto out; + if (hw->phy.type != e1000_phy_m88) + return E1000_SUCCESS; + if (hw->phy.id == I347AT4_E_PHY_ID || + hw->phy.id == M88E1340M_E_PHY_ID || + hw->phy.id == M88E1112_E_PHY_ID) + return E1000_SUCCESS; + if (hw->phy.id == I210_I_PHY_ID) + return E1000_SUCCESS; ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; /* * Resetting the phy means we need to re-force TX_CLK in the @@ -1875,7 +1917,7 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) phy_data |= M88E1000_EPSCR_TX_CLK_25; ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); if (ret_val) - goto out; + return ret_val; /* * In addition, we must re-enable CRS on Tx for both half and full @@ -1883,12 +1925,11 @@ s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) */ ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); -out: return ret_val; } @@ -1911,25 +1952,25 @@ s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data); if (ret_val) - goto out; + return ret_val; e1000_phy_force_speed_duplex_setup(hw, &data); ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data); if (ret_val) - goto out; + return ret_val; /* Disable MDI-X support for 10/100 */ ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); if (ret_val) - goto out; + return ret_val; data &= ~IFE_PMC_AUTO_MDIX; data &= ~IFE_PMC_FORCE_MDIX; ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data); if (ret_val) - goto out; + return ret_val; DEBUGOUT1("IFE PMC: %X\n", data); @@ -1941,7 +1982,7 @@ s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; if (!link) DEBUGOUT("Link taking longer than expected.\n"); @@ -1950,11 +1991,10 @@ s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -2014,7 +2054,7 @@ void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) DEBUGOUT("Forcing 10mb\n"); } - e1000_config_collision_dist_generic(hw); + hw->mac.ops.config_collision_dist(hw); E1000_WRITE_REG(hw, E1000_CTRL, ctrl); } @@ -2036,24 +2076,24 @@ void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 data; DEBUGFUNC("e1000_set_d3_lplu_state_generic"); - if (!(hw->phy.ops.read_reg)) - goto out; + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); if (ret_val) - goto out; + return ret_val; if (!active) { data &= ~IGP02E1000_PM_D3_LPLU; ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, data); if (ret_val) - goto out; + return ret_val; /* * LPLU and SmartSpeed are mutually exclusive. LPLU is used * during Dx states where the power conservation is most @@ -2065,27 +2105,27 @@ s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active) IGP01E1000_PHY_PORT_CONFIG, &data); if (ret_val) - goto out; + return ret_val; data |= IGP01E1000_PSCFR_SMART_SPEED; ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, data); if (ret_val) - goto out; + return ret_val; } else if (phy->smart_speed == e1000_smart_speed_off) { ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &data); if (ret_val) - goto out; + return ret_val; data &= ~IGP01E1000_PSCFR_SMART_SPEED; ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, data); if (ret_val) - goto out; + return ret_val; } } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || @@ -2094,20 +2134,19 @@ s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active) ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, data); if (ret_val) - goto out; + return ret_val; /* When LPLU is enabled, we should disable SmartSpeed */ ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &data); if (ret_val) - goto out; + return ret_val; data &= ~IGP01E1000_PSCFR_SMART_SPEED; ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, data); } -out: return ret_val; } @@ -2128,6 +2167,7 @@ s32 e1000_check_downshift_generic(struct e1000_hw *hw) DEBUGFUNC("e1000_check_downshift_generic"); switch (phy->type) { + case e1000_phy_i210: case e1000_phy_m88: case e1000_phy_gg82563: case e1000_phy_bm: @@ -2144,16 +2184,14 @@ s32 e1000_check_downshift_generic(struct e1000_hw *hw) default: /* speed downshift not supported */ phy->speed_downgraded = FALSE; - ret_val = E1000_SUCCESS; - goto out; + return E1000_SUCCESS; } ret_val = phy->ops.read_reg(hw, offset, &phy_data); if (!ret_val) - phy->speed_downgraded = (phy_data & mask) ? TRUE : FALSE; + phy->speed_downgraded = !!(phy_data & mask); -out: return ret_val; } @@ -2206,7 +2244,7 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) */ ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); if (ret_val) - goto out; + return ret_val; if ((data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { @@ -2228,7 +2266,6 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) ? e1000_rev_polarity_reversed : e1000_rev_polarity_normal; -out: return ret_val; } @@ -2281,7 +2318,7 @@ s32 e1000_wait_autoneg_generic(struct e1000_hw *hw) DEBUGFUNC("e1000_wait_autoneg_generic"); - if (!(hw->phy.ops.read_reg)) + if (!hw->phy.ops.read_reg) return E1000_SUCCESS; /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ @@ -2321,7 +2358,7 @@ s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, DEBUGFUNC("e1000_phy_has_link_generic"); - if (!(hw->phy.ops.read_reg)) + if (!hw->phy.ops.read_reg) return E1000_SUCCESS; for (i = 0; i < iterations; i++) { @@ -2349,7 +2386,7 @@ s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, usec_delay(usec_interval); } - *success = (i < iterations) ? TRUE : FALSE; + *success = (i < iterations); return ret_val; } @@ -2379,22 +2416,20 @@ s32 e1000_get_cable_length_m88(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) - goto out; + return ret_val; 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; - goto out; - } + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; phy->min_cable_length = e1000_m88_cable_length_table[index]; phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; -out: - return ret_val; + return E1000_SUCCESS; } s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) @@ -2406,28 +2441,49 @@ s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) DEBUGFUNC("e1000_get_cable_length_m88_gen2"); switch (hw->phy.id) { + case I210_I_PHY_ID: + /* Get cable length from PHY Cable Diagnostics Control Reg */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + (I347AT4_PCDL + phy->addr), + &phy_data); + if (ret_val) + return ret_val; + + /* Check if the unit of cable length is meters or cm */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + I347AT4_PCDC, &phy_data2); + if (ret_val) + return ret_val; + + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + + /* Populate the phy structure with cable length in meters */ + phy->min_cable_length = phy_data / (is_cm ? 100 : 1); + phy->max_cable_length = phy_data / (is_cm ? 100 : 1); + phy->cable_length = phy_data / (is_cm ? 100 : 1); + break; case M88E1340M_E_PHY_ID: case I347AT4_E_PHY_ID: /* Remember the original page select and set it to 7 */ ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, &default_page); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07); if (ret_val) - goto out; + return ret_val; /* Get cable length from PHY Cable Diagnostics Control Reg */ ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr), &phy_data); if (ret_val) - goto out; + return ret_val; /* Check if the unit of cable length is meters or cm */ ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2); if (ret_val) - goto out; + return ret_val; is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); @@ -2440,30 +2496,30 @@ s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, default_page); if (ret_val) - goto out; + return ret_val; break; + case M88E1112_E_PHY_ID: /* Remember the original page select and set it to 5 */ ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, &default_page); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x05); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, M88E1112_VCT_DSP_DISTANCE, &phy_data); if (ret_val) - goto out; + return ret_val; 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; - goto out; - } + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; phy->min_cable_length = e1000_m88_cable_length_table[index]; phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; @@ -2475,15 +2531,13 @@ s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, default_page); if (ret_val) - goto out; + return ret_val; break; default: - ret_val = -E1000_ERR_PHY; - goto out; + return -E1000_ERR_PHY; } -out: return ret_val; } @@ -2501,7 +2555,7 @@ out: s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 phy_data, i, agc_value = 0; u16 cur_agc_index, max_agc_index = 0; u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1; @@ -2518,7 +2572,7 @@ s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &phy_data); if (ret_val) - goto out; + return ret_val; /* * Getting bits 15:9, which represent the combination of @@ -2531,10 +2585,8 @@ s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) /* Array index bound check. */ if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) || - (cur_agc_index == 0)) { - ret_val = -E1000_ERR_PHY; - goto out; - } + (cur_agc_index == 0)) + return -E1000_ERR_PHY; /* Remove min & max AGC values from calculation. */ if (e1000_igp_2_cable_length_table[min_agc_index] > @@ -2558,8 +2610,7 @@ s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -2583,45 +2634,43 @@ s32 e1000_get_phy_info_m88(struct e1000_hw *hw) 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; + return -E1000_ERR_CONFIG; } ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) - goto out; + return ret_val; if (!link) { DEBUGOUT("Phy info is only valid if link is up\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); if (ret_val) - goto out; + return ret_val; - phy->polarity_correction = (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) - ? TRUE : FALSE; + phy->polarity_correction = !!(phy_data & + M88E1000_PSCR_POLARITY_REVERSAL); ret_val = e1000_check_polarity_m88(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) - goto out; + return ret_val; - phy->is_mdix = (phy_data & M88E1000_PSSR_MDIX) ? TRUE : FALSE; + phy->is_mdix = !!(phy_data & M88E1000_PSSR_MDIX); if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { ret_val = hw->phy.ops.get_cable_length(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data); if (ret_val) - goto out; + return ret_val; phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) ? e1000_1000t_rx_status_ok @@ -2637,7 +2686,6 @@ s32 e1000_get_phy_info_m88(struct e1000_hw *hw) phy->remote_rx = e1000_1000t_rx_status_undefined; } -out: return ret_val; } @@ -2661,35 +2709,34 @@ s32 e1000_get_phy_info_igp(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) - goto out; + return ret_val; if (!link) { DEBUGOUT("Phy info is only valid if link is up\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } phy->polarity_correction = TRUE; ret_val = e1000_check_polarity_igp(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); if (ret_val) - goto out; + return ret_val; - phy->is_mdix = (data & IGP01E1000_PSSR_MDIX) ? TRUE : FALSE; + phy->is_mdix = !!(data & IGP01E1000_PSSR_MDIX); if ((data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { ret_val = phy->ops.get_cable_length(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); if (ret_val) - goto out; + return ret_val; phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) ? e1000_1000t_rx_status_ok @@ -2704,7 +2751,6 @@ s32 e1000_get_phy_info_igp(struct e1000_hw *hw) phy->remote_rx = e1000_1000t_rx_status_undefined; } -out: return ret_val; } @@ -2725,24 +2771,22 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) - goto out; + return ret_val; if (!link) { DEBUGOUT("Phy info is only valid if link is up\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } 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; + return ret_val; + phy->polarity_correction = !(data & IFE_PSC_AUTO_POLARITY_DISABLE); if (phy->polarity_correction) { ret_val = e1000_check_polarity_ife(hw); if (ret_val) - goto out; + return ret_val; } else { /* Polarity is forced */ phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY) @@ -2752,17 +2796,16 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); if (ret_val) - goto out; + return ret_val; - phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? TRUE : FALSE; + phy->is_mdix = !!(data & IFE_PMC_MDIX_STATUS); /* 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; + return E1000_SUCCESS; } /** @@ -2774,26 +2817,25 @@ out: **/ s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u16 phy_ctrl; DEBUGFUNC("e1000_phy_sw_reset_generic"); - if (!(hw->phy.ops.read_reg)) - goto out; + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); if (ret_val) - goto out; + return ret_val; phy_ctrl |= MII_CR_RESET; ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl); if (ret_val) - goto out; + return ret_val; usec_delay(1); -out: return ret_val; } @@ -2809,20 +2851,20 @@ out: s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val = E1000_SUCCESS; + s32 ret_val; u32 ctrl; DEBUGFUNC("e1000_phy_hw_reset_generic"); - ret_val = phy->ops.check_reset_block(hw); - if (ret_val) { - ret_val = E1000_SUCCESS; - goto out; + if (phy->ops.check_reset_block) { + ret_val = phy->ops.check_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; } ret_val = phy->ops.acquire(hw); if (ret_val) - goto out; + return ret_val; ctrl = E1000_READ_REG(hw, E1000_CTRL); E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST); @@ -2837,10 +2879,7 @@ s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw) phy->ops.release(hw); - ret_val = phy->ops.get_cfg_done(hw); - -out: - return ret_val; + return phy->ops.get_cfg_done(hw); } /** @@ -2994,6 +3033,9 @@ enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id) case I82580_I_PHY_ID: phy_type = e1000_phy_82580; break; + case I210_I_PHY_ID: + phy_type = e1000_phy_i210; + break; default: phy_type = e1000_phy_unknown; break; @@ -3011,7 +3053,6 @@ enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id) **/ s32 e1000_determine_phy_address(struct e1000_hw *hw) { - s32 ret_val = -E1000_ERR_PHY_TYPE; u32 phy_addr = 0; u32 i; enum e1000_phy_type phy_type = e1000_phy_unknown; @@ -3030,17 +3071,15 @@ s32 e1000_determine_phy_address(struct e1000_hw *hw) * If phy_type is valid, break - we found our * PHY address */ - if (phy_type != e1000_phy_unknown) { - ret_val = E1000_SUCCESS; - goto out; - } + if (phy_type != e1000_phy_unknown) + return E1000_SUCCESS; + msec_delay(1); i++; } while (i < 10); } -out: - return ret_val; + return -E1000_ERR_PHY_TYPE; } /** @@ -3083,7 +3122,7 @@ s32 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data, FALSE, FALSE); - goto out; + goto release; } hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); @@ -3108,13 +3147,13 @@ s32 e1000_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) ret_val = e1000_write_phy_reg_mdic(hw, page_select, (page << page_shift)); if (ret_val) - goto out; + goto release; } ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); -out: +release: hw->phy.ops.release(hw); return ret_val; } @@ -3144,7 +3183,7 @@ s32 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data, TRUE, FALSE); - goto out; + goto release; } hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); @@ -3169,12 +3208,12 @@ s32 e1000_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) ret_val = e1000_write_phy_reg_mdic(hw, page_select, (page << page_shift)); if (ret_val) - goto out; + goto release; } ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); -out: +release: hw->phy.ops.release(hw); return ret_val; } @@ -3204,7 +3243,7 @@ s32 e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data, TRUE, FALSE); - goto out; + goto release; } hw->phy.addr = 1; @@ -3216,12 +3255,12 @@ s32 e1000_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) page); if (ret_val) - goto out; + goto release; } ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); -out: +release: hw->phy.ops.release(hw); return ret_val; } @@ -3250,7 +3289,7 @@ s32 e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data, FALSE, FALSE); - goto out; + goto release; } hw->phy.addr = 1; @@ -3261,13 +3300,13 @@ s32 e1000_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) page); if (ret_val) - goto out; + goto release; } ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); -out: +release: hw->phy.ops.release(hw); return ret_val; } @@ -3287,10 +3326,8 @@ s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) DEBUGFUNC("e1000_enable_phy_wakeup_reg_access_bm"); - if (!phy_reg) { - ret_val = -E1000_ERR_PARAM; - goto out; - } + if (!phy_reg) + return -E1000_ERR_PARAM; /* All page select, port ctrl and wakeup registers use phy address 1 */ hw->phy.addr = 1; @@ -3299,14 +3336,14 @@ s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) ret_val = e1000_set_page_igp(hw, (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT)); if (ret_val) { DEBUGOUT("Could not set Port Control page\n"); - goto out; + return ret_val; } ret_val = e1000_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg); if (ret_val) { DEBUGOUT2("Could not read PHY register %d.%d\n", BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG); - goto out; + return ret_val; } /* @@ -3321,15 +3358,14 @@ s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) if (ret_val) { DEBUGOUT2("Could not write PHY register %d.%d\n", BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG); - goto out; + return ret_val; } - /* Select Host Wakeup Registers page */ - ret_val = e1000_set_page_igp(hw, (BM_WUC_PAGE << IGP_PAGE_SHIFT)); - - /* caller now able to write registers on the Wakeup registers page */ -out: - return ret_val; + /* + * Select Host Wakeup Registers page - caller now able to write + * registers on the Wakeup registers page + */ + return e1000_set_page_igp(hw, (BM_WUC_PAGE << IGP_PAGE_SHIFT)); } /** @@ -3356,7 +3392,7 @@ s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) ret_val = e1000_set_page_igp(hw, (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT)); if (ret_val) { DEBUGOUT("Could not set Port Control page\n"); - goto out; + return ret_val; } /* Restore 769.17 to its original value */ @@ -3364,7 +3400,7 @@ s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) if (ret_val) DEBUGOUT2("Could not restore PHY register %d.%d\n", BM_PORT_CTRL_PAGE, BM_WUC_ENABLE_REG); -out: + return ret_val; } @@ -3413,7 +3449,7 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, ret_val = e1000_enable_phy_wakeup_reg_access_bm(hw, &phy_reg); if (ret_val) { DEBUGOUT("Could not enable PHY wakeup reg access\n"); - goto out; + return ret_val; } } @@ -3423,7 +3459,7 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg); if (ret_val) { DEBUGOUT1("Could not write address opcode to page %d\n", page); - goto out; + return ret_val; } if (read) { @@ -3438,13 +3474,12 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, if (ret_val) { DEBUGOUT2("Could not access PHY reg %d.%d\n", page, reg); - goto out; + return ret_val; } if (!page_set) ret_val = e1000_disable_phy_wakeup_reg_access_bm(hw, &phy_reg); -out: return ret_val; } @@ -3459,10 +3494,16 @@ out: void e1000_power_up_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; + u16 power_reg = 0; /* The PHY will retain its settings across a power down/up cycle */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); mii_reg &= ~MII_CR_POWER_DOWN; + if (hw->phy.type == e1000_phy_i210) { + hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg); + power_reg &= ~GS40G_CS_POWER_DOWN; + hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg); + } hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); } @@ -3477,10 +3518,17 @@ void e1000_power_up_phy_copper(struct e1000_hw *hw) void e1000_power_down_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; + u16 power_reg = 0; /* The PHY will retain its settings across a power down/up cycle */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); mii_reg |= MII_CR_POWER_DOWN; + /* i210 Phy requires an additional bit for power up/down */ + if (hw->phy.type == e1000_phy_i210) { + hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg); + power_reg |= GS40G_CS_POWER_DOWN; + hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg); + } hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); msec_delay(1); } @@ -3646,7 +3694,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data, if ((hw->phy.type == e1000_phy_82578) && (hw->phy.revision >= 1) && (hw->phy.addr == 2) && - ((MAX_PHY_REG_ADDRESS & reg) == 0) && + !(MAX_PHY_REG_ADDRESS & reg) && (data & (1 << 11))) { u16 data2 = 0x7EFF; ret_val = e1000_access_phy_debug_regs_hv(hw, @@ -3770,7 +3818,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, ret_val = e1000_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F); if (ret_val) { DEBUGOUT("Could not write the Address Offset port register\n"); - goto out; + return ret_val; } /* Read or write the data value next */ @@ -3779,12 +3827,9 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, else ret_val = e1000_write_phy_reg_mdic(hw, data_reg, *data); - if (ret_val) { + if (ret_val) DEBUGOUT("Could not access the Data port register\n"); - goto out; - } -out: return ret_val; } @@ -3807,24 +3852,24 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) DEBUGFUNC("e1000_link_stall_workaround_hv"); if (hw->phy.type != e1000_phy_82578) - goto out; + return E1000_SUCCESS; /* Do not apply workaround if in PHY loopback bit 14 set */ hw->phy.ops.read_reg(hw, PHY_CONTROL, &data); if (data & PHY_CONTROL_LB) - goto out; + return E1000_SUCCESS; /* check if link is up and at 1Gbps */ ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data); if (ret_val) - goto out; + return ret_val; data &= BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | BM_CS_STATUS_SPEED_MASK; if (data != (BM_CS_STATUS_LINK_UP | BM_CS_STATUS_RESOLVED | BM_CS_STATUS_SPEED_1000)) - goto out; + return E1000_SUCCESS; msec_delay(200); @@ -3833,13 +3878,10 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) HV_MUX_DATA_CTRL_GEN_TO_MAC | HV_MUX_DATA_CTRL_FORCE_SPEED); if (ret_val) - goto out; - - ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL, - HV_MUX_DATA_CTRL_GEN_TO_MAC); + return ret_val; -out: - return ret_val; + return hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL, + HV_MUX_DATA_CTRL_GEN_TO_MAC); } /** @@ -3885,13 +3927,13 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); if (ret_val) - goto out; + return ret_val; e1000_phy_force_speed_duplex_setup(hw, &phy_data); ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); if (ret_val) - goto out; + return ret_val; usec_delay(1); @@ -3901,7 +3943,7 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); if (ret_val) - goto out; + return ret_val; if (!link) DEBUGOUT("Link taking longer than expected.\n"); @@ -3909,11 +3951,8 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) /* Try once more */ ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, 100000, &link); - if (ret_val) - goto out; } -out: return ret_val; } @@ -3937,35 +3976,34 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw) ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) - goto out; + return ret_val; if (!link) { DEBUGOUT("Phy info is only valid if link is up\n"); - ret_val = -E1000_ERR_CONFIG; - goto out; + return -E1000_ERR_CONFIG; } phy->polarity_correction = TRUE; ret_val = e1000_check_polarity_82577(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); if (ret_val) - goto out; + return ret_val; - phy->is_mdix = (data & I82577_PHY_STATUS2_MDIX) ? TRUE : FALSE; + phy->is_mdix = !!(data & I82577_PHY_STATUS2_MDIX); if ((data & I82577_PHY_STATUS2_SPEED_MASK) == I82577_PHY_STATUS2_SPEED_1000MBPS) { ret_val = hw->phy.ops.get_cable_length(hw); if (ret_val) - goto out; + return ret_val; ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); if (ret_val) - goto out; + return ret_val; phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) ? e1000_1000t_rx_status_ok @@ -3980,8 +4018,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw) phy->remote_rx = e1000_1000t_rx_status_undefined; } -out: - return ret_val; + return E1000_SUCCESS; } /** @@ -4001,7 +4038,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw) ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data); if (ret_val) - goto out; + return ret_val; length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >> I82577_DSTATUS_CABLE_LENGTH_SHIFT; @@ -4011,6 +4048,68 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw) phy->cable_length = length; -out: + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_gs40g - Write GS40G PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * 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_gs40g(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_write_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_write_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); + return ret_val; +} + +/** + * e1000_read_phy_reg_gs40g - Read GS40G PHY register + * @hw: pointer to the HW structure + * @offset: lower half is register offset to read to + * upper half is page to use. + * @data: data to read at register offset + * + * Acquires semaphore, if necessary, then reads the data in the PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_read_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_read_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); return ret_val; } diff --git a/sys/dev/e1000/e1000_phy.h b/sys/dev/e1000/e1000_phy.h index 93d0afc..edcbcab 100644 --- a/sys/dev/e1000/e1000_phy.h +++ b/sys/dev/e1000/e1000_phy.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -41,6 +41,10 @@ void e1000_null_phy_generic(struct e1000_hw *hw); s32 e1000_null_lplu_state(struct e1000_hw *hw, bool active); s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data); s32 e1000_null_set_page(struct e1000_hw *hw, u16 data); +s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); +s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); s32 e1000_check_downshift_generic(struct e1000_hw *hw); s32 e1000_check_polarity_m88(struct e1000_hw *hw); s32 e1000_check_polarity_igp(struct e1000_hw *hw); @@ -112,6 +116,8 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw); s32 e1000_get_phy_info_82577(struct e1000_hw *hw); s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw); s32 e1000_get_cable_length_82577(struct e1000_hw *hw); +s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); #define E1000_MAX_PHY_ADDR 8 @@ -128,6 +134,17 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw); #define IGP_PAGE_SHIFT 5 #define PHY_REG_MASK 0x1F +/* GS40G - I210 PHY defines */ +#define GS40G_PAGE_SELECT 0x16 +#define GS40G_PAGE_SHIFT 16 +#define GS40G_OFFSET_MASK 0xFFFF +#define GS40G_PAGE_2 0x20000 +#define GS40G_MAC_REG2 0x15 +#define GS40G_MAC_LB 0x4140 +#define GS40G_MAC_SPEED_1G 0X0006 +#define GS40G_COPPER_SPEC 0x0010 +#define GS40G_CS_POWER_DOWN 0x0002 + /* BM/HV Specific Registers */ #define BM_PORT_CTRL_PAGE 769 #define BM_PCIE_PAGE 770 @@ -174,8 +191,9 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw); #define I82577_PHY_STATUS2_SPEED_100MBPS 0x0100 /* I82577 PHY Control 2 */ -#define I82577_PHY_CTRL2_AUTO_MDIX 0x0400 -#define I82577_PHY_CTRL2_FORCE_MDI_MDIX 0x0200 +#define I82577_PHY_CTRL2_MANUAL_MDIX 0x0200 +#define I82577_PHY_CTRL2_AUTO_MDI_MDIX 0x0400 +#define I82577_PHY_CTRL2_MDIX_CFG_MASK 0x0600 /* I82577 PHY Diagnostics Status */ #define I82577_DSTATUS_CABLE_LENGTH 0x03FC diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h index 2cc89d5..8c4e4af 100644 --- a/sys/dev/e1000/e1000_regs.h +++ b/sys/dev/e1000/e1000_regs.h @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -51,6 +51,7 @@ #define E1000_BARCTRL_FLSIZE 0x0700 /* BAR ctrl Flsize */ #define E1000_BARCTRL_CSRSIZE 0x2000 /* BAR ctrl CSR size */ #define E1000_I350_BARCTRL 0x5BFC /* BAR ctrl reg */ +#define E1000_I350_DTXMXPKTSZ 0x355C /* Maximum sent packet size reg*/ #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 */ @@ -143,6 +144,62 @@ #define E1000_PBRWAC 0x024E8 /* Rx packet buffer wrap around counter - RO */ #define E1000_RDTR 0x02820 /* Rx Delay Timer - RW */ #define E1000_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer - RW */ +#define E1000_SRWR 0x12018 /* Shadow Ram Write Register - RW */ +#define E1000_I210_FLMNGCTL 0x12038 +#define E1000_I210_FLMNGDATA 0x1203C +#define E1000_I210_FLMNGCNT 0x12040 + +#define E1000_I210_FLSWCTL 0x12048 +#define E1000_I210_FLSWDATA 0x1204C +#define E1000_I210_FLSWCNT 0x12050 + +#define E1000_I210_FLA 0x1201C + +#define E1000_INVM_DATA_REG(_n) (0x12120 + 4*(_n)) +#define E1000_INVM_SIZE 64 /* Number of INVM Data Registers */ + +/* QAV Tx mode control register */ +#define E1000_I210_TQAVCTRL 0x3570 + +/* QAV Tx mode control register bitfields masks */ +/* QAV enable */ +#define E1000_TQAVCTRL_MODE (1 << 0) +/* Fetching arbitration type */ +#define E1000_TQAVCTRL_FETCH_ARB (1 << 4) +/* Fetching timer enable */ +#define E1000_TQAVCTRL_FETCH_TIMER_ENABLE (1 << 5) +/* Launch arbitration type */ +#define E1000_TQAVCTRL_LAUNCH_ARB (1 << 8) +/* Launch timer enable */ +#define E1000_TQAVCTRL_LAUNCH_TIMER_ENABLE (1 << 9) +/* SP waits for SR enable */ +#define E1000_TQAVCTRL_SP_WAIT_SR (1 << 10) +/* Fetching timer correction */ +#define E1000_TQAVCTRL_FETCH_TIMER_DELTA_OFFSET 16 +#define E1000_TQAVCTRL_FETCH_TIMER_DELTA \ + (0xFFFF << E1000_TQAVCTRL_FETCH_TIMER_DELTA_OFFSET) + +/* High credit registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVHC(_n) (0x300C + 0x40 * (_n)) + +/* Queues fetch arbitration priority control register */ +#define E1000_I210_TQAVARBCTRL 0x3574 +/* Queues priority masks where _n and _p can be 0-3. */ +#define E1000_TQAVARBCTRL_QUEUE_PRI(_n, _p) ((_p) << (2 * _n)) +/* QAV Tx mode control registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVCC(_n) (0x3004 + 0x40 * (_n)) + +/* QAV Tx mode control register bitfields masks */ +#define E1000_TQAVCC_IDLE_SLOPE 0xFFFF /* Idle slope */ +#define E1000_TQAVCC_KEEP_CREDITS (1 << 30) /* Keep credits opt enable */ +#define E1000_TQAVCC_QUEUE_MODE (1 << 31) /* SP vs. SR Tx mode */ + +/* Good transmitted packets counter registers */ +#define E1000_PQGPTC(_n) (0x010014 + (0x100 * (_n))) + +/* Queues packet buffer size masks where _n can be 0-3 and _s 0-63 [kB] */ +#define E1000_I210_TXPBS_SIZE(_n, _s) ((_s) << (6 * _n)) + /* * Convenience macros * @@ -424,10 +481,11 @@ #define E1000_HOST_IF 0x08800 /* Host Interface */ #define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ #define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ +#define E1000_HIBBA 0x8F40 /* Host Interface Buffer Base Address */ /* Flexible Host Filter Table */ -#define E1000_FHFT(_n) (0x09000 + (_n * 0x100)) +#define E1000_FHFT(_n) (0x09000 + ((_n) * 0x100)) /* Ext Flexible Host Filter Table */ -#define E1000_FHFT_EXT(_n) (0x09A00 + (_n * 0x100)) +#define E1000_FHFT_EXT(_n) (0x09A00 + ((_n) * 0x100)) #define E1000_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */ @@ -590,10 +648,6 @@ /* PCIe Parity Status Register */ #define E1000_PCIEERRSTS 0x05BA8 -#define E1000_LTRMINV 0x5BB0 /* LTR Minimum Value */ -#define E1000_LTRMAXV 0x5BB4 /* LTR Maximum Value */ -#define E1000_DOBFFCTL 0x3F24 /* DMA OBFF Control Register */ - #define E1000_PROXYS 0x5F64 /* Proxying Status */ #define E1000_PROXYFC 0x5F60 /* Proxying Filter Control */ /* Thermal sensor configuration and status registers */ @@ -603,7 +657,7 @@ #define E1000_THHIGHTC 0x0810C /* High Threshold Control */ #define E1000_THSTAT 0x08110 /* Thermal Sensor Status */ -/*Energy Efficient Ethernet "EEE" registers */ +/* Energy Efficient Ethernet "EEE" registers */ #define E1000_IPCNFG 0x0E38 /* Internal PHY Configuration */ #define E1000_LTRC 0x01A0 /* Latency Tolerance Reporting Control */ #define E1000_EEER 0x0E30 /* Energy Efficient Ethernet "EEE"*/ @@ -617,4 +671,9 @@ #define E1000_O2BGPTC 0x08FE4 /* OS2BMC packets received by BMC */ #define E1000_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */ +#define E1000_LTRMINV 0x5BB0 /* LTR Minimum Value */ +#define E1000_LTRMAXV 0x5BB4 /* LTR Maximum Value */ +#define E1000_DOBFFCTL 0x3F24 /* DMA OBFF Control Register */ + + #endif diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 283f776..5d8c2b8 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -193,13 +193,14 @@ static int em_detach(device_t); 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 *, struct tx_ring *); #ifdef EM_MULTIQUEUE static int em_mq_start(struct ifnet *, struct mbuf *); static int em_mq_start_locked(struct ifnet *, struct tx_ring *, struct mbuf *); static void em_qflush(struct ifnet *); +#else +static void em_start(struct ifnet *); +static void em_start_locked(struct ifnet *, struct tx_ring *); #endif static int em_ioctl(struct ifnet *, u_long, caddr_t); static void em_init(void *); @@ -234,8 +235,8 @@ static void em_enable_intr(struct adapter *); static void em_disable_intr(struct adapter *); static void em_update_stats_counters(struct adapter *); static void em_add_hw_stats(struct adapter *adapter); -static bool em_txeof(struct tx_ring *); -static bool em_rxeof(struct rx_ring *, int, int *); +static void em_txeof(struct tx_ring *); +static bool em_rxeof(struct rx_ring *, int, int *, struct mbuf **); #ifndef __NO_STRICT_ALIGNMENT static int em_fixup_rx(struct rx_ring *); #endif @@ -288,11 +289,15 @@ static void em_handle_link(void *context, int pending); static void em_set_sysctl_value(struct adapter *, const char *, const char *, int *, int); static int em_set_flowcntl(SYSCTL_HANDLER_ARGS); +static int em_sysctl_eee(SYSCTL_HANDLER_ARGS); static __inline void em_rx_discard(struct rx_ring *, int); #ifdef DEVICE_POLLING -static poll_handler_t em_poll; +static poll_handler_mq_t em_poll; +#ifdef EM_MULTIQUEUE +#error multiqueue must be disabled +#endif #endif /* POLLING */ /********************************************************************* @@ -388,7 +393,7 @@ SYSCTL_INT(_hw_em, OID_AUTO, rx_process_limit, CTLFLAG_RDTUN, "at a time, -1 means unlimited"); /* Energy efficient ethernet - default to OFF */ -static int eee_setting = 0; +static int eee_setting = 1; TUNABLE_INT("hw.em.eee_setting", &eee_setting); SYSCTL_INT(_hw_em, OID_AUTO, eee_setting, CTLFLAG_RDTUN, &eee_setting, 0, "Enable Energy Efficient Ethernet"); @@ -631,9 +636,12 @@ em_attach(device_t dev) " due to SOL/IDER session.\n"); /* Sysctl for setting Energy Efficient Ethernet */ - em_set_sysctl_value(adapter, "eee_control", - "enable Energy Efficient Ethernet", - &hw->dev_spec.ich8lan.eee_disable, eee_setting); + hw->dev_spec.ich8lan.eee_disable = eee_setting; + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "eee_control", CTLTYPE_INT|CTLFLAG_RW, + adapter, 0, em_sysctl_eee, "I", + "Disable Energy Efficient Ethernet"); /* ** Start from a known state, this is @@ -762,7 +770,7 @@ em_detach(device_t dev) #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) - ether_poll_deregister(ifp); + ether_poll_deregister_mq(ifp, 0); #endif if (adapter->led_dev != NULL) @@ -836,6 +844,7 @@ static int em_resume(device_t dev) { struct adapter *adapter = device_get_softc(dev); + struct tx_ring *txr = adapter->tx_rings; struct ifnet *ifp = adapter->ifp; EM_CORE_LOCK(adapter); @@ -843,8 +852,22 @@ em_resume(device_t dev) e1000_resume_workarounds_pchlan(&adapter->hw); em_init_locked(adapter); em_init_manageability(adapter); + + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING) && adapter->link_active) { + for (int i = 0; i < adapter->num_queues; i++, txr++) { + EM_TX_LOCK(txr); +#ifdef EM_MULTIQUEUE + 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 + EM_TX_UNLOCK(txr); + } + } EM_CORE_UNLOCK(adapter); - em_start(ifp); return bus_generic_resume(dev); } @@ -866,8 +889,8 @@ em_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m) 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 (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) || + adapter->link_active == 0) { if (m != NULL) err = drbr_enqueue(ifp, txr->br, m); return (err); @@ -948,7 +971,7 @@ em_qflush(struct ifnet *ifp) } if_qflush(ifp); } -#endif /* EM_MULTIQUEUE */ +#else /* !EM_MULTIQUEUE */ static void em_start_locked(struct ifnet *ifp, struct tx_ring *txr) @@ -958,11 +981,8 @@ em_start_locked(struct ifnet *ifp, struct tx_ring *txr) EM_TX_LOCK_ASSERT(txr); - if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != - IFF_DRV_RUNNING) - return; - - if (!adapter->link_active) + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || + adapter->link_active == 0) return; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { @@ -1009,14 +1029,9 @@ em_start(struct ifnet *ifp) em_start_locked(ifp, txr); EM_TX_UNLOCK(txr); } - /* - ** If we went inactive schedule - ** a task to clean up. - */ - if (ifp->if_drv_flags & IFF_DRV_OACTIVE) - taskqueue_enqueue(txr->tq, &txr->tx_task); return; } +#endif /* EM_MULTIQUEUE */ /********************************************************************* * Ioctl entry point @@ -1167,15 +1182,14 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data) #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { - error = ether_poll_register(em_poll, ifp); - if (error) - return (error); + ether_poll_register_mq(em_poll, + ifp, 0, 1, NULL); EM_CORE_LOCK(adapter); em_disable_intr(adapter); ifp->if_capenable |= IFCAP_POLLING; EM_CORE_UNLOCK(adapter); } else { - error = ether_poll_deregister(ifp); + ether_poll_deregister_mq(ifp, 0); /* Enable interrupt even in error case */ EM_CORE_LOCK(adapter); em_enable_intr(adapter); @@ -1247,6 +1261,10 @@ em_init_locked(struct adapter *adapter) EM_CORE_LOCK_ASSERT(adapter); + /* Tell the stack that the interface is no longer active */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + em_disable_intr(adapter); callout_stop(&adapter->timer); @@ -1328,10 +1346,6 @@ em_init_locked(struct adapter *adapter) /* Don't lose promiscuous settings */ em_set_promisc(adapter); - /* Set the interface as ACTIVE */ - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - callout_reset(&adapter->timer, hz, em_local_timer, adapter); e1000_clear_hw_cntrs_base_generic(&adapter->hw); @@ -1359,6 +1373,9 @@ em_init_locked(struct adapter *adapter) /* AMT based hardware can now take control from firmware */ if (adapter->has_manage && adapter->has_amt) em_get_hw_control(adapter); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void @@ -1373,51 +1390,55 @@ em_init(void *arg) #ifdef DEVICE_POLLING -/********************************************************************* - * - * Legacy polling routine: note this only works with single queue - * - *********************************************************************/ +/* + * Multi-queue polling code: XXX works only with single queue. + */ static int -em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +em_poll(struct ifnet *ifp, void *queue, enum poll_cmd cmd, + int count, struct mbuf **mc) { struct adapter *adapter = ifp->if_softc; struct tx_ring *txr = adapter->tx_rings; struct rx_ring *rxr = adapter->rx_rings; u32 reg_icr; - int rx_done; + bool more; - EM_CORE_LOCK(adapter); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - EM_CORE_UNLOCK(adapter); - return (0); - } + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return 0; - if (cmd == POLL_AND_CHECK_STATUS) { + switch (cmd) { + case 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)) { - callout_stop(&adapter->timer); adapter->hw.mac.get_link_status = 1; - em_update_link_status(adapter); - callout_reset(&adapter->timer, hz, - em_local_timer, adapter); + em_handle_link(adapter, 0); } - } - EM_CORE_UNLOCK(adapter); - - em_rxeof(rxr, count, &rx_done); + /* FALLTHROUGH */ + case POLL_ONLY: + more = em_rxeof(rxr, count, NULL, mc); + break; - EM_TX_LOCK(txr); - em_txeof(txr); + case POLL_TX: + EM_TX_LOCK(txr); #ifdef EM_MULTIQUEUE - if (!drbr_empty(ifp, txr->br)) - em_mq_start_locked(ifp, txr, NULL); + if (!drbr_empty(ifp, txr->br)) + em_mq_start_locked(ifp, txr, NULL); #else - em_start_locked(ifp, txr); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp, txr); #endif - EM_TX_UNLOCK(txr); + em_txeof(txr); + EM_TX_UNLOCK(txr); + more = FALSE; + break; + + default: + more = FALSE; + } - return (rx_done); + return (more); } #endif /* DEVICE_POLLING */ @@ -1479,23 +1500,27 @@ em_handle_que(void *context, int pending) if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - bool more = em_rxeof(rxr, adapter->rx_process_limit, NULL); + bool more = em_rxeof(rxr, adapter->rx_process_limit, NULL, NULL); EM_TX_LOCK(txr); em_txeof(txr); #ifdef EM_MULTIQUEUE if (!drbr_empty(ifp, txr->br)) em_mq_start_locked(ifp, txr, NULL); #else - em_start_locked(ifp, txr); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp, txr); #endif EM_TX_UNLOCK(txr); - if (more || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + if (more) { taskqueue_enqueue(adapter->tq, &adapter->que_task); return; } } - em_enable_intr(adapter); +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif + em_enable_intr(adapter); return; } @@ -1510,17 +1535,25 @@ em_msix_tx(void *arg) { struct tx_ring *txr = arg; struct adapter *adapter = txr->adapter; - bool more; + struct ifnet *ifp = adapter->ifp; +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif ++txr->tx_irq; EM_TX_LOCK(txr); - more = em_txeof(txr); + em_txeof(txr); +#ifdef EM_MULTIQUEUE + 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 + /* Reenable this interrupt */ + E1000_WRITE_REG(&adapter->hw, E1000_IMS, txr->ims); 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; } @@ -1536,9 +1569,14 @@ em_msix_rx(void *arg) struct rx_ring *rxr = arg; struct adapter *adapter = rxr->adapter; bool more; +#ifdef DEVICE_POLLING + struct ifnet *ifp = adapter->ifp; + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif ++rxr->rx_irq; - more = em_rxeof(rxr, adapter->rx_process_limit, NULL); + more = em_rxeof(rxr, adapter->rx_process_limit, NULL, NULL); if (more) taskqueue_enqueue(rxr->tq, &rxr->rx_task); else @@ -1557,7 +1595,12 @@ em_msix_link(void *arg) { struct adapter *adapter = arg; u32 reg_icr; +#ifdef DEVICE_POLLING + struct ifnet *ifp = adapter->ifp; + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif ++adapter->link_irq; reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR); @@ -1577,7 +1620,7 @@ em_handle_rx(void *context, int pending) struct adapter *adapter = rxr->adapter; bool more; - more = em_rxeof(rxr, adapter->rx_process_limit, NULL); + more = em_rxeof(rxr, adapter->rx_process_limit, NULL, NULL); if (more) taskqueue_enqueue(rxr->tq, &rxr->rx_task); else @@ -1598,7 +1641,8 @@ em_handle_tx(void *context, int pending) if (!drbr_empty(ifp, txr->br)) em_mq_start_locked(ifp, txr, NULL); #else - em_start_locked(ifp, txr); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp, txr); #endif E1000_WRITE_REG(&adapter->hw, E1000_IMS, txr->ims); EM_TX_UNLOCK(txr); @@ -1608,6 +1652,7 @@ static void em_handle_link(void *context, int pending) { struct adapter *adapter = context; + struct tx_ring *txr = adapter->tx_rings; struct ifnet *ifp = adapter->ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) @@ -1619,6 +1664,19 @@ em_handle_link(void *context, int pending) callout_reset(&adapter->timer, hz, em_local_timer, adapter); E1000_WRITE_REG(&adapter->hw, E1000_IMS, EM_MSIX_LINK | E1000_IMS_LSC); + if (adapter->link_active) { + for (int i = 0; i < adapter->num_queues; i++, txr++) { + EM_TX_LOCK(txr); +#ifdef EM_MULTIQUEUE + 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 + EM_TX_UNLOCK(txr); + } + } EM_CORE_UNLOCK(adapter); } @@ -2200,15 +2258,19 @@ em_local_timer(void *arg) goto hung; /* Schedule a TX tasklet if needed */ if (txr->tx_avail <= EM_MAX_SCATTER) +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif taskqueue_enqueue(txr->tq, &txr->tx_task); } adapter->pause_frames = 0; callout_reset(&adapter->timer, hz, em_local_timer, adapter); -#ifndef DEVICE_POLLING /* Trigger an RX interrupt to guarantee mbuf refresh */ - E1000_WRITE_REG(&adapter->hw, E1000_ICS, trigger); +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) #endif + E1000_WRITE_REG(&adapter->hw, E1000_ICS, trigger); return; hung: /* Looks like we're hung */ @@ -2891,20 +2953,21 @@ em_setup_interface(device_t dev, struct adapter *adapter) ifp->if_softc = adapter; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = em_ioctl; +#ifdef EM_MULTIQUEUE + /* Multiqueue stack interface */ + ifp->if_transmit = em_mq_start; + ifp->if_qflush = em_qflush; +#else ifp->if_start = em_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); +#endif ether_ifattach(ifp, adapter->hw.mac.addr); ifp->if_capabilities = ifp->if_capenable = 0; -#ifdef EM_MULTIQUEUE - /* Multiqueue stack interface */ - ifp->if_transmit = em_mq_start; - ifp->if_qflush = em_qflush; -#endif ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; ifp->if_capabilities |= IFCAP_TSO4; @@ -3259,6 +3322,7 @@ em_setup_transmit_ring(struct tx_ring *txr) /* 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 */ @@ -3275,6 +3339,7 @@ em_setup_transmit_ring(struct tx_ring *txr) m_freem(txbuf->m_head); txbuf->m_head = NULL; } + /* clear the watch index */ txbuf->next_eop = -1; } @@ -3710,7 +3775,7 @@ em_tso_setup(struct tx_ring *txr, struct mbuf *mp, int ip_off, * tx_buffer is put back on the free queue. * **********************************************************************/ -static bool +static void em_txeof(struct tx_ring *txr) { struct adapter *adapter = txr->adapter; @@ -3724,7 +3789,7 @@ em_txeof(struct tx_ring *txr) /* No work, make sure watchdog is off */ if (txr->tx_avail == adapter->num_tx_desc) { txr->queue_status = EM_QUEUE_IDLE; - return (FALSE); + return; } processed = 0; @@ -3813,10 +3878,7 @@ em_txeof(struct tx_ring *txr) /* Disable watchdog if all clean */ if (txr->tx_avail == adapter->num_tx_desc) { txr->queue_status = EM_QUEUE_IDLE; - return (FALSE); } - - return (TRUE); } @@ -4273,7 +4335,7 @@ em_initialize_receive_unit(struct adapter *adapter) * For polling we also now return the number of cleaned packets *********************************************************************/ static bool -em_rxeof(struct rx_ring *rxr, int count, int *done) +em_rxeof(struct rx_ring *rxr, int count, int *done, struct mbuf **mc) { struct adapter *adapter = rxr->adapter; struct ifnet *ifp = adapter->ifp; @@ -4283,6 +4345,7 @@ em_rxeof(struct rx_ring *rxr, int count, int *done) int i, processed, rxdone = 0; bool eop; struct e1000_rx_desc *cur; + struct mbuf *mc_p = NULL; EM_RX_LOCK(rxr); @@ -4370,9 +4433,18 @@ next_desc: /* Send to the stack */ if (sendmp != NULL) { rxr->next_to_check = i; - EM_RX_UNLOCK(rxr); - (*ifp->if_input)(ifp, sendmp); - EM_RX_LOCK(rxr); + if (mc != NULL) { + if (*mc == NULL) + *mc = mc_p = sendmp; + else { + mc_p->m_nextpkt = sendmp; + mc_p = sendmp; + } + } else { + EM_RX_UNLOCK(rxr); + (*ifp->if_input)(ifp, sendmp); + EM_RX_LOCK(rxr); + } i = rxr->next_to_check; } @@ -5571,6 +5643,27 @@ em_set_flowcntl(SYSCTL_HANDLER_ARGS) return (error); } +/* +** Manage Energy Efficient Ethernet: +** Control values: +** 0/1 - enabled/disabled +*/ +static int +em_sysctl_eee(SYSCTL_HANDLER_ARGS) +{ + struct adapter *adapter = (struct adapter *) arg1; + int error, value; + + value = adapter->hw.dev_spec.ich8lan.eee_disable; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error || req->newptr == NULL) + return (error); + EM_CORE_LOCK(adapter); + adapter->hw.dev_spec.ich8lan.eee_disable = (value != 0); + em_init_locked(adapter); + EM_CORE_UNLOCK(adapter); + return (0); +} static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS) diff --git a/sys/dev/e1000/if_igb.c b/sys/dev/e1000/if_igb.c index 82dee40..80b8db8 100644 --- a/sys/dev/e1000/if_igb.c +++ b/sys/dev/e1000/if_igb.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -100,7 +100,7 @@ int igb_display_debug_stats = 0; /********************************************************************* * Driver version: *********************************************************************/ -char igb_driver_version[] = "version - 2.3.1"; +char igb_driver_version[] = "version - 2.3.4"; /********************************************************************* @@ -150,6 +150,14 @@ static igb_vendor_info_t igb_vendor_info_array[] = { 0x8086, E1000_DEV_ID_I350_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I350_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0}, { 0x8086, E1000_DEV_ID_I350_VF, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_COPPER_IT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_COPPER_OEM1, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I210_SGMII, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_I211_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, /* required last entry */ { 0, 0, 0, 0, 0} }; @@ -171,13 +179,15 @@ static int igb_detach(device_t); static int igb_shutdown(device_t); static int igb_suspend(device_t); static int igb_resume(device_t); -static void igb_start(struct ifnet *); -static void igb_start_locked(struct tx_ring *, struct ifnet *ifp); #if __FreeBSD_version >= 800000 static int igb_mq_start(struct ifnet *, struct mbuf *); static int igb_mq_start_locked(struct ifnet *, struct tx_ring *, struct mbuf *); static void igb_qflush(struct ifnet *); +static void igb_deferred_mq_start(void *, int); +#else +static void igb_start(struct ifnet *); +static void igb_start_locked(struct tx_ring *, struct ifnet *ifp); #endif static int igb_ioctl(struct ifnet *, u_long, caddr_t); static void igb_init(void *); @@ -221,7 +231,7 @@ 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, int *); +static bool igb_rxeof(struct igb_queue *, int, int *, struct mbuf **); static void igb_rx_checksum(u32, struct mbuf *, u32); static bool igb_tx_ctx_setup(struct tx_ring *, struct mbuf *); static bool igb_tso_setup(struct tx_ring *, struct mbuf *, int, @@ -261,14 +271,16 @@ static void igb_msix_que(void *); static void igb_msix_link(void *); static void igb_handle_que(void *context, int pending); static void igb_handle_link(void *context, int pending); +static void igb_handle_link_locked(struct adapter *); static void igb_set_sysctl_value(struct adapter *, const char *, const char *, int *, int); static int igb_set_flowcntl(SYSCTL_HANDLER_ARGS); static int igb_sysctl_dmac(SYSCTL_HANDLER_ARGS); +static int igb_sysctl_eee(SYSCTL_HANDLER_ARGS); #ifdef DEVICE_POLLING -static poll_handler_t igb_poll; +static poll_handler_mq_t igb_poll; #endif /* POLLING */ /********************************************************************* @@ -565,11 +577,13 @@ igb_attach(device_t dev) SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dmac", CTLTYPE_INT|CTLFLAG_RW, adapter, 0, igb_sysctl_dmac, "I", "DMA Coalesce"); - igb_set_sysctl_value(adapter, "eee_disabled", - "enable Energy Efficient Ethernet", - &adapter->hw.dev_spec._82575.eee_disable, - TRUE); - e1000_set_eee_i350(&adapter->hw); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "eee_disabled", CTLTYPE_INT|CTLFLAG_RW, + adapter, 0, igb_sysctl_eee, "I", + "Disable Energy Efficient Ethernet"); + if (adapter->hw.phy.media_type == e1000_media_type_copper) + e1000_set_eee_i350(&adapter->hw); } /* @@ -580,7 +594,9 @@ igb_attach(device_t dev) 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) { + if (((adapter->hw.mac.type != e1000_i210) && + (adapter->hw.mac.type != e1000_i211)) && + (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, @@ -707,12 +723,14 @@ igb_detach(device_t dev) return (EBUSY); } + ether_ifdetach(adapter->ifp); + if (adapter->led_dev != NULL) led_destroy(adapter->led_dev); #ifdef DEVICE_POLLING - if (ifp->if_capenable & IFCAP_POLLING) - ether_poll_deregister(ifp); + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_deregister_mq(ifp, i); #endif IGB_CORE_LOCK(adapter); @@ -738,8 +756,6 @@ igb_detach(device_t dev) if (adapter->vlan_detach != NULL) EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach); - ether_ifdetach(adapter->ifp); - callout_drain(&adapter->timer); igb_free_pci_resources(adapter); @@ -798,6 +814,7 @@ static int igb_resume(device_t dev) { struct adapter *adapter = device_get_softc(dev); + struct tx_ring *txr = adapter->tx_rings; struct ifnet *ifp = adapter->ifp; IGB_CORE_LOCK(adapter); @@ -805,9 +822,21 @@ igb_resume(device_t dev) igb_init_manageability(adapter); if ((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING)) - igb_start(ifp); - + (ifp->if_drv_flags & IFF_DRV_RUNNING) && adapter->link_active) { + for (int i = 0; i < adapter->num_queues; i++, txr++) { + IGB_TX_LOCK(txr); +#if __FreeBSD_version >= 800000 + /* Process the stack queue only if not depleted */ + if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && + !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); + } + } IGB_CORE_UNLOCK(adapter); return bus_generic_resume(dev); @@ -902,24 +931,36 @@ igb_mq_start(struct ifnet *ifp, struct mbuf *m) struct igb_queue *que; struct tx_ring *txr; int i, err = 0; - bool moveable = TRUE; /* Which queue to use */ - if ((m->m_flags & M_FLOWID) != 0) { + if ((m->m_flags & M_FLOWID) != 0) i = m->m_pkthdr.flowid % adapter->num_queues; - moveable = FALSE; - } else - i = curcpu % adapter->num_queues; + else + i = 0; txr = &adapter->tx_rings[i]; que = &adapter->queues[i]; - if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && - IGB_TX_TRYLOCK(txr)) { - err = igb_mq_start_locked(ifp, txr, m); - IGB_TX_UNLOCK(txr); - } else { + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (buf_ring_full(txr->br) && + (txr->queue_status & IGB_QUEUE_DEPLETED) == 0) { + IGB_TX_LOCK(txr); + igb_mq_start_locked(ifp, txr, NULL); + IGB_TX_UNLOCK(txr); + } err = drbr_enqueue(ifp, txr->br, m); - taskqueue_enqueue(que->tq, &que->que_task); + } else +#endif + { + if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && + IGB_TX_TRYLOCK(txr)) { + err = igb_mq_start_locked(ifp, txr, m); + IGB_TX_UNLOCK(txr); + } else { + err = drbr_enqueue(ifp, txr->br, m); + taskqueue_enqueue(que->tq, &txr->txq_task); + } } return (err); @@ -935,7 +976,7 @@ igb_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m) IGB_TX_LOCK_ASSERT(txr); if (((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) || - (txr->queue_status == IGB_QUEUE_DEPLETED) || + (txr->queue_status & IGB_QUEUE_DEPLETED) || adapter->link_active == 0) { if (m != NULL) err = drbr_enqueue(ifp, txr->br, m); @@ -979,6 +1020,22 @@ igb_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m) } /* + * Called from a taskqueue to drain queued transmit packets. + */ +static void +igb_deferred_mq_start(void *arg, int pending) +{ + struct tx_ring *txr = arg; + struct adapter *adapter = txr->adapter; + struct ifnet *ifp = adapter->ifp; + + IGB_TX_LOCK(txr); + if (!drbr_empty(ifp, txr->br)) + igb_mq_start_locked(ifp, txr, NULL); + IGB_TX_UNLOCK(txr); +} + +/* ** Flush all ring buffers */ static void @@ -1094,10 +1151,7 @@ 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_enable_intr(adapter); IGB_CORE_UNLOCK(adapter); } break; @@ -1125,20 +1179,24 @@ igb_ioctl(struct ifnet *ifp, u_long command, caddr_t data) mask = ifr->ifr_reqcap ^ ifp->if_capenable; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { + IGB_CORE_LOCK(adapter); + igb_disable_intr(adapter); + IGB_CORE_UNLOCK(adapter); if (ifr->ifr_reqcap & IFCAP_POLLING) { - error = ether_poll_register(igb_poll, ifp); - if (error) - return (error); + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_register_mq(igb_poll, + ifp, i, adapter->num_queues, + &adapter->queues[i]); IGB_CORE_LOCK(adapter); - igb_disable_intr(adapter); ifp->if_capenable |= IFCAP_POLLING; + igb_enable_intr(adapter); IGB_CORE_UNLOCK(adapter); } else { - error = ether_poll_deregister(ifp); - /* Enable interrupt even in error case */ + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_deregister_mq(ifp, i); IGB_CORE_LOCK(adapter); - igb_enable_intr(adapter); ifp->if_capenable &= ~IFCAP_POLLING; + igb_enable_intr(adapter); IGB_CORE_UNLOCK(adapter); } } @@ -1202,6 +1260,9 @@ igb_init_locked(struct adapter *adapter) INIT_DEBUGOUT("igb_init: begin"); IGB_CORE_LOCK_ASSERT(adapter); + /* Disable the stack. */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; igb_disable_intr(adapter); callout_stop(&adapter->timer); @@ -1266,34 +1327,26 @@ igb_init_locked(struct adapter *adapter) /* Don't lose promiscuous settings */ igb_set_promisc(adapter); - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - callout_reset(&adapter->timer, hz, igb_local_timer, adapter); e1000_clear_hw_cntrs_base_generic(&adapter->hw); +#ifndef DEVICE_POLLING + /* RSS hash is useful with one queue for load balancing. */ if (adapter->msix > 1) /* Set up queue routing */ +#endif igb_configure_queues(adapter); /* 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 */ - { - 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); /* Set Energy Efficient Ethernet */ + if (adapter->hw.phy.media_type == e1000_media_type_copper) + e1000_set_eee_i350(&adapter->hw); - e1000_set_eee_i350(&adapter->hw); + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void @@ -1318,22 +1371,22 @@ igb_handle_que(void *context, int pending) if (ifp->if_drv_flags & IFF_DRV_RUNNING) { bool more; - more = igb_rxeof(que, adapter->rx_process_limit, NULL); + more = igb_rxeof(que, adapter->rx_process_limit, NULL, NULL); IGB_TX_LOCK(txr); - if (igb_txeof(txr)) - more = TRUE; + igb_txeof(txr); #if __FreeBSD_version >= 800000 /* Process the stack queue only if not depleted */ if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && !drbr_empty(ifp, txr->br)) igb_mq_start_locked(ifp, txr, NULL); #else - igb_start_locked(txr, ifp); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + igb_start_locked(txr, ifp); #endif IGB_TX_UNLOCK(txr); /* Do we need another? */ - if (more || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + if (more) { taskqueue_enqueue(que->tq, &que->que_task); return; } @@ -1356,8 +1409,35 @@ igb_handle_link(void *context, int pending) { struct adapter *adapter = context; + IGB_CORE_LOCK(adapter); + igb_handle_link_locked(adapter); + IGB_CORE_UNLOCK(adapter); +} + +static void +igb_handle_link_locked(struct adapter *adapter) +{ + struct tx_ring *txr = adapter->tx_rings; + struct ifnet *ifp = adapter->ifp; + + IGB_CORE_LOCK_ASSERT(adapter); adapter->hw.mac.get_link_status = 1; igb_update_link_status(adapter); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && adapter->link_active) { + for (int i = 0; i < adapter->num_queues; i++, txr++) { + IGB_TX_LOCK(txr); +#if __FreeBSD_version >= 800000 + /* Process the stack queue only if not depleted */ + if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && + !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); + } + } } /********************************************************************* @@ -1405,59 +1485,47 @@ igb_irq_fast(void *arg) } #ifdef DEVICE_POLLING -/********************************************************************* - * - * 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) +/* + * Multi-queue polling code. + */ static int -#else -#define POLL_RETURN_COUNT(a) -static void -#endif -igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +igb_poll(struct ifnet *ifp, void *queue, enum poll_cmd cmd, + int count, struct mbuf **mc) { - 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; + struct igb_queue *que = (struct igb_queue *)queue; + struct tx_ring *txr = que->txr; bool more; - 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)) - igb_handle_link(adapter, 0); - - if (reg_icr & E1000_ICR_RXO) - adapter->rx_overruns++; - } - IGB_CORE_UNLOCK(adapter); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return (0); - igb_rxeof(que, count, &rx_done); + switch (cmd) { + case POLL_ONLY: + case POLL_AND_CHECK_STATUS: + more = igb_rxeof(que, count, NULL, mc); + break; - IGB_TX_LOCK(txr); - do { - more = igb_txeof(txr); - } while (loop-- && more); + case POLL_TX: + IGB_TX_LOCK(txr); #if __FreeBSD_version >= 800000 - if (!drbr_empty(ifp, txr->br)) - igb_mq_start_locked(ifp, txr, NULL); + /* Process the stack queue only if not depleted */ + if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && + !drbr_empty(ifp, txr->br)) + igb_mq_start_locked(ifp, txr, NULL); #else - igb_start_locked(txr, ifp); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + igb_start_locked(txr, ifp); #endif - IGB_TX_UNLOCK(txr); - return POLL_RETURN_COUNT(rx_done); + igb_txeof(txr); + IGB_TX_UNLOCK(txr); + more = FALSE; + break; + + default: + more = FALSE; + } + + return (more); } #endif /* DEVICE_POLLING */ @@ -1471,19 +1539,35 @@ igb_msix_que(void *arg) { struct igb_queue *que = arg; struct adapter *adapter = que->adapter; + struct ifnet *ifp = adapter->ifp; struct tx_ring *txr = que->txr; struct rx_ring *rxr = que->rxr; u32 newitr = 0; - bool more_tx, more_rx; + bool more_rx = FALSE; E1000_WRITE_REG(&adapter->hw, E1000_EIMC, que->eims); ++que->irqs; +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { IGB_TX_LOCK(txr); - more_tx = igb_txeof(txr); + igb_txeof(txr); +#if __FreeBSD_version >= 800000 + /* Process the stack queue only if not depleted */ + if (((txr->queue_status & IGB_QUEUE_DEPLETED) == 0) && + !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); - more_rx = igb_rxeof(que, adapter->rx_process_limit, NULL); + more_rx = igb_rxeof(que, adapter->rx_process_limit, NULL, NULL); if (adapter->enable_aim == FALSE) goto no_calc; @@ -1535,10 +1619,10 @@ igb_msix_que(void *arg) txr->packets = 0; rxr->bytes = 0; rxr->packets = 0; - + } no_calc: /* Schedule a clean task if needed*/ - if (more_tx || more_rx) + if (more_rx) taskqueue_enqueue(que->tq, &que->que_task); else /* Reenable this interrupt */ @@ -2090,6 +2174,9 @@ igb_local_timer(void *arg) if (txr->queue_status & IGB_QUEUE_DEPLETED) ++busy; if ((txr->queue_status & IGB_QUEUE_IDLE) == 0) +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif taskqueue_enqueue(que->tq, &que->que_task); } if (hung == adapter->num_queues) @@ -2102,10 +2189,11 @@ igb_local_timer(void *arg) adapter->pause_frames = 0; callout_reset(&adapter->timer, hz, igb_local_timer, adapter); -#ifndef DEVICE_POLLING +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif /* Schedule all queue interrupts - deadlock protection */ E1000_WRITE_REG(&adapter->hw, E1000_EICS, adapter->que_mask); -#endif return; timeout: @@ -2320,6 +2408,7 @@ igb_allocate_legacy(struct adapter *adapter) { device_t dev = adapter->dev; struct igb_queue *que = adapter->queues; + struct tx_ring *txr = adapter->tx_rings; int error, rid = 0; /* Turn off all interrupts */ @@ -2338,6 +2427,10 @@ igb_allocate_legacy(struct adapter *adapter) return (ENXIO); } +#if __FreeBSD_version >= 800000 + TASK_INIT(&txr->txq_task, 0, igb_deferred_mq_start, txr); +#endif + /* * Try allocating a fast interrupt and the associated deferred * processing contexts. @@ -2411,9 +2504,13 @@ igb_allocate_msix(struct adapter *adapter) */ if (adapter->num_queues > 1) bus_bind_intr(dev, que->res, i); +#if __FreeBSD_version >= 800000 + TASK_INIT(&que->txr->txq_task, 0, igb_deferred_mq_start, + que->txr); +#endif /* Make tasklet for deferred handling */ TASK_INIT(&que->que_task, 0, igb_handle_que, que); - que->tq = taskqueue_create_fast("igb_que", M_NOWAIT, + que->tq = taskqueue_create("igb_que", M_NOWAIT, taskqueue_thread_enqueue, &que->tq); taskqueue_start_threads(&que->tq, 1, PI_NET, "%s que", device_get_nameunit(adapter->dev)); @@ -2461,6 +2558,8 @@ igb_configure_queues(struct adapter *adapter) switch (adapter->hw.mac.type) { case e1000_82580: case e1000_i350: + case e1000_i210: + case e1000_i211: case e1000_vfadapt: case e1000_vfadapt_i350: /* RX entries */ @@ -2620,13 +2719,24 @@ igb_free_pci_resources(struct adapter *adapter) else (adapter->msix != 0) ? (rid = 1):(rid = 0); + que = adapter->queues; if (adapter->tag != NULL) { + taskqueue_drain(que->tq, &adapter->link_task); 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); + for (int i = 0; i < adapter->num_queues; i++, que++) { + if (que->tq != NULL) { +#if __FreeBSD_version >= 800000 + taskqueue_drain(que->tq, &que->txr->txq_task); +#endif + taskqueue_drain(que->tq, &que->que_task); + taskqueue_free(que->tq); + } + } mem: if (adapter->msix) pci_release_msi(dev); @@ -2648,7 +2758,7 @@ static int igb_setup_msix(struct adapter *adapter) { device_t dev = adapter->dev; - int rid, want, queues, msgs; + int rid, want, queues, msgs, maxqueues; /* tuneable override */ if (igb_enable_msix == 0) @@ -2679,16 +2789,29 @@ igb_setup_msix(struct adapter *adapter) /* Manual override */ if (igb_num_queues != 0) queues = igb_num_queues; - if (queues > 8) /* max queues */ - queues = 8; - - /* Can have max of 4 queues on 82575 */ - if ((adapter->hw.mac.type == e1000_82575) && (queues > 4)) - queues = 4; - /* Limit the VF devices to one queue */ - if (adapter->vf_ifp) - queues = 1; + /* Sanity check based on HW */ + switch (adapter->hw.mac.type) { + case e1000_82575: + maxqueues = 4; + break; + case e1000_82576: + case e1000_82580: + case e1000_i350: + maxqueues = 8; + break; + case e1000_i210: + maxqueues = 4; + break; + case e1000_i211: + maxqueues = 2; + break; + default: /* VF interfaces */ + maxqueues = 1; + break; + } + if (queues > maxqueues) + queues = maxqueues; /* ** One vector (RX/TX pair) per queue @@ -2757,6 +2880,9 @@ igb_reset(struct adapter *adapter) pba = E1000_READ_REG(hw, E1000_RXPBS); pba = e1000_rxpbs_adjust_82580(pba); break; + case e1000_i210: + case e1000_i211: + pba = E1000_PBA_34K; default: break; } @@ -2824,7 +2950,9 @@ igb_reset(struct adapter *adapter) device_printf(dev, "Hardware Initialization Failed\n"); /* Setup DMA Coalescing */ - if (hw->mac.type == e1000_i350) { + if ((hw->mac.type > e1000_82580) && + (hw->mac.type != e1000_i211)) { + u32 dmac; u32 reg = ~E1000_DMACR_DMAC_EN; if (adapter->dmac == 0) { /* Disabling it */ @@ -2832,26 +2960,36 @@ igb_reset(struct adapter *adapter) goto reset_out; } - hwm = (pba - 4) << 10; - reg = (((pba-6) << E1000_DMACR_DMACTHR_SHIFT) - & E1000_DMACR_DMACTHR_MASK); + /* Set starting thresholds */ + E1000_WRITE_REG(hw, E1000_DMCTXTH, 0); + E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); + hwm = 64 * pba - adapter->max_frame_size / 16; + if (hwm < 64 * (pba - 6)) + hwm = 64 * (pba - 6); + reg = E1000_READ_REG(hw, E1000_FCRTC); + reg &= ~E1000_FCRTC_RTH_COAL_MASK; + reg |= ((hwm << E1000_FCRTC_RTH_COAL_SHIFT) + & E1000_FCRTC_RTH_COAL_MASK); + E1000_WRITE_REG(hw, E1000_FCRTC, reg); + + + dmac = pba - adapter->max_frame_size / 512; + if (dmac < pba - 10) + dmac = pba - 10; + reg = E1000_READ_REG(hw, E1000_DMACR); + reg &= ~E1000_DMACR_DMACTHR_MASK; + reg = ((dmac << E1000_DMACR_DMACTHR_SHIFT) + & E1000_DMACR_DMACTHR_MASK); /* transition to L0x or L1 if available..*/ reg |= (E1000_DMACR_DMAC_EN | E1000_DMACR_DMAC_LX_MASK); - /* timer = value in adapter->dmac in 32usec intervals */ reg |= (adapter->dmac >> 5); E1000_WRITE_REG(hw, E1000_DMACR, reg); - /* No lower threshold */ - E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); - - /* set hwm to PBA - 2 * max frame size */ - E1000_WRITE_REG(hw, E1000_FCRTC, hwm); - /* Set the interval before transition */ reg = E1000_READ_REG(hw, E1000_DMCTLX); - reg |= 0x800000FF; /* 255 usec */ + reg |= 0x80000004; E1000_WRITE_REG(hw, E1000_DMCTLX, reg); /* free space in tx packet buffer to wake from DMA coal */ @@ -2860,9 +2998,15 @@ igb_reset(struct adapter *adapter) /* 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); + reg &= ~E1000_PCIEMISC_LX_DECISION; + E1000_WRITE_REG(hw, E1000_PCIEMISC, reg); device_printf(dev, "DMA Coalescing enabled\n"); + + } else if (hw->mac.type == e1000_82580) { + u32 reg = E1000_READ_REG(hw, E1000_PCIEMISC); + E1000_WRITE_REG(hw, E1000_DMACR, 0); + E1000_WRITE_REG(hw, E1000_PCIEMISC, + reg & ~E1000_PCIEMISC_LX_DECISION); } reset_out: @@ -4457,7 +4601,7 @@ igb_rx_input(struct rx_ring *rxr, struct ifnet *ifp, struct mbuf *m, u32 ptype) * Return TRUE if more to clean, FALSE otherwise *********************************************************************/ static bool -igb_rxeof(struct igb_queue *que, int count, int *done) +igb_rxeof(struct igb_queue *que, int count, int *done, struct mbuf **mc) { struct adapter *adapter = que->adapter; struct rx_ring *rxr = que->rxr; @@ -4467,6 +4611,7 @@ igb_rxeof(struct igb_queue *que, int count, int *done) int i, processed = 0, rxdone = 0; u32 ptype, staterr = 0; union e1000_adv_rx_desc *cur; + struct mbuf *mc_p = NULL; IGB_RX_LOCK(rxr); /* Sync the ring. */ @@ -4618,7 +4763,15 @@ next_desc: */ if (sendmp != NULL) { rxr->next_to_check = i; - igb_rx_input(rxr, ifp, sendmp, ptype); + if (mc != NULL) { + if (*mc == NULL) + *mc = mc_p = sendmp; + else { + mc_p->m_nextpkt = sendmp; + mc_p = sendmp; + } + } else + igb_rx_input(rxr, ifp, sendmp, ptype); i = rxr->next_to_check; rxdone++; } @@ -4810,6 +4963,12 @@ igb_enable_intr(struct adapter *adapter) /* With RSS set up what to auto clear */ if (adapter->msix_mem) { u32 mask = (adapter->que_mask | adapter->link_mask); +#ifdef DEVICE_POLLING + struct ifnet *ifp = adapter->ifp; + /* Remove queue interrupt only. */ + if (ifp->if_capenable & IFCAP_POLLING) + mask &= ~adapter->que_mask; +#endif E1000_WRITE_REG(&adapter->hw, E1000_EIAC, mask); E1000_WRITE_REG(&adapter->hw, E1000_EIAM, mask); E1000_WRITE_REG(&adapter->hw, E1000_EIMS, mask); @@ -5741,3 +5900,25 @@ igb_sysctl_dmac(SYSCTL_HANDLER_ARGS) igb_init(adapter); return (error); } + +/* +** Manage Energy Efficient Ethernet: +** Control values: +** 0/1 - enabled/disabled +*/ +static int +igb_sysctl_eee(SYSCTL_HANDLER_ARGS) +{ + struct adapter *adapter = (struct adapter *) arg1; + int error, value; + + value = adapter->hw.dev_spec._82575.eee_disable; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error || req->newptr == NULL) + return (error); + IGB_CORE_LOCK(adapter); + adapter->hw.dev_spec._82575.eee_disable = (value != 0); + igb_init_locked(adapter); + IGB_CORE_UNLOCK(adapter); + return (0); +} diff --git a/sys/dev/e1000/if_igb.h b/sys/dev/e1000/if_igb.h index 9989206..8ff1b44 100644 --- a/sys/dev/e1000/if_igb.h +++ b/sys/dev/e1000/if_igb.h @@ -301,6 +301,7 @@ struct tx_ring { struct buf_ring *br; #endif bus_dma_tag_t txtag; + struct task txq_task; u32 bytes; u32 packets; diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c index 61c9645..53d0ca6 100644 --- a/sys/dev/e1000/if_lem.c +++ b/sys/dev/e1000/if_lem.c @@ -1,6 +1,6 @@ /****************************************************************************** - Copyright (c) 2001-2011, Intel Corporation + Copyright (c) 2001-2012, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without @@ -85,7 +85,7 @@ /********************************************************************* * Legacy Em Driver version: *********************************************************************/ -char lem_driver_version[] = "1.0.4"; +char lem_driver_version[] = "1.0.5"; /********************************************************************* * PCI Device ID Table @@ -239,15 +239,12 @@ 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 */ static int lem_irq_fast(void *); 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; @@ -304,11 +301,13 @@ 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 +/* Interrupt style - default to fast */ +static int lem_use_legacy_irq = 0; +TUNABLE_INT("hw.em.use_legacy_irq", &lem_use_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; @@ -317,6 +316,7 @@ TUNABLE_INT("hw.em.fc_setting", &lem_fc_setting); /* Global used in WOL setup with multiport cards */ static int global_quad_port_a = 0; + /********************************************************************* * Device identification routine * @@ -446,12 +446,10 @@ lem_attach(device_t dev) 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 /* Sysctl for setting the interface flow control */ lem_set_flow_cntrl(adapter, "flow_control", @@ -1181,28 +1179,9 @@ lem_init_locked(struct adapter *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 @@ -1217,6 +1196,9 @@ lem_init_locked(struct adapter *adapter) /* AMT based hardware can now take control from firmware */ if (adapter->has_manage && adapter->has_amt) lem_get_hw_control(adapter); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void @@ -1271,7 +1253,6 @@ lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) } #endif /* DEVICE_POLLING */ -#ifdef EM_LEGACY_IRQ /********************************************************************* * * Legacy Interrupt Service routine @@ -1285,7 +1266,8 @@ lem_intr(void *arg) u32 reg_icr; - if (ifp->if_capenable & IFCAP_POLLING) + if ((ifp->if_capenable & IFCAP_POLLING) || + ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)) return; EM_CORE_LOCK(adapter); @@ -1293,11 +1275,10 @@ lem_intr(void *arg) 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 == 0xffffffff) || (reg_icr == 0)) { + EM_CORE_UNLOCK(adapter); + return; + } if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { callout_stop(&adapter->timer); @@ -1307,23 +1288,22 @@ lem_intr(void *arg) lem_tx_purge(adapter); callout_reset(&adapter->timer, hz, lem_local_timer, adapter); - goto out; + EM_CORE_UNLOCK(adapter); + return; } - EM_TX_LOCK(adapter); + EM_CORE_UNLOCK(adapter); lem_rxeof(adapter, -1, NULL); + + EM_TX_LOCK(adapter); 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) @@ -1407,7 +1387,6 @@ lem_irq_fast(void *arg) adapter->rx_overruns++; return FILTER_HANDLED; } -#endif /* ~EM_LEGACY_IRQ */ /********************************************************************* @@ -2204,19 +2183,21 @@ lem_allocate_irq(struct adapter *adapter) return (ENXIO); } -#ifdef EM_LEGACY_IRQ - /* We do Legacy setup */ - if ((error = bus_setup_intr(dev, adapter->res[0], - INTR_TYPE_NET | INTR_MPSAFE, NULL, lem_intr, adapter, - &adapter->tag[0])) != 0) { - device_printf(dev, "Failed to register interrupt handler"); - return (error); + /* Do Legacy setup? */ + if (lem_use_legacy_irq) { + if ((error = bus_setup_intr(dev, adapter->res[0], + INTR_TYPE_NET | INTR_MPSAFE, NULL, lem_intr, adapter, + &adapter->tag[0])) != 0) { + device_printf(dev, + "Failed to register interrupt handler"); + return (error); + } + return (0); } -#else /* FAST_IRQ */ /* - * Try allocating a fast interrupt and the associated deferred - * processing contexts. + * Use 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); @@ -2233,7 +2214,6 @@ lem_allocate_irq(struct adapter *adapter) adapter->tq = NULL; return (error); } -#endif /* EM_LEGACY_IRQ */ return (0); } @@ -2655,6 +2635,7 @@ lem_setup_transmit_structures(struct adapter *adapter) } /* Reset state */ + adapter->last_hw_offload = 0; adapter->next_avail_tx_desc = 0; adapter->next_tx_to_clean = 0; adapter->num_tx_desc_avail = adapter->num_tx_desc; @@ -3247,20 +3228,6 @@ lem_initialize_receive_unit(struct adapter *adapter) 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), @@ -3758,10 +3725,6 @@ 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); } @@ -3770,9 +3733,7 @@ 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); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); } /* @@ -4606,7 +4567,6 @@ lem_set_flow_cntrl(struct adapter *adapter, const char *name, OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, 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) @@ -4616,4 +4576,3 @@ lem_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/ixgbe/ixgbe.c b/sys/dev/ixgbe/ixgbe.c index 762d9f4..3f57eac 100644 --- a/sys/dev/ixgbe/ixgbe.c +++ b/sys/dev/ixgbe/ixgbe.c @@ -33,6 +33,7 @@ /*$FreeBSD$*/ #ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" #include "opt_inet.h" #include "opt_inet6.h" #endif @@ -47,7 +48,7 @@ int ixgbe_display_debug_stats = 0; /********************************************************************* * Driver version *********************************************************************/ -char ixgbe_driver_version[] = "2.4.5"; +char ixgbe_driver_version[] = "2.4.8"; /********************************************************************* * PCI Device ID Table @@ -80,8 +81,10 @@ static ixgbe_vendor_info_t ixgbe_vendor_info_array[] = {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_T3_LOM, 0, 0, 0}, {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_COMBO_BACKPLANE, 0, 0, 0}, {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_BACKPLANE_FCOE, 0, 0, 0}, + {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_SF2, 0, 0, 0}, {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_FCOE, 0, 0, 0}, {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599EN_SFP, 0, 0, 0}, + {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T1, 0, 0, 0}, {IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T, 0, 0, 0}, /* required last entry */ {0, 0, 0, 0, 0} @@ -146,7 +149,7 @@ static void ixgbe_enable_intr(struct adapter *); static void ixgbe_disable_intr(struct adapter *); static void ixgbe_update_stats_counters(struct adapter *); static bool ixgbe_txeof(struct tx_ring *); -static bool ixgbe_rxeof(struct ix_queue *, int); +static bool ixgbe_rxeof(struct ix_queue *, int, struct mbuf **); static void ixgbe_rx_checksum(u32, struct mbuf *, u32); static void ixgbe_set_promisc(struct adapter *); static void ixgbe_set_multi(struct adapter *); @@ -177,6 +180,8 @@ static __inline void ixgbe_rx_discard(struct rx_ring *, int); static __inline void ixgbe_rx_input(struct rx_ring *, struct ifnet *, struct mbuf *, u32); +static inline void ixgbe_disable_queue(struct adapter *adapter, u32 vector); + /* Support for pluggable optic modules */ static bool ixgbe_sfp_probe(struct adapter *); static void ixgbe_setup_optics(struct adapter *); @@ -199,6 +204,10 @@ static void ixgbe_atr(struct tx_ring *, struct mbuf *); static void ixgbe_reinit_fdir(void *, int); #endif +#ifdef DEVICE_POLLING +static poll_handler_mq_t ixgbe_poll; +#endif + /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ @@ -242,10 +251,6 @@ TUNABLE_INT("hw.ixgbe.max_interrupt_rate", &ixgbe_max_interrupt_rate); static int ixgbe_rx_process_limit = 128; TUNABLE_INT("hw.ixgbe.rx_process_limit", &ixgbe_rx_process_limit); -/* Flow control setting, default to full */ -static int ixgbe_flow_control = ixgbe_fc_full; -TUNABLE_INT("hw.ixgbe.flow_control", &ixgbe_flow_control); - /* ** Smart speed setting, default to on ** this only works as a compile option @@ -409,11 +414,6 @@ ixgbe_attach(device_t dev) OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW, adapter, 0, ixgbe_set_flowcntl, "I", "Flow Control"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), - OID_AUTO, "advertise_gig", CTLTYPE_INT | CTLFLAG_RW, - adapter, 0, ixgbe_set_advertise, "I", "1G Link"); - SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "enable_aim", CTLTYPE_INT|CTLFLAG_RW, @@ -519,28 +519,25 @@ ixgbe_attach(device_t dev) goto err_late; } - /* Get Hardware Flow Control setting */ - hw->fc.requested_mode = ixgbe_fc_full; - adapter->fc = hw->fc.requested_mode; - hw->fc.pause_time = IXGBE_FC_PAUSE; - hw->fc.low_water = IXGBE_FC_LO; - hw->fc.high_water[0] = IXGBE_FC_HI; - hw->fc.send_xon = TRUE; - error = ixgbe_init_hw(hw); - if (error == IXGBE_ERR_EEPROM_VERSION) { + switch (error) { + case IXGBE_ERR_EEPROM_VERSION: device_printf(dev, "This device is a pre-production adapter/" "LOM. Please be aware there may be issues associated " "with your hardware.\n If you are experiencing problems " "please contact your Intel or hardware representative " "who provided you with this hardware.\n"); - } else if (error == IXGBE_ERR_SFP_NOT_SUPPORTED) + break; + case IXGBE_ERR_SFP_NOT_SUPPORTED: device_printf(dev,"Unsupported SFP+ Module\n"); - - if (error) { error = EIO; device_printf(dev,"Hardware Initialization Failure\n"); goto err_late; + case IXGBE_ERR_SFP_NOT_PRESENT: + device_printf(dev,"No SFP+ Module found\n"); + /* falls thru */ + default: + break; } /* Detect and set physical type */ @@ -630,6 +627,11 @@ ixgbe_detach(device_t dev) INIT_DEBUGOUT("ixgbe_detach: begin"); +#ifdef DEVICE_POLLING + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_deregister_mq(adapter->ifp, i); +#endif + /* Make sure VLANS are not using driver */ if (adapter->ifp->if_vlantrunk != NULL) { device_printf(dev,"Vlan in use, detach first\n"); @@ -718,11 +720,13 @@ ixgbe_start_locked(struct tx_ring *txr, struct ifnet * ifp) IXGBE_TX_LOCK_ASSERT(txr); - if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != - IFF_DRV_RUNNING) - return; - if (!adapter->link_active) - return; + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || + adapter->link_active == 0) + return; + + /* Call cleanup if number of TX descriptors low */ + if (txr->tx_avail <= IXGBE_TX_CLEANUP_THRESHOLD) + ixgbe_txeof(txr); while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { if (txr->tx_avail <= IXGBE_QUEUE_MIN_FREE) { @@ -788,18 +792,31 @@ ixgbe_mq_start(struct ifnet *ifp, struct mbuf *m) if ((m->m_flags & M_FLOWID) != 0) i = m->m_pkthdr.flowid % adapter->num_queues; else - i = curcpu % adapter->num_queues; + i = 0; txr = &adapter->tx_rings[i]; que = &adapter->queues[i]; - if (((txr->queue_status & IXGBE_QUEUE_DEPLETED) == 0) && - IXGBE_TX_TRYLOCK(txr)) { - err = ixgbe_mq_start_locked(ifp, txr, m); - IXGBE_TX_UNLOCK(txr); - } else { +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (buf_ring_full(txr->br) && + (txr->queue_status & IXGBE_QUEUE_DEPLETED) == 0) { + IXGBE_TX_LOCK(txr); + ixgbe_mq_start_locked(ifp, txr, NULL); + IXGBE_TX_UNLOCK(txr); + } err = drbr_enqueue(ifp, txr->br, m); - taskqueue_enqueue(que->tq, &que->que_task); + } else +#endif + { + if (((txr->queue_status & IXGBE_QUEUE_DEPLETED) == 0) && + IXGBE_TX_TRYLOCK(txr)) { + err = ixgbe_mq_start_locked(ifp, txr, m); + IXGBE_TX_UNLOCK(txr); + } else { + err = drbr_enqueue(ifp, txr->br, m); + taskqueue_enqueue(que->tq, &que->que_task); + } } return (err); @@ -813,7 +830,7 @@ ixgbe_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m) int enqueued, err = 0; if (((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) || - (txr->queue_status == IXGBE_QUEUE_DEPLETED) || + (txr->queue_status & IXGBE_QUEUE_DEPLETED) || adapter->link_active == 0) { if (m != NULL) err = drbr_enqueue(ifp, txr->br, m); @@ -984,6 +1001,30 @@ ixgbe_ioctl(struct ifnet * ifp, u_long command, caddr_t data) { int mask = ifr->ifr_reqcap ^ ifp->if_capenable; IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); +#ifdef DEVICE_POLLING + if (mask & IFCAP_POLLING) { + IXGBE_CORE_LOCK(adapter); + ixgbe_disable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); + if (ifr->ifr_reqcap & IFCAP_POLLING) { + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_register_mq(ixgbe_poll, + ifp, i, adapter->num_queues, + &adapter->queues[i]); + IXGBE_CORE_LOCK(adapter); + ifp->if_capenable |= IFCAP_POLLING; + ixgbe_enable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); + } else { + for (int i = 0; i < adapter->num_queues; i++) + ether_poll_deregister_mq(ifp, i); + IXGBE_CORE_LOCK(adapter); + ifp->if_capenable &= ~IFCAP_POLLING; + ixgbe_enable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); + } + } +#endif /* DEVICE_POLLING */ if (mask & IFCAP_HWCSUM) ifp->if_capenable ^= IFCAP_HWCSUM; if (mask & IFCAP_TSO4) @@ -1037,6 +1078,10 @@ ixgbe_init_locked(struct adapter *adapter) mtx_assert(&adapter->core_mtx, MA_OWNED); INIT_DEBUGOUT("ixgbe_init: begin"); + /* Disable the stack. */ + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + hw->adapter_stopped = FALSE; ixgbe_stop_adapter(hw); callout_stop(&adapter->timer); @@ -1134,6 +1179,14 @@ ixgbe_init_locked(struct adapter *adapter) txdctl |= IXGBE_TXDCTL_ENABLE; /* Set WTHRESH to 8, burst writeback */ txdctl |= (8 << 16); + /* + * When the internal queue falls below PTHRESH (32), + * start prefetching as long as there are at least + * HTHRESH (1) buffers ready. The values are taken + * from the Intel linux driver 3.8.21. + * Prefetching enables tx line rate even with 1 queue. + */ + txdctl |= (32 << 0) | (1 << 8); IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(i), txdctl); } @@ -1192,7 +1245,7 @@ ixgbe_init_locked(struct adapter *adapter) #ifdef IXGBE_FDIR /* Init Flow director */ if (hw->mac.type != ixgbe_mac_82598EB) { - u32 hdrm = 64 << fdir_pballoc; + u32 hdrm = 32 << fdir_pballoc; hw->mac.ops.setup_rxpba(hw, 0, hdrm, PBA_STRATEGY_EQUAL); ixgbe_init_fdir_signature_82599(&adapter->hw, fdir_pballoc); @@ -1218,6 +1271,35 @@ ixgbe_init_locked(struct adapter *adapter) /* Config/Enable Link */ ixgbe_config_link(adapter); + /* Hardware Packet Buffer & Flow Control setup */ + { + u32 rxpb, frame, size, tmp; + + frame = adapter->max_frame_size; + + /* Calculate High Water */ + if (hw->mac.type == ixgbe_mac_X540) + tmp = IXGBE_DV_X540(frame, frame); + else + tmp = IXGBE_DV(frame, frame); + size = IXGBE_BT2KB(tmp); + rxpb = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) >> 10; + hw->fc.high_water[0] = rxpb - size; + + /* Now calculate Low Water */ + if (hw->mac.type == ixgbe_mac_X540) + tmp = IXGBE_LOW_DV_X540(frame); + else + tmp = IXGBE_LOW_DV(frame); + hw->fc.low_water[0] = IXGBE_BT2KB(tmp); + + adapter->fc = hw->fc.requested_mode = ixgbe_fc_full; + hw->fc.pause_time = IXGBE_FC_PAUSE; + hw->fc.send_xon = TRUE; + } + /* Initialize the FC settings */ + ixgbe_start_hw(hw); + /* And now turn on interrupts */ ixgbe_enable_intr(adapter); @@ -1240,6 +1322,51 @@ ixgbe_init(void *arg) } +#ifdef DEVICE_POLLING +/* + * Multi-queue polling code. + */ +static int +ixgbe_poll(struct ifnet *ifp, void *queue, enum poll_cmd cmd, + int count, struct mbuf **mc) +{ + struct ix_queue *que = (struct ix_queue *)queue; + struct tx_ring *txr = que->txr; + bool more; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return (0); + + switch (cmd) { + case POLL_ONLY: + case POLL_AND_CHECK_STATUS: + more = ixgbe_rxeof(que, count, mc); + break; + + case POLL_TX: + IXGBE_TX_LOCK(txr); +#if __FreeBSD_version >= 800000 + if ((txr->queue_status & IXGBE_QUEUE_DEPLETED) == 0 && + !drbr_empty(ifp, txr->br)) + ixgbe_mq_start_locked(ifp, txr, NULL); +#else + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + ixgbe_start_locked(txr, ifp); +#endif + ixgbe_txeof(txr); + IXGBE_TX_UNLOCK(txr); + more = FALSE; + break; + + default: + more = FALSE; + } + + return (more); +} +#endif /* DEVICE_POLLING */ + + /* ** ** MSIX Interrupt Handlers and Tasklets @@ -1249,10 +1376,21 @@ ixgbe_init(void *arg) static inline void ixgbe_enable_queue(struct adapter *adapter, u32 vector) { +#ifdef DEVICE_POLLING + struct ifnet *ifp = adapter->ifp; +#endif struct ixgbe_hw *hw = &adapter->hw; u64 queue = (u64)(1 << vector); u32 mask; +#ifdef DEVICE_POLLING + /* Disable queue intr for polling. */ + if (ifp->if_capenable & IFCAP_POLLING) { + ixgbe_disable_queue(adapter, vector); + return; + } +#endif + if (hw->mac.type == ixgbe_mac_82598EB) { mask = (IXGBE_EIMS_RTX_QUEUE & queue); IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask); @@ -1289,8 +1427,17 @@ ixgbe_disable_queue(struct adapter *adapter, u32 vector) static inline void ixgbe_rearm_queues(struct adapter *adapter, u64 queues) { +#ifdef DEVICE_POLLING + struct ifnet *ifp = adapter->ifp; +#endif u32 mask; +#ifdef DEVICE_POLLING + /* Disable queue intr for polling. */ + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif + if (adapter->hw.mac.type == ixgbe_mac_82598EB) { mask = (IXGBE_EIMS_RTX_QUEUE & queues); IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, mask); @@ -1313,7 +1460,7 @@ ixgbe_handle_que(void *context, int pending) bool more; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - more = ixgbe_rxeof(que, adapter->rx_process_limit); + more = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); ixgbe_txeof(txr); #if __FreeBSD_version >= 800000 @@ -1324,7 +1471,7 @@ ixgbe_handle_que(void *context, int pending) ixgbe_start_locked(txr, ifp); #endif IXGBE_TX_UNLOCK(txr); - if (more || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + if (more) { taskqueue_enqueue(que->tq, &que->que_task); return; } @@ -1361,7 +1508,7 @@ ixgbe_legacy_irq(void *arg) return; } - more_rx = ixgbe_rxeof(que, adapter->rx_process_limit); + more_rx = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); do { @@ -1399,15 +1546,21 @@ ixgbe_msix_que(void *arg) { struct ix_queue *que = arg; struct adapter *adapter = que->adapter; + struct ifnet *ifp = adapter->ifp; struct tx_ring *txr = que->txr; struct rx_ring *rxr = que->rxr; - bool more_tx, more_rx; + bool more_tx = FALSE, more_rx = FALSE; u32 newitr = 0; ixgbe_disable_queue(adapter, que->msix); ++que->irqs; +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + return; +#endif - more_rx = ixgbe_rxeof(que, adapter->rx_process_limit); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + more_rx = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); more_tx = ixgbe_txeof(txr); @@ -1473,7 +1626,7 @@ ixgbe_msix_que(void *arg) txr->packets = 0; rxr->bytes = 0; rxr->packets = 0; - + } no_calc: if (more_tx || more_rx) taskqueue_enqueue(que->tq, &que->que_task); @@ -1507,10 +1660,8 @@ ixgbe_msix_link(void *arg) /* This is probably overkill :) */ if (!atomic_cmpset_int(&adapter->fdir_reinit, 0, 1)) return; - /* Clear the interrupt */ - IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_FLOW_DIR); - /* Turn off the interface */ - adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + /* Disable the interrupt */ + IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EICR_FLOW_DIR); taskqueue_enqueue(adapter->tq, &adapter->fdir_task); } else #endif @@ -1654,6 +1805,10 @@ ixgbe_xmit(struct tx_ring *txr, struct mbuf **m_headp) m_head = *m_headp; + /* Do a clean if descriptors are low */ + if (txr->tx_avail <= IXGBE_TX_CLEANUP_THRESHOLD) + ixgbe_txeof(txr); + /* Basic descriptor defines */ cmd_type_len = (IXGBE_ADVTXD_DTYP_DATA | IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT); @@ -1962,8 +2117,12 @@ ixgbe_local_timer(void *arg) ++hung; if (txr->queue_status & IXGBE_QUEUE_DEPLETED) ++busy; - if ((txr->queue_status & IXGBE_QUEUE_IDLE) == 0) + if ((txr->queue_status & IXGBE_QUEUE_IDLE) == 0) { +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif taskqueue_enqueue(que->tq, &que->que_task); + } } /* Only truely watchdog if all queues show hung */ if (hung == adapter->num_queues) @@ -2013,6 +2172,8 @@ ixgbe_update_link_status(struct adapter *adapter) ((adapter->link_speed == 128)? 10:1), "Full Duplex"); adapter->link_active = TRUE; + /* Update any Flow Control changes */ + ixgbe_fc_enable(&adapter->hw); if_link_state_change(ifp, LINK_STATE_UP); } } else { /* Link down */ @@ -2528,6 +2689,9 @@ ixgbe_setup_interface(device_t dev, struct adapter *adapter) | IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif /* Don't enable LRO by default */ ifp->if_capabilities |= IFCAP_LRO; @@ -2993,7 +3157,7 @@ ixgbe_initialize_transmit_units(struct adapter *adapter) txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i)); break; } - txctrl &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; switch (hw->mac.type) { case ixgbe_mac_82598EB: IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(i), txctrl); @@ -3407,10 +3571,12 @@ ixgbe_txeof(struct tx_ring *txr) tx_buffer = &txr->tx_buffers[first]; /* For cleanup we just use legacy struct */ tx_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[first]; + prefetch(tx_desc); last = tx_buffer->eop_index; if (last == -1) return FALSE; eop_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[last]; + prefetch(eop_desc); /* ** Get the index of the first descriptor @@ -3457,6 +3623,7 @@ ixgbe_txeof(struct tx_ring *txr) tx_buffer = &txr->tx_buffers[first]; tx_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[first]; + prefetch(tx_desc); } ++txr->packets; ++ifp->if_opackets; @@ -3465,6 +3632,7 @@ ixgbe_txeof(struct tx_ring *txr) if (last != -1) { eop_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[last]; + prefetch(eop_desc); /* Get next done point */ if (++last == adapter->num_tx_desc) last = 0; done = last; @@ -4011,7 +4179,12 @@ ixgbe_initialize_receive_units(struct adapter *adapter) rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM); /* Setup RSS */ +#ifndef DEVICE_POLLING + /* RSS hash is useful with one queue for load balancing. */ if (adapter->num_queues > 1) { +#else + { +#endif int i, j; reta = 0; @@ -4218,7 +4391,7 @@ ixgbe_rx_discard(struct rx_ring *rxr, int i) * Return TRUE for more work, FALSE for all clean. *********************************************************************/ static bool -ixgbe_rxeof(struct ix_queue *que, int count) +ixgbe_rxeof(struct ix_queue *que, int count, struct mbuf **mc) { struct adapter *adapter = que->adapter; struct rx_ring *rxr = que->rxr; @@ -4229,6 +4402,7 @@ ixgbe_rxeof(struct ix_queue *que, int count) u32 staterr = 0; union ixgbe_adv_rx_desc *cur; struct ixgbe_rx_buf *rbuf, *nbuf; + struct mbuf *mc_p = NULL; IXGBE_RX_LOCK(rxr); @@ -4244,6 +4418,7 @@ ixgbe_rxeof(struct ix_queue *que, int count) BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); cur = &rxr->rx_base[i]; + prefetch(cur); staterr = le32toh(cur->wb.upper.status_error); if ((staterr & IXGBE_RXD_STAT_DD) == 0) @@ -4261,8 +4436,7 @@ ixgbe_rxeof(struct ix_queue *que, int count) mp = rbuf->m_pack; plen = le16toh(cur->wb.upper.length); - ptype = le32toh(cur->wb.lower.lo_dword.data) & - IXGBE_RXDADV_PKTTYPE_MASK; + ptype = le32toh(cur->wb.lower.lo_dword.data) & IXGBE_RXDADV_PKTTYPE_MASK; hdr = le16toh(cur->wb.lower.lo_dword.hs_rss.hdr_info); eop = ((staterr & IXGBE_RXD_STAT_EOP) != 0); @@ -4388,6 +4562,7 @@ ixgbe_rxeof(struct ix_queue *que, int count) ** that determines what we are */ sendmp = rbuf->fmp; + prefetch(sendmp); rbuf->m_pack = rbuf->fmp = NULL; if (sendmp != NULL) { /* secondary frag */ @@ -4437,7 +4612,15 @@ next_desc: /* Now send to the stack or do LRO */ if (sendmp != NULL) { rxr->next_to_check = i; - ixgbe_rx_input(rxr, ifp, sendmp, ptype); + if (mc != NULL) { + if (*mc == NULL) + *mc = mc_p = sendmp; + else { + mc_p->m_nextpkt = sendmp; + mc_p = sendmp; + } + } else + ixgbe_rx_input(rxr, ifp, sendmp, ptype); i = rxr->next_to_check; } @@ -4889,6 +5072,8 @@ ixgbe_reinit_fdir(void *context, int pending) return; ixgbe_reinit_fdir_tables_82599(&adapter->hw); adapter->fdir_reinit = 0; + /* re-enable flow director interrupts */ + IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR); /* Restart the interface */ ifp->if_drv_flags |= IFF_DRV_RUNNING; return; @@ -5185,15 +5370,16 @@ ixgbe_add_hw_stats(struct adapter *adapter) queue_list = SYSCTL_CHILDREN(queue_node); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "interrupt_rate", - CTLFLAG_RD, &adapter->queues[i], sizeof(&adapter->queues[i]), + CTLTYPE_UINT | CTLFLAG_RD, &adapter->queues[i], + sizeof(&adapter->queues[i]), ixgbe_sysctl_interrupt_rate_handler, "IU", "Interrupt Rate"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_head", - CTLFLAG_RD, txr, sizeof(txr), + CTLTYPE_UINT | CTLFLAG_RD, txr, sizeof(txr), ixgbe_sysctl_tdh_handler, "IU", "Transmit Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_tail", - CTLFLAG_RD, txr, sizeof(txr), + CTLTYPE_UINT | CTLFLAG_RD, txr, sizeof(txr), ixgbe_sysctl_tdt_handler, "IU", "Transmit Descriptor Tail"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", @@ -5218,11 +5404,11 @@ ixgbe_add_hw_stats(struct adapter *adapter) queue_list = SYSCTL_CHILDREN(queue_node); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_head", - CTLFLAG_RD, rxr, sizeof(rxr), + CTLTYPE_UINT | CTLFLAG_RD, rxr, sizeof(rxr), ixgbe_sysctl_rdh_handler, "IU", "Receive Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_tail", - CTLFLAG_RD, rxr, sizeof(rxr), + CTLTYPE_UINT | CTLFLAG_RD, rxr, sizeof(rxr), ixgbe_sysctl_rdt_handler, "IU", "Receive Descriptor Tail"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_packets", @@ -5433,8 +5619,9 @@ ixgbe_set_flowcntl(SYSCTL_HANDLER_ARGS) default: adapter->hw.fc.requested_mode = ixgbe_fc_none; } - - ixgbe_fc_enable(&adapter->hw, 0); + /* Don't autoneg if forcing a value */ + adapter->hw.fc.disable_fc_autoneg = TRUE; + ixgbe_fc_enable(&adapter->hw); return error; } @@ -5450,9 +5637,9 @@ ixgbe_add_rx_process_limit(struct adapter *adapter, const char *name, /* ** Control link advertise speed: -** 0 - normal ** 1 - advertise only 1G ** 2 - advertise 100Mb +** 3 - advertise normal */ static int ixgbe_set_advertise(SYSCTL_HANDLER_ARGS) @@ -5466,13 +5653,15 @@ ixgbe_set_advertise(SYSCTL_HANDLER_ARGS) adapter = (struct adapter *) arg1; dev = adapter->dev; hw = &adapter->hw; - last = hw->phy.autoneg_advertised; + last = adapter->advertise; error = sysctl_handle_int(oidp, &adapter->advertise, 0, req); - if ((error) || (adapter->advertise == -1)) return (error); + if (adapter->advertise == last) /* no change */ + return (0); + if (!((hw->phy.media_type == ixgbe_media_type_copper) || (hw->phy.multispeed_fiber))) return (error); @@ -5486,11 +5675,10 @@ ixgbe_set_advertise(SYSCTL_HANDLER_ARGS) speed = IXGBE_LINK_SPEED_1GB_FULL; else if (adapter->advertise == 2) speed = IXGBE_LINK_SPEED_100_FULL; - else + else if (adapter->advertise == 3) speed = IXGBE_LINK_SPEED_1GB_FULL | IXGBE_LINK_SPEED_10GB_FULL; - - if (speed == last) /* no change */ + else /* bogus value */ return (error); hw->mac.autotry_restart = TRUE; diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h index bffde72..91818b3 100644 --- a/sys/dev/ixgbe/ixgbe.h +++ b/sys/dev/ixgbe/ixgbe.h @@ -144,7 +144,7 @@ * This parameters control when the driver calls the routine to reclaim * transmit descriptors. */ -#define IXGBE_TX_CLEANUP_THRESHOLD (adapter->num_tx_desc / 8) +#define IXGBE_TX_CLEANUP_THRESHOLD (adapter->num_tx_desc / 2) #define IXGBE_TX_OP_THRESHOLD (adapter->num_tx_desc / 32) #define IXGBE_MAX_FRAME_SIZE 0x3F00 diff --git a/sys/dev/ixgbe/ixgbe_82598.c b/sys/dev/ixgbe/ixgbe_82598.c index ab41c7b..9d3164c 100644 --- a/sys/dev/ixgbe/ixgbe_82598.c +++ b/sys/dev/ixgbe/ixgbe_82598.c @@ -104,31 +104,6 @@ out: } /** - * ixgbe_get_pcie_msix_count_82598 - Gets MSI-X vector count - * @hw: pointer to hardware structure - * - * Read PCIe configuration space, and get the MSI-X vector count from - * the capabilities table. - **/ -u32 ixgbe_get_pcie_msix_count_82598(struct ixgbe_hw *hw) -{ - u32 msix_count = 18; - - DEBUGFUNC("ixgbe_get_pcie_msix_count_82598"); - - if (hw->mac.msix_vectors_from_pcie) { - msix_count = IXGBE_READ_PCIE_WORD(hw, - IXGBE_PCIE_MSIX_82598_CAPS); - msix_count &= IXGBE_PCIE_MSIX_TBL_SZ_MASK; - - /* MSI-X count is zero-based in HW, so increment to give - * proper value */ - msix_count++; - } - return msix_count; -} - -/** * ixgbe_init_ops_82598 - Inits func ptrs and MAC type * @hw: pointer to hardware structure * @@ -176,7 +151,7 @@ s32 ixgbe_init_ops_82598(struct ixgbe_hw *hw) mac->rx_pb_size = 512; mac->max_tx_queues = 32; mac->max_rx_queues = 64; - mac->max_msix_vectors = ixgbe_get_pcie_msix_count_82598(hw); + mac->max_msix_vectors = ixgbe_get_pcie_msix_count_generic(hw); /* SFP+ Module */ phy->ops.read_i2c_eeprom = &ixgbe_read_i2c_eeprom_82598; @@ -280,15 +255,15 @@ s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw) for (i = 0; ((i < hw->mac.max_tx_queues) && (i < IXGBE_DCA_MAX_QUEUES_82598)); i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(i)); - regval &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + regval &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(i), regval); } for (i = 0; ((i < hw->mac.max_rx_queues) && (i < IXGBE_DCA_MAX_QUEUES_82598)); i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i)); - regval &= ~(IXGBE_DCA_RXCTRL_DESC_WRO_EN | - IXGBE_DCA_RXCTRL_DESC_HSRO_EN); + regval &= ~(IXGBE_DCA_RXCTRL_DATA_WRO_EN | + IXGBE_DCA_RXCTRL_HEAD_WRO_EN); IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval); } @@ -416,21 +391,41 @@ out: /** * ixgbe_fc_enable_82598 - Enable flow control * @hw: pointer to hardware structure - * @packetbuf_num: packet buffer number (0-7) * * Enable flow control according to the current settings. **/ -s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num) +s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw) { s32 ret_val = IXGBE_SUCCESS; u32 fctrl_reg; u32 rmcs_reg; u32 reg; + u32 fcrtl, fcrth; u32 link_speed = 0; + int i; bool link_up; DEBUGFUNC("ixgbe_fc_enable_82598"); + /* Validate the water mark configuration */ + if (!hw->fc.pause_time) { + ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + + /* Low water mark of zero causes XOFF floods */ + for (i = 0; i < IXGBE_DCB_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & ixgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + if (!hw->fc.low_water[i] || + hw->fc.low_water[i] >= hw->fc.high_water[i]) { + DEBUGOUT("Invalid water mark configuration\n"); + ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + } + } + /* * On 82598 having Rx FC on causes resets while doing 1G * so if it's on turn it off once we know link_speed. For @@ -452,9 +447,7 @@ s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num) } /* Negotiate the fc mode to use */ - ret_val = ixgbe_fc_autoneg(hw); - if (ret_val == IXGBE_ERR_FLOW_CONTROL) - goto out; + ixgbe_fc_autoneg(hw); /* Disable any previous flow control settings */ fctrl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL); @@ -516,28 +509,27 @@ s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num) IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg); /* Set up and enable Rx high/low water mark thresholds, enable XON. */ - if (hw->fc.current_mode & ixgbe_fc_tx_pause) { - reg = hw->fc.low_water << 6; - if (hw->fc.send_xon) - reg |= IXGBE_FCRTL_XONE; - - IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num), reg); - - reg = hw->fc.high_water[packetbuf_num] << 6; - reg |= IXGBE_FCRTH_FCEN; + for (i = 0; i < IXGBE_DCB_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & ixgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE; + fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN; + IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), fcrtl); + IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), fcrth); + } else { + IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), 0); + IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), 0); + } - IXGBE_WRITE_REG(hw, IXGBE_FCRTH(packetbuf_num), reg); } /* Configure pause time (2 TCs per register) */ - reg = IXGBE_READ_REG(hw, IXGBE_FCTTV(packetbuf_num / 2)); - if ((packetbuf_num & 1) == 0) - reg = (reg & 0xFFFF0000) | hw->fc.pause_time; - else - reg = (reg & 0x0000FFFF) | (hw->fc.pause_time << 16); - IXGBE_WRITE_REG(hw, IXGBE_FCTTV(packetbuf_num / 2), reg); + reg = hw->fc.pause_time * 0x00010001; + for (i = 0; i < (IXGBE_DCB_MAX_TRAFFIC_CLASS / 2); i++) + IXGBE_WRITE_REG(hw, IXGBE_FCTTV(i), reg); - IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1)); + /* Configure flow control refresh threshold value */ + IXGBE_WRITE_REG(hw, IXGBE_FCRTV, hw->fc.pause_time / 2); out: return ret_val; @@ -1325,15 +1317,15 @@ void ixgbe_enable_relaxed_ordering_82598(struct ixgbe_hw *hw) for (i = 0; ((i < hw->mac.max_tx_queues) && (i < IXGBE_DCA_MAX_QUEUES_82598)); i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(i)); - regval |= IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + regval |= IXGBE_DCA_TXCTRL_DESC_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(i), regval); } for (i = 0; ((i < hw->mac.max_rx_queues) && (i < IXGBE_DCA_MAX_QUEUES_82598)); i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i)); - regval |= (IXGBE_DCA_RXCTRL_DESC_WRO_EN | - IXGBE_DCA_RXCTRL_DESC_HSRO_EN); + regval |= IXGBE_DCA_RXCTRL_DATA_WRO_EN | + IXGBE_DCA_RXCTRL_HEAD_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval); } diff --git a/sys/dev/ixgbe/ixgbe_82598.h b/sys/dev/ixgbe/ixgbe_82598.h index 01ddef5..a195b15 100755 --- a/sys/dev/ixgbe/ixgbe_82598.h +++ b/sys/dev/ixgbe/ixgbe_82598.h @@ -36,7 +36,7 @@ #define _IXGBE_82598_H_ u32 ixgbe_get_pcie_msix_count_82598(struct ixgbe_hw *hw); -s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num); +s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw); s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw); void ixgbe_enable_relaxed_ordering_82598(struct ixgbe_hw *hw); s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq); diff --git a/sys/dev/ixgbe/ixgbe_82599.c b/sys/dev/ixgbe/ixgbe_82599.c index a5fa15e..184ee45 100644 --- a/sys/dev/ixgbe/ixgbe_82599.c +++ b/sys/dev/ixgbe/ixgbe_82599.c @@ -243,6 +243,7 @@ s32 ixgbe_init_ops_82599(struct ixgbe_hw *hw) /* RAR, Multicast, VLAN */ mac->ops.set_vmdq = &ixgbe_set_vmdq_generic; + mac->ops.set_vmdq_san_mac = &ixgbe_set_vmdq_san_mac_generic; mac->ops.clear_vmdq = &ixgbe_clear_vmdq_generic; mac->ops.insert_mac_addr = &ixgbe_insert_mac_addr_generic; mac->rar_highwater = 1; @@ -304,7 +305,9 @@ s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw, /* Check if 1G SFP module. */ if (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 || - hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1) { + hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1) { *speed = IXGBE_LINK_SPEED_1GB_FULL; *negotiation = TRUE; goto out; @@ -420,6 +423,7 @@ enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) case IXGBE_DEV_ID_82599_SFP: case IXGBE_DEV_ID_82599_SFP_FCOE: case IXGBE_DEV_ID_82599_SFP_EM: + case IXGBE_DEV_ID_82599_SFP_SF2: case IXGBE_DEV_ID_82599EN_SFP: media_type = ixgbe_media_type_fiber; break; @@ -1088,6 +1092,9 @@ mac_reset_top: hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, hw->mac.san_addr, 0, IXGBE_RAH_AV); + /* Save the SAN MAC RAR index */ + hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; + /* Reserve the last RAR for the SAN MAC address */ hw->mac.num_rar_entries--; } @@ -2037,6 +2044,8 @@ sfp_check: physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR; else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) physical_layer = IXGBE_PHYSICAL_LAYER_1000BASE_T; + else if (comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) + physical_layer = IXGBE_PHYSICAL_LAYER_1000BASE_SX; break; default: break; @@ -2233,3 +2242,4 @@ static s32 ixgbe_read_eeprom_82599(struct ixgbe_hw *hw, return ret_val; } + diff --git a/sys/dev/ixgbe/ixgbe_api.c b/sys/dev/ixgbe/ixgbe_api.c index b8b94b8..faee9d3 100644 --- a/sys/dev/ixgbe/ixgbe_api.c +++ b/sys/dev/ixgbe/ixgbe_api.c @@ -118,6 +118,7 @@ s32 ixgbe_set_mac_type(struct ixgbe_hw *hw) case IXGBE_DEV_ID_82599_BACKPLANE_FCOE: case IXGBE_DEV_ID_82599_SFP_FCOE: case IXGBE_DEV_ID_82599_SFP_EM: + case IXGBE_DEV_ID_82599_SFP_SF2: case IXGBE_DEV_ID_82599EN_SFP: case IXGBE_DEV_ID_82599_CX4: case IXGBE_DEV_ID_82599_T3_LOM: @@ -130,6 +131,7 @@ s32 ixgbe_set_mac_type(struct ixgbe_hw *hw) hw->mac.type = ixgbe_mac_X540_vf; break; case IXGBE_DEV_ID_X540T: + case IXGBE_DEV_ID_X540T1: hw->mac.type = ixgbe_mac_X540; break; default: @@ -811,6 +813,18 @@ s32 ixgbe_set_vmdq(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { return ixgbe_call_func(hw, hw->mac.ops.set_vmdq, (hw, rar, vmdq), IXGBE_NOT_IMPLEMENTED); + +} + +/** + * ixgbe_set_vmdq_san_mac - Associate VMDq index 127 with a receive address + * @hw: pointer to hardware structure + * @vmdq: VMDq default pool index + **/ +s32 ixgbe_set_vmdq_san_mac(struct ixgbe_hw *hw, u32 vmdq) +{ + return ixgbe_call_func(hw, hw->mac.ops.set_vmdq_san_mac, + (hw, vmdq), IXGBE_NOT_IMPLEMENTED); } /** @@ -960,13 +974,12 @@ s32 ixgbe_set_vlvf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on, /** * ixgbe_fc_enable - Enable flow control * @hw: pointer to hardware structure - * @packetbuf_num: packet buffer number (0-7) * * Configures the flow control settings based on SW configuration. **/ -s32 ixgbe_fc_enable(struct ixgbe_hw *hw, s32 packetbuf_num) +s32 ixgbe_fc_enable(struct ixgbe_hw *hw) { - return ixgbe_call_func(hw, hw->mac.ops.fc_enable, (hw, packetbuf_num), + return ixgbe_call_func(hw, hw->mac.ops.fc_enable, (hw), IXGBE_NOT_IMPLEMENTED); } @@ -1102,7 +1115,7 @@ u32 ixgbe_get_supported_physical_layer(struct ixgbe_hw *hw) } /** - * ixgbe_enable_rx_dma - Enables Rx DMA unit, dependant on device specifics + * ixgbe_enable_rx_dma - Enables Rx DMA unit, dependent on device specifics * @hw: pointer to hardware structure * @regval: bitfield to write to the Rx DMA register * diff --git a/sys/dev/ixgbe/ixgbe_api.h b/sys/dev/ixgbe/ixgbe_api.h index 7c612e5..3a9e0cf 100644 --- a/sys/dev/ixgbe/ixgbe_api.h +++ b/sys/dev/ixgbe/ixgbe_api.h @@ -104,6 +104,7 @@ s32 ixgbe_set_rar(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, u32 enable_addr); s32 ixgbe_clear_rar(struct ixgbe_hw *hw, u32 index); s32 ixgbe_set_vmdq(struct ixgbe_hw *hw, u32 rar, u32 vmdq); +s32 ixgbe_set_vmdq_san_mac(struct ixgbe_hw *hw, u32 vmdq); s32 ixgbe_clear_vmdq(struct ixgbe_hw *hw, u32 rar, u32 vmdq); s32 ixgbe_init_rx_addrs(struct ixgbe_hw *hw); u32 ixgbe_get_num_rx_addrs(struct ixgbe_hw *hw); @@ -120,7 +121,7 @@ s32 ixgbe_set_vfta(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on); s32 ixgbe_set_vlvf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on, bool *vfta_changed); -s32 ixgbe_fc_enable(struct ixgbe_hw *hw, s32 packetbuf_num); +s32 ixgbe_fc_enable(struct ixgbe_hw *hw); s32 ixgbe_set_fw_drv_ver(struct ixgbe_hw *hw, u8 maj, u8 min, u8 build, u8 ver); void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr); diff --git a/sys/dev/ixgbe/ixgbe_common.c b/sys/dev/ixgbe/ixgbe_common.c index 304b6d1..51806d6 100644 --- a/sys/dev/ixgbe/ixgbe_common.c +++ b/sys/dev/ixgbe/ixgbe_common.c @@ -51,13 +51,6 @@ static void ixgbe_release_eeprom(struct ixgbe_hw *hw); static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr); static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw, u16 *san_mac_offset); -static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw); -static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw); -static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw); -static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw); -static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, - u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm); -static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num); static s32 ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, u16 words, u16 *data); static s32 ixgbe_write_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset, @@ -146,6 +139,177 @@ s32 ixgbe_init_ops_generic(struct ixgbe_hw *hw) } /** + * ixgbe_device_supports_autoneg_fc - Check if phy supports autoneg flow + * control + * @hw: pointer to hardware structure + * + * There are several phys that do not support autoneg flow control. This + * function check the device id to see if the associated phy supports + * autoneg flow control. + **/ +static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) +{ + + DEBUGFUNC("ixgbe_device_supports_autoneg_fc"); + + switch (hw->device_id) { + case IXGBE_DEV_ID_X540T: + case IXGBE_DEV_ID_X540T1: + return IXGBE_SUCCESS; + case IXGBE_DEV_ID_82599_T3_LOM: + return IXGBE_SUCCESS; + default: + return IXGBE_ERR_FC_NOT_SUPPORTED; + } +} + +/** + * ixgbe_setup_fc - Set up flow control + * @hw: pointer to hardware structure + * + * Called at init time to set up flow control. + **/ +static s32 ixgbe_setup_fc(struct ixgbe_hw *hw) +{ + s32 ret_val = IXGBE_SUCCESS; + u32 reg = 0, reg_bp = 0; + u16 reg_cu = 0; + + DEBUGFUNC("ixgbe_setup_fc"); + + /* + * Validate the requested mode. Strict IEEE mode does not allow + * ixgbe_fc_rx_pause because it will cause us to fail at UNH. + */ + if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { + DEBUGOUT("ixgbe_fc_rx_pause not valid in strict IEEE mode\n"); + ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + + /* + * 10gig parts do not have a word in the EEPROM to determine the + * default flow control setting, so we explicitly set it to full. + */ + if (hw->fc.requested_mode == ixgbe_fc_default) + hw->fc.requested_mode = ixgbe_fc_full; + + /* + * Set up the 1G and 10G flow control advertisement registers so the + * HW will be able to do fc autoneg once the cable is plugged in. If + * we link at 10G, the 1G advertisement is harmless and vice versa. + */ + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + case ixgbe_media_type_backplane: + reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA); + reg_bp = IXGBE_READ_REG(hw, IXGBE_AUTOC); + break; + case ixgbe_media_type_copper: + hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_ADVT, + IXGBE_MDIO_AUTO_NEG_DEV_TYPE, ®_cu); + break; + default: + break; + } + + /* + * The possible values of fc.requested_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.requested_mode) { + case ixgbe_fc_none: + /* Flow control completely disabled by software override. */ + reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE); + if (hw->phy.media_type == ixgbe_media_type_backplane) + reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE | + IXGBE_AUTOC_ASM_PAUSE); + else if (hw->phy.media_type == ixgbe_media_type_copper) + reg_cu &= ~(IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE); + break; + case ixgbe_fc_tx_pause: + /* + * Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + reg |= IXGBE_PCS1GANA_ASM_PAUSE; + reg &= ~IXGBE_PCS1GANA_SYM_PAUSE; + if (hw->phy.media_type == ixgbe_media_type_backplane) { + reg_bp |= IXGBE_AUTOC_ASM_PAUSE; + reg_bp &= ~IXGBE_AUTOC_SYM_PAUSE; + } else if (hw->phy.media_type == ixgbe_media_type_copper) { + reg_cu |= IXGBE_TAF_ASM_PAUSE; + reg_cu &= ~IXGBE_TAF_SYM_PAUSE; + } + break; + case ixgbe_fc_rx_pause: + /* + * Rx Flow control is enabled and Tx Flow control is + * disabled by software override. 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 PAUSE, as such we fall + * through to the fc_full statement. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + case ixgbe_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + reg |= IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE; + if (hw->phy.media_type == ixgbe_media_type_backplane) + reg_bp |= IXGBE_AUTOC_SYM_PAUSE | + IXGBE_AUTOC_ASM_PAUSE; + else if (hw->phy.media_type == ixgbe_media_type_copper) + reg_cu |= IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE; + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + ret_val = IXGBE_ERR_CONFIG; + goto out; + break; + } + + if (hw->mac.type != ixgbe_mac_X540) { + /* + * Enable auto-negotiation between the MAC & PHY; + * the MAC will advertise clause 37 flow control. + */ + IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg); + reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL); + + /* Disable AN timeout */ + if (hw->fc.strict_ieee) + reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN; + + IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg); + DEBUGOUT1("Set up FC; PCS1GLCTL = 0x%08X\n", reg); + } + + /* + * AUTOC restart handles negotiation of 1G and 10G on backplane + * and copper. There is no need to set the PCS1GCTL register. + * + */ + if (hw->phy.media_type == ixgbe_media_type_backplane) { + reg_bp |= IXGBE_AUTOC_AN_RESTART; + IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_bp); + } else if ((hw->phy.media_type == ixgbe_media_type_copper) && + (ixgbe_device_supports_autoneg_fc(hw) == IXGBE_SUCCESS)) { + hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_ADVT, + IXGBE_MDIO_AUTO_NEG_DEV_TYPE, reg_cu); + } + + DEBUGOUT1("Set up FC; IXGBE_AUTOC = 0x%08X\n", reg); +out: + return ret_val; +} + +/** * ixgbe_start_hw_generic - Prepare hardware for Tx/Rx * @hw: pointer to hardware structure * @@ -156,6 +320,7 @@ s32 ixgbe_init_ops_generic(struct ixgbe_hw *hw) **/ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) { + s32 ret_val; u32 ctrl_ext; DEBUGFUNC("ixgbe_start_hw_generic"); @@ -178,12 +343,15 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) IXGBE_WRITE_FLUSH(hw); /* Setup flow control */ - ixgbe_setup_fc(hw, 0); + ret_val = ixgbe_setup_fc(hw); + if (ret_val != IXGBE_SUCCESS) + goto out; /* Clear adapter stopped flag */ hw->adapter_stopped = FALSE; - return IXGBE_SUCCESS; +out: + return ret_val; } /** @@ -211,14 +379,14 @@ s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw) /* Disable relaxed ordering */ for (i = 0; i < hw->mac.max_tx_queues; i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i)); - regval &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + regval &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(i), regval); } for (i = 0; i < hw->mac.max_rx_queues; i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i)); - regval &= ~(IXGBE_DCA_RXCTRL_DESC_WRO_EN | - IXGBE_DCA_RXCTRL_DESC_HSRO_EN); + regval &= ~(IXGBE_DCA_RXCTRL_DATA_WRO_EN | + IXGBE_DCA_RXCTRL_HEAD_WRO_EN); IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval); } @@ -583,6 +751,9 @@ s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw) case IXGBE_PCI_LINK_SPEED_5000: hw->bus.speed = ixgbe_bus_speed_5000; break; + case IXGBE_PCI_LINK_SPEED_8000: + hw->bus.speed = ixgbe_bus_speed_8000; + break; default: hw->bus.speed = ixgbe_bus_speed_unknown; break; @@ -2242,27 +2413,44 @@ s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw) /** * ixgbe_fc_enable_generic - Enable flow control * @hw: pointer to hardware structure - * @packetbuf_num: packet buffer number (0-7) * * Enable flow control according to the current settings. **/ -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num) +s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) { s32 ret_val = IXGBE_SUCCESS; u32 mflcn_reg, fccfg_reg; u32 reg; u32 fcrtl, fcrth; + int i; DEBUGFUNC("ixgbe_fc_enable_generic"); - /* Negotiate the fc mode to use */ - ret_val = ixgbe_fc_autoneg(hw); - if (ret_val == IXGBE_ERR_FLOW_CONTROL) + /* Validate the water mark configuration */ + if (!hw->fc.pause_time) { + ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; goto out; + } + + /* Low water mark of zero causes XOFF floods */ + for (i = 0; i < IXGBE_DCB_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & ixgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + if (!hw->fc.low_water[i] || + hw->fc.low_water[i] >= hw->fc.high_water[i]) { + DEBUGOUT("Invalid water mark configuration\n"); + ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + goto out; + } + } + } + + /* Negotiate the fc mode to use */ + ixgbe_fc_autoneg(hw); /* Disable any previous flow control settings */ mflcn_reg = IXGBE_READ_REG(hw, IXGBE_MFLCN); - mflcn_reg &= ~(IXGBE_MFLCN_RFCE | IXGBE_MFLCN_RPFCE); + mflcn_reg &= ~(IXGBE_MFLCN_RPFCE_MASK | IXGBE_MFLCN_RFCE); fccfg_reg = IXGBE_READ_REG(hw, IXGBE_FCCFG); fccfg_reg &= ~(IXGBE_FCCFG_TFCE_802_3X | IXGBE_FCCFG_TFCE_PRIORITY); @@ -2319,120 +2507,110 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num) IXGBE_WRITE_REG(hw, IXGBE_MFLCN, mflcn_reg); IXGBE_WRITE_REG(hw, IXGBE_FCCFG, fccfg_reg); - fcrth = hw->fc.high_water[packetbuf_num] << 10; - fcrtl = hw->fc.low_water << 10; - if (hw->fc.current_mode & ixgbe_fc_tx_pause) { - fcrth |= IXGBE_FCRTH_FCEN; - if (hw->fc.send_xon) - fcrtl |= IXGBE_FCRTL_XONE; - } + /* Set up and enable Rx high/low water mark thresholds, enable XON. */ + for (i = 0; i < IXGBE_DCB_MAX_TRAFFIC_CLASS; i++) { + if ((hw->fc.current_mode & ixgbe_fc_tx_pause) && + hw->fc.high_water[i]) { + fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE; + IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl); + fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN; + } else { + IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), 0); + /* + * In order to prevent Tx hangs when the internal Tx + * switch is enabled we must set the high water mark + * to the maximum FCRTH value. This allows the Tx + * switch to function even under heavy Rx workloads. + */ + fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32; + } - IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(packetbuf_num), fcrth); - IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(packetbuf_num), fcrtl); + IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), fcrth); + } /* Configure pause time (2 TCs per register) */ - reg = IXGBE_READ_REG(hw, IXGBE_FCTTV(packetbuf_num / 2)); - if ((packetbuf_num & 1) == 0) - reg = (reg & 0xFFFF0000) | hw->fc.pause_time; - else - reg = (reg & 0x0000FFFF) | (hw->fc.pause_time << 16); - IXGBE_WRITE_REG(hw, IXGBE_FCTTV(packetbuf_num / 2), reg); + reg = hw->fc.pause_time * 0x00010001; + for (i = 0; i < (IXGBE_DCB_MAX_TRAFFIC_CLASS / 2); i++) + IXGBE_WRITE_REG(hw, IXGBE_FCTTV(i), reg); - IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1)); + /* Configure flow control refresh threshold value */ + IXGBE_WRITE_REG(hw, IXGBE_FCRTV, hw->fc.pause_time / 2); out: return ret_val; } /** - * ixgbe_fc_autoneg - Configure flow control + * ixgbe_negotiate_fc - Negotiate flow control * @hw: pointer to hardware structure + * @adv_reg: flow control advertised settings + * @lp_reg: link partner's flow control settings + * @adv_sym: symmetric pause bit in advertisement + * @adv_asm: asymmetric pause bit in advertisement + * @lp_sym: symmetric pause bit in link partner advertisement + * @lp_asm: asymmetric pause bit in link partner advertisement * - * Compares our advertised flow control capabilities to those advertised by - * our link partner, and determines the proper flow control mode to use. + * Find the intersection between advertised settings and link partner's + * advertised settings **/ -s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw) +static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, + u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm) { - s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; - ixgbe_link_speed speed; - bool link_up; + if ((!(adv_reg)) || (!(lp_reg))) + return IXGBE_ERR_FC_NOT_NEGOTIATED; - DEBUGFUNC("ixgbe_fc_autoneg"); + if ((adv_reg & adv_sym) && (lp_reg & lp_sym)) { + /* + * 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 + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == ixgbe_fc_full) { + hw->fc.current_mode = ixgbe_fc_full; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_rx_pause; + DEBUGOUT("Flow Control=RX PAUSE frames only\n"); + } + } else if (!(adv_reg & adv_sym) && (adv_reg & adv_asm) && + (lp_reg & lp_sym) && (lp_reg & lp_asm)) { + hw->fc.current_mode = ixgbe_fc_tx_pause; + DEBUGOUT("Flow Control = TX PAUSE frames only.\n"); + } else if ((adv_reg & adv_sym) && (adv_reg & adv_asm) && + !(lp_reg & lp_sym) && (lp_reg & lp_asm)) { + hw->fc.current_mode = ixgbe_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_none; + DEBUGOUT("Flow Control = NONE.\n"); + } + return IXGBE_SUCCESS; +} - if (hw->fc.disable_fc_autoneg) - goto out; +/** + * ixgbe_fc_autoneg_fiber - Enable flow control on 1 gig fiber + * @hw: pointer to hardware structure + * + * Enable flow control according on 1 gig fiber. + **/ +static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw) +{ + u32 pcs_anadv_reg, pcs_lpab_reg, linkstat; + s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; /* - * AN should have completed when the cable was plugged in. - * Look for reasons to bail out. Bail out if: - * - FC autoneg is disabled, or if - * - link is not up. - * - * Since we're being called from an LSC, link is already known to be up. - * So use link_up_wait_to_complete=FALSE. - */ - hw->mac.ops.check_link(hw, &speed, &link_up, FALSE); - if (!link_up) { - ret_val = IXGBE_ERR_FLOW_CONTROL; - goto out; - } - - switch (hw->phy.media_type) { - /* Autoneg flow control on fiber adapters */ - case ixgbe_media_type_fiber: - if (speed == IXGBE_LINK_SPEED_1GB_FULL) - ret_val = ixgbe_fc_autoneg_fiber(hw); - break; - - /* Autoneg flow control on backplane adapters */ - case ixgbe_media_type_backplane: - ret_val = ixgbe_fc_autoneg_backplane(hw); - break; - - /* Autoneg flow control on copper adapters */ - case ixgbe_media_type_copper: - if (ixgbe_device_supports_autoneg_fc(hw) == IXGBE_SUCCESS) - ret_val = ixgbe_fc_autoneg_copper(hw); - break; - - default: - break; - } - -out: - if (ret_val == IXGBE_SUCCESS) { - hw->fc.fc_was_autonegged = TRUE; - } else { - hw->fc.fc_was_autonegged = FALSE; - hw->fc.current_mode = hw->fc.requested_mode; - } - return ret_val; -} - -/** - * ixgbe_fc_autoneg_fiber - Enable flow control on 1 gig fiber - * @hw: pointer to hardware structure - * - * Enable flow control according on 1 gig fiber. - **/ -static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw) -{ - u32 pcs_anadv_reg, pcs_lpab_reg, linkstat; - s32 ret_val; - - /* - * On multispeed fiber at 1g, bail out if - * - link is up but AN did not complete, or if - * - link is up and AN completed but timed out + * On multispeed fiber at 1g, bail out if + * - link is up but AN did not complete, or if + * - link is up and AN completed but timed out */ linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA); if ((!!(linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) || - (!!(linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) { - ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; + (!!(linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) goto out; - } pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA); pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP); @@ -2456,7 +2634,7 @@ out: static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw) { u32 links2, anlp1_reg, autoc_reg, links; - s32 ret_val; + s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; /* * On backplane, bail out if @@ -2464,21 +2642,13 @@ static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw) * - we are 82599 and link partner is not AN enabled */ links = IXGBE_READ_REG(hw, IXGBE_LINKS); - if ((links & IXGBE_LINKS_KX_AN_COMP) == 0) { - hw->fc.fc_was_autonegged = FALSE; - hw->fc.current_mode = hw->fc.requested_mode; - ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; + if ((links & IXGBE_LINKS_KX_AN_COMP) == 0) goto out; - } if (hw->mac.type == ixgbe_mac_82599EB) { links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2); - if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0) { - hw->fc.fc_was_autonegged = FALSE; - hw->fc.current_mode = hw->fc.requested_mode; - ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; + if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0) goto out; - } } /* * Read the 10g AN autoc and LP ability registers and resolve @@ -2520,227 +2690,62 @@ static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw) } /** - * ixgbe_negotiate_fc - Negotiate flow control - * @hw: pointer to hardware structure - * @adv_reg: flow control advertised settings - * @lp_reg: link partner's flow control settings - * @adv_sym: symmetric pause bit in advertisement - * @adv_asm: asymmetric pause bit in advertisement - * @lp_sym: symmetric pause bit in link partner advertisement - * @lp_asm: asymmetric pause bit in link partner advertisement - * - * Find the intersection between advertised settings and link partner's - * advertised settings - **/ -static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, - u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm) -{ - if ((!(adv_reg)) || (!(lp_reg))) - return IXGBE_ERR_FC_NOT_NEGOTIATED; - - if ((adv_reg & adv_sym) && (lp_reg & lp_sym)) { - /* - * 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 - * ONLY. Hence, we must now check to see if we need to - * turn OFF the TRANSMISSION of PAUSE frames. - */ - if (hw->fc.requested_mode == ixgbe_fc_full) { - hw->fc.current_mode = ixgbe_fc_full; - DEBUGOUT("Flow Control = FULL.\n"); - } else { - hw->fc.current_mode = ixgbe_fc_rx_pause; - DEBUGOUT("Flow Control=RX PAUSE frames only\n"); - } - } else if (!(adv_reg & adv_sym) && (adv_reg & adv_asm) && - (lp_reg & lp_sym) && (lp_reg & lp_asm)) { - hw->fc.current_mode = ixgbe_fc_tx_pause; - DEBUGOUT("Flow Control = TX PAUSE frames only.\n"); - } else if ((adv_reg & adv_sym) && (adv_reg & adv_asm) && - !(lp_reg & lp_sym) && (lp_reg & lp_asm)) { - hw->fc.current_mode = ixgbe_fc_rx_pause; - DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); - } else { - hw->fc.current_mode = ixgbe_fc_none; - DEBUGOUT("Flow Control = NONE.\n"); - } - return IXGBE_SUCCESS; -} - -/** - * ixgbe_setup_fc - Set up flow control + * ixgbe_fc_autoneg - Configure flow control * @hw: pointer to hardware structure * - * Called at init time to set up flow control. + * Compares our advertised flow control capabilities to those advertised by + * our link partner, and determines the proper flow control mode to use. **/ -static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num) +void ixgbe_fc_autoneg(struct ixgbe_hw *hw) { - s32 ret_val = IXGBE_SUCCESS; - u32 reg = 0, reg_bp = 0; - u16 reg_cu = 0; - - DEBUGFUNC("ixgbe_setup_fc"); + s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED; + ixgbe_link_speed speed; + bool link_up; - /* Validate the packetbuf configuration */ - if (packetbuf_num < 0 || packetbuf_num > 7) { - DEBUGOUT1("Invalid packet buffer number [%d], expected range " - "is 0-7\n", packetbuf_num); - ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; - goto out; - } + DEBUGFUNC("ixgbe_fc_autoneg"); /* - * Validate the water mark configuration. Zero water marks are invalid - * because it causes the controller to just blast out fc packets. + * AN should have completed when the cable was plugged in. + * Look for reasons to bail out. Bail out if: + * - FC autoneg is disabled, or if + * - link is not up. */ - if (!hw->fc.low_water || - !hw->fc.high_water[packetbuf_num] || - !hw->fc.pause_time) { - DEBUGOUT("Invalid water mark configuration\n"); - ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + if (hw->fc.disable_fc_autoneg) goto out; - } - /* - * Validate the requested mode. Strict IEEE mode does not allow - * ixgbe_fc_rx_pause because it will cause us to fail at UNH. - */ - if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { - DEBUGOUT("ixgbe_fc_rx_pause not valid in strict IEEE mode\n"); - ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS; + hw->mac.ops.check_link(hw, &speed, &link_up, FALSE); + if (!link_up) goto out; - } - - /* - * 10gig parts do not have a word in the EEPROM to determine the - * default flow control setting, so we explicitly set it to full. - */ - if (hw->fc.requested_mode == ixgbe_fc_default) - hw->fc.requested_mode = ixgbe_fc_full; - - /* - * Set up the 1G and 10G flow control advertisement registers so the - * HW will be able to do fc autoneg once the cable is plugged in. If - * we link at 10G, the 1G advertisement is harmless and vice versa. - */ switch (hw->phy.media_type) { + /* Autoneg flow control on fiber adapters */ case ixgbe_media_type_fiber: + if (speed == IXGBE_LINK_SPEED_1GB_FULL) + ret_val = ixgbe_fc_autoneg_fiber(hw); + break; + + /* Autoneg flow control on backplane adapters */ case ixgbe_media_type_backplane: - reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA); - reg_bp = IXGBE_READ_REG(hw, IXGBE_AUTOC); + ret_val = ixgbe_fc_autoneg_backplane(hw); break; + /* Autoneg flow control on copper adapters */ case ixgbe_media_type_copper: - hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_ADVT, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, ®_cu); + if (ixgbe_device_supports_autoneg_fc(hw) == IXGBE_SUCCESS) + ret_val = ixgbe_fc_autoneg_copper(hw); break; default: - ; - } - - /* - * The possible values of fc.requested_mode are: - * 0: Flow control is completely disabled - * 1: Rx flow control is enabled (we can receive pause frames, - * but not send pause frames). - * 2: Tx flow control is enabled (we can send pause frames but - * we do not support receiving pause frames). - * 3: Both Rx and Tx flow control (symmetric) are enabled. - * other: Invalid. - */ - switch (hw->fc.requested_mode) { - case ixgbe_fc_none: - /* Flow control completely disabled by software override. */ - reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE); - if (hw->phy.media_type == ixgbe_media_type_backplane) - reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE | - IXGBE_AUTOC_ASM_PAUSE); - else if (hw->phy.media_type == ixgbe_media_type_copper) - reg_cu &= ~(IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE); - break; - case ixgbe_fc_rx_pause: - /* - * Rx Flow control is enabled and Tx Flow control is - * disabled by software override. 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 PAUSE. Later, we will - * disable the adapter's ability to send PAUSE frames. - */ - reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE); - if (hw->phy.media_type == ixgbe_media_type_backplane) - reg_bp |= (IXGBE_AUTOC_SYM_PAUSE | - IXGBE_AUTOC_ASM_PAUSE); - else if (hw->phy.media_type == ixgbe_media_type_copper) - reg_cu |= (IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE); - break; - case ixgbe_fc_tx_pause: - /* - * Tx Flow control is enabled, and Rx Flow control is - * disabled by software override. - */ - reg |= (IXGBE_PCS1GANA_ASM_PAUSE); - reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE); - if (hw->phy.media_type == ixgbe_media_type_backplane) { - reg_bp |= (IXGBE_AUTOC_ASM_PAUSE); - reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE); - } else if (hw->phy.media_type == ixgbe_media_type_copper) { - reg_cu |= (IXGBE_TAF_ASM_PAUSE); - reg_cu &= ~(IXGBE_TAF_SYM_PAUSE); - } - break; - case ixgbe_fc_full: - /* Flow control (both Rx and Tx) is enabled by SW override. */ - reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE); - if (hw->phy.media_type == ixgbe_media_type_backplane) - reg_bp |= (IXGBE_AUTOC_SYM_PAUSE | - IXGBE_AUTOC_ASM_PAUSE); - else if (hw->phy.media_type == ixgbe_media_type_copper) - reg_cu |= (IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE); - break; - default: - DEBUGOUT("Flow control param set incorrectly\n"); - ret_val = IXGBE_ERR_CONFIG; - goto out; break; } - if (hw->mac.type != ixgbe_mac_X540) { - /* - * Enable auto-negotiation between the MAC & PHY; - * the MAC will advertise clause 37 flow control. - */ - IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg); - reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL); - - /* Disable AN timeout */ - if (hw->fc.strict_ieee) - reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN; - - IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg); - DEBUGOUT1("Set up FC; PCS1GLCTL = 0x%08X\n", reg); - } - - /* - * AUTOC restart handles negotiation of 1G and 10G on backplane - * and copper. There is no need to set the PCS1GCTL register. - * - */ - if (hw->phy.media_type == ixgbe_media_type_backplane) { - reg_bp |= IXGBE_AUTOC_AN_RESTART; - IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_bp); - } else if ((hw->phy.media_type == ixgbe_media_type_copper) && - (ixgbe_device_supports_autoneg_fc(hw) == IXGBE_SUCCESS)) { - hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_ADVT, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, reg_cu); - } - - DEBUGOUT1("Set up FC; IXGBE_AUTOC = 0x%08X\n", reg); out: - return ret_val; + if (ret_val == IXGBE_SUCCESS) { + hw->fc.fc_was_autonegged = TRUE; + } else { + hw->fc.fc_was_autonegged = FALSE; + hw->fc.current_mode = hw->fc.requested_mode; + } } /** @@ -3131,20 +3136,35 @@ san_mac_addr_out: * Read PCIe configuration space, and get the MSI-X vector count from * the capabilities table. **/ -u32 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw) +u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw) { - u32 msix_count = 64; + u16 msix_count = 1; + u16 max_msix_count; + u16 pcie_offset; + + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + pcie_offset = IXGBE_PCIE_MSIX_82598_CAPS; + max_msix_count = IXGBE_MAX_MSIX_VECTORS_82598; + break; + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + pcie_offset = IXGBE_PCIE_MSIX_82599_CAPS; + max_msix_count = IXGBE_MAX_MSIX_VECTORS_82599; + break; + default: + return msix_count; + } DEBUGFUNC("ixgbe_get_pcie_msix_count_generic"); - if (hw->mac.msix_vectors_from_pcie) { - msix_count = IXGBE_READ_PCIE_WORD(hw, - IXGBE_PCIE_MSIX_82599_CAPS); - msix_count &= IXGBE_PCIE_MSIX_TBL_SZ_MASK; - - /* MSI-X count is zero-based in HW, so increment to give - * proper value */ - msix_count++; - } + msix_count = IXGBE_READ_PCIE_WORD(hw, pcie_offset); + msix_count &= IXGBE_PCIE_MSIX_TBL_SZ_MASK; + + /* MSI-X count is zero-based in HW */ + msix_count++; + + if (msix_count > max_msix_count) + msix_count = max_msix_count; return msix_count; } @@ -3299,6 +3319,33 @@ s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) } /** + * This function should only be involved in the IOV mode. + * In IOV mode, Default pool is next pool after the number of + * VFs advertized and not 0. + * MPSAR table needs to be updated for SAN_MAC RAR [hw->mac.san_mac_rar_index] + * + * ixgbe_set_vmdq_san_mac - Associate default VMDq pool index with a rx address + * @hw: pointer to hardware struct + * @vmdq: VMDq pool index + **/ +s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq) +{ + u32 rar = hw->mac.san_mac_rar_index; + + DEBUGFUNC("ixgbe_set_vmdq_san_mac"); + + if (vmdq < 32) { + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), 1 << vmdq); + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), 0); + } else { + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), 0); + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), 1 << (vmdq - 32)); + } + + return IXGBE_SUCCESS; +} + +/** * ixgbe_init_uta_tables_generic - Initialize the Unicast Table Array * @hw: pointer to hardware structure **/ @@ -3718,30 +3765,6 @@ out: } /** - * ixgbe_device_supports_autoneg_fc - Check if phy supports autoneg flow - * control - * @hw: pointer to hardware structure - * - * There are several phys that do not support autoneg flow control. This - * function check the device id to see if the associated phy supports - * autoneg flow control. - **/ -static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) -{ - - DEBUGFUNC("ixgbe_device_supports_autoneg_fc"); - - switch (hw->device_id) { - case IXGBE_DEV_ID_X540T: - return IXGBE_SUCCESS; - case IXGBE_DEV_ID_82599_T3_LOM: - return IXGBE_SUCCESS; - default: - return IXGBE_ERR_FC_NOT_SUPPORTED; - } -} - -/** * ixgbe_set_mac_anti_spoofing - Enable/Disable MAC anti-spoofing * @hw: pointer to hardware structure * @enable: enable or disable switch for anti-spoofing @@ -3765,20 +3788,22 @@ void ixgbe_set_mac_anti_spoofing(struct ixgbe_hw *hw, bool enable, int pf) * PFVFSPOOF register array is size 8 with 8 bits assigned to * MAC anti-spoof enables in each register array element. */ - for (j = 0; j < IXGBE_PFVFSPOOF_REG_COUNT; j++) + for (j = 0; j < pf_target_reg; j++) IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(j), pfvfspoof); - /* If not enabling anti-spoofing then done */ - if (!enable) - return; - /* * The PF should be allowed to spoof so that it can support - * emulation mode NICs. Reset the bit assigned to the PF + * emulation mode NICs. Do not set the bits assigned to the PF + */ + pfvfspoof &= (1 << pf_target_shift) - 1; + IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(j), pfvfspoof); + + /* + * Remaining pools belong to the PF so they do not need to have + * anti-spoofing enabled. */ - pfvfspoof = IXGBE_READ_REG(hw, IXGBE_PFVFSPOOF(pf_target_reg)); - pfvfspoof ^= (1 << pf_target_shift); - IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(pf_target_reg), pfvfspoof); + for (j++; j < IXGBE_PFVFSPOOF_REG_COUNT; j++) + IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(j), 0); } /** @@ -3837,14 +3862,14 @@ void ixgbe_enable_relaxed_ordering_gen2(struct ixgbe_hw *hw) /* Enable relaxed ordering */ for (i = 0; i < hw->mac.max_tx_queues; i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i)); - regval |= IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + regval |= IXGBE_DCA_TXCTRL_DESC_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(i), regval); } for (i = 0; i < hw->mac.max_rx_queues; i++) { regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i)); - regval |= (IXGBE_DCA_RXCTRL_DESC_WRO_EN | - IXGBE_DCA_RXCTRL_DESC_HSRO_EN); + regval |= IXGBE_DCA_RXCTRL_DATA_WRO_EN | + IXGBE_DCA_RXCTRL_HEAD_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval); } @@ -3878,7 +3903,7 @@ static u8 ixgbe_calculate_checksum(u8 *buffer, u32 length) * @hw: pointer to the HW structure * @buffer: contains the command to write and where the return status will * be placed - * @lenght: lenght of buffer, must be multiple of 4 bytes + * @length: length of buffer, must be multiple of 4 bytes * * Communicates with the manageability block. On success return IXGBE_SUCCESS * else return IXGBE_ERR_HOST_INTERFACE_COMMAND. @@ -4057,17 +4082,17 @@ void ixgbe_set_rxpba_generic(struct ixgbe_hw *hw, int num_pb, u32 headroom, * buffers requested using supplied strategy. */ switch (strategy) { - case (PBA_STRATEGY_WEIGHTED): + case PBA_STRATEGY_WEIGHTED: /* ixgbe_dcb_pba_80_48 strategy weight first half of packet * buffer with 5/8 of the packet buffer space. */ - rxpktsize = (pbsize * 5 * 2) / (num_pb * 8); + rxpktsize = (pbsize * 5) / (num_pb * 4); pbsize -= rxpktsize * (num_pb / 2); rxpktsize <<= IXGBE_RXPBSIZE_SHIFT; for (; i < (num_pb / 2); i++) IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize); /* Fall through to configure remaining packet buffers */ - case (PBA_STRATEGY_EQUAL): + case PBA_STRATEGY_EQUAL: rxpktsize = (pbsize / (num_pb - i)) << IXGBE_RXPBSIZE_SHIFT; for (; i < num_pb; i++) IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), rxpktsize); diff --git a/sys/dev/ixgbe/ixgbe_common.h b/sys/dev/ixgbe/ixgbe_common.h index 4bd4b75..3bbc537 100644 --- a/sys/dev/ixgbe/ixgbe_common.h +++ b/sys/dev/ixgbe/ixgbe_common.h @@ -42,7 +42,7 @@ IXGBE_WRITE_REG(hw, reg + 4, (u32) (value >> 32)); \ } while (0) -u32 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw); +u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw); s32 ixgbe_init_ops_generic(struct ixgbe_hw *hw); s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw); @@ -95,8 +95,8 @@ s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); s32 ixgbe_disable_sec_rx_path_generic(struct ixgbe_hw *hw); s32 ixgbe_enable_sec_rx_path_generic(struct ixgbe_hw *hw); -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packtetbuf_num); -s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw); +s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw); +void ixgbe_fc_autoneg(struct ixgbe_hw *hw); s32 ixgbe_validate_mac_addr(u8 *mac_addr); s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask); @@ -110,6 +110,7 @@ s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr); s32 ixgbe_set_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr); s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); +s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq); s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); s32 ixgbe_insert_mac_addr_generic(struct ixgbe_hw *hw, u8 *addr, u32 vmdq); s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw); diff --git a/sys/dev/ixgbe/ixgbe_osdep.h b/sys/dev/ixgbe/ixgbe_osdep.h index 4ba3f10..943c99f 100644 --- a/sys/dev/ixgbe/ixgbe_osdep.h +++ b/sys/dev/ixgbe/ixgbe_osdep.h @@ -55,6 +55,7 @@ #include #define ASSERT(x) if(!(x)) panic("IXGBE: x") +#define EWARN(H, W, S) printf(W) /* The happy-fun DELAY macro is defined in /usr/src/sys/i386/include/clock.h */ #define usec_delay(x) DELAY(x) @@ -110,6 +111,14 @@ typedef uint64_t u64; typedef boolean_t bool; #endif +/* shared code requires this */ +#define __le16 u16 +#define __le32 u32 +#define __le64 u64 +#define __be16 u16 +#define __be32 u32 +#define __be64 u64 + #define le16_to_cpu #if __FreeBSD_version < 800000 @@ -128,7 +137,7 @@ typedef boolean_t bool; static __inline void prefetch(void *x) { - __asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x)); + __asm volatile("prefetchnta %0" :: "m" (*(unsigned long *)x)); } #else #define prefetch(x) diff --git a/sys/dev/ixgbe/ixgbe_phy.c b/sys/dev/ixgbe/ixgbe_phy.c index a8693ce..3f262f9 100644 --- a/sys/dev/ixgbe/ixgbe_phy.c +++ b/sys/dev/ixgbe/ixgbe_phy.c @@ -1020,6 +1020,8 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) * 8 SFP_act_lmt_DA_CORE1 - 82599-specific * 9 SFP_1g_cu_CORE0 - 82599-specific * 10 SFP_1g_cu_CORE1 - 82599-specific + * 11 SFP_1g_sx_CORE0 - 82599-specific + * 12 SFP_1g_sx_CORE1 - 82599-specific */ if (hw->mac.type == ixgbe_mac_82598EB) { if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) @@ -1070,6 +1072,13 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) else hw->phy.sfp_type = ixgbe_sfp_type_1g_cu_core1; + } else if (comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) { + if (hw->bus.lan_id == 0) + hw->phy.sfp_type = + ixgbe_sfp_type_1g_sx_core0; + else + hw->phy.sfp_type = + ixgbe_sfp_type_1g_sx_core1; } else { hw->phy.sfp_type = ixgbe_sfp_type_unknown; } @@ -1162,7 +1171,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) /* Verify supported 1G SFP modules */ if (comp_codes_10g == 0 && !(hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 || - hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0)) { + hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1)) { hw->phy.type = ixgbe_phy_sfp_unsupported; status = IXGBE_ERR_SFP_NOT_SUPPORTED; goto out; @@ -1177,14 +1188,31 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) ixgbe_get_device_caps(hw, &enforce_sfp); if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP) && !((hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0) || - (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1))) { + (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1) || + (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0) || + (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1))) { /* Make sure we're a supported PHY type */ if (hw->phy.type == ixgbe_phy_sfp_intel) { status = IXGBE_SUCCESS; } else { - DEBUGOUT("SFP+ module not supported\n"); - hw->phy.type = ixgbe_phy_sfp_unsupported; - status = IXGBE_ERR_SFP_NOT_SUPPORTED; + if (hw->allow_unsupported_sfp == TRUE) { + EWARN(hw, "WARNING: Intel (R) Network " + "Connections are quality tested " + "using Intel (R) Ethernet Optics." + " Using untested modules is not " + "supported and may cause unstable" + " operation or damage to the " + "module or the adapter. Intel " + "Corporation is not responsible " + "for any harm caused by using " + "untested modules.\n", status); + status = IXGBE_SUCCESS; + } else { + DEBUGOUT("SFP+ module not supported\n"); + hw->phy.type = + ixgbe_phy_sfp_unsupported; + status = IXGBE_ERR_SFP_NOT_SUPPORTED; + } } } else { status = IXGBE_SUCCESS; @@ -1238,10 +1266,12 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, * SR modules */ if (sfp_type == ixgbe_sfp_type_da_act_lmt_core0 || - sfp_type == ixgbe_sfp_type_1g_cu_core0) + sfp_type == ixgbe_sfp_type_1g_cu_core0 || + sfp_type == ixgbe_sfp_type_1g_sx_core0) sfp_type = ixgbe_sfp_type_srlr_core0; else if (sfp_type == ixgbe_sfp_type_da_act_lmt_core1 || - sfp_type == ixgbe_sfp_type_1g_cu_core1) + sfp_type == ixgbe_sfp_type_1g_cu_core1 || + sfp_type == ixgbe_sfp_type_1g_sx_core1) sfp_type = ixgbe_sfp_type_srlr_core1; /* Read offset to PHY init contents */ @@ -1715,15 +1745,24 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data) **/ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) { + u32 i = 0; + u32 timeout = IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT; + u32 i2cctl_r = 0; + DEBUGFUNC("ixgbe_raise_i2c_clk"); - *i2cctl |= IXGBE_I2C_CLK_OUT; + for (i = 0; i < timeout; i++) { + *i2cctl |= IXGBE_I2C_CLK_OUT; - IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl); - IXGBE_WRITE_FLUSH(hw); + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl); + IXGBE_WRITE_FLUSH(hw); + /* SCL rise time (1000ns) */ + usec_delay(IXGBE_I2C_T_RISE); - /* SCL rise time (1000ns) */ - usec_delay(IXGBE_I2C_T_RISE); + i2cctl_r = IXGBE_READ_REG(hw, IXGBE_I2CCTL); + if (i2cctl_r & IXGBE_I2C_CLK_IN) + break; + } } /** @@ -1840,7 +1879,7 @@ void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw) } /** - * ixgbe_tn_check_overtemp - Checks if an overtemp occured. + * ixgbe_tn_check_overtemp - Checks if an overtemp occurred. * @hw: pointer to hardware structure * * Checks if the LASI temp alarm status was triggered due to overtemp diff --git a/sys/dev/ixgbe/ixgbe_type.h b/sys/dev/ixgbe/ixgbe_type.h index f25b229..52944fd 100644 --- a/sys/dev/ixgbe/ixgbe_type.h +++ b/sys/dev/ixgbe/ixgbe_type.h @@ -66,12 +66,14 @@ #define IXGBE_DEV_ID_82599_BACKPLANE_FCOE 0x152A #define IXGBE_DEV_ID_82599_SFP_FCOE 0x1529 #define IXGBE_DEV_ID_82599_SFP_EM 0x1507 +#define IXGBE_DEV_ID_82599_SFP_SF2 0x154D #define IXGBE_DEV_ID_82599EN_SFP 0x1557 #define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC #define IXGBE_DEV_ID_82599_T3_LOM 0x151C #define IXGBE_DEV_ID_82599_VF 0x10ED #define IXGBE_DEV_ID_X540_VF 0x1515 #define IXGBE_DEV_ID_X540T 0x1528 +#define IXGBE_DEV_ID_X540T1 0x1560 /* General Registers */ #define IXGBE_CTRL 0x00000 @@ -118,6 +120,8 @@ #define IXGBE_I2C_CLK_OUT 0x00000002 #define IXGBE_I2C_DATA_IN 0x00000004 #define IXGBE_I2C_DATA_OUT 0x00000008 +#define IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT 500 + /* Interrupt Registers */ #define IXGBE_EICR 0x00800 @@ -832,6 +836,7 @@ #define IXGBE_GCR_EXT_VT_MODE_64 0x00000003 #define IXGBE_GCR_EXT_SRIOV (IXGBE_GCR_EXT_MSIX_EN | \ IXGBE_GCR_EXT_VT_MODE_64) +#define IXGBE_GCR_EXT_VT_MODE_MASK 0x00000003 /* Time Sync Registers */ #define IXGBE_TSYNCRXCTL 0x05188 /* Rx Time Sync Control register - RW */ #define IXGBE_TSYNCTXCTL 0x08C00 /* Tx Time Sync Control register - RW */ @@ -852,6 +857,8 @@ #define IXGBE_TRGTTIMH0 0x08C28 /* Target Time Register 0 High - RW */ #define IXGBE_TRGTTIML1 0x08C2C /* Target Time Register 1 Low - RW */ #define IXGBE_TRGTTIMH1 0x08C30 /* Target Time Register 1 High - RW */ +#define IXGBE_CLKTIML 0x08C34 /* Clock Out Time Register Low - RW */ +#define IXGBE_CLKTIMH 0x08C38 /* Clock Out Time Register High - RW */ #define IXGBE_FREQOUT0 0x08C34 /* Frequency Out 0 Control register - RW */ #define IXGBE_FREQOUT1 0x08C38 /* Frequency Out 1 Control register - RW */ #define IXGBE_AUXSTMPL0 0x08C3C /* Auxiliary Time Stamp 0 register Low - RO */ @@ -1071,14 +1078,16 @@ #define IXGBE_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* Rx Desc header ena */ #define IXGBE_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* Rx Desc payload ena */ #define IXGBE_DCA_RXCTRL_DESC_RRO_EN (1 << 9) /* Rx rd Desc Relax Order */ -#define IXGBE_DCA_RXCTRL_DESC_WRO_EN (1 << 13) /* Rx wr Desc Relax Order */ -#define IXGBE_DCA_RXCTRL_DESC_HSRO_EN (1 << 15) /* Rx Split Header RO */ +#define IXGBE_DCA_RXCTRL_DATA_WRO_EN (1 << 13) /* Rx wr data Relax Order */ +#define IXGBE_DCA_RXCTRL_HEAD_WRO_EN (1 << 15) /* Rx wr header RO */ #define IXGBE_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ #define IXGBE_DCA_TXCTRL_CPUID_MASK_82599 0xFF000000 /* Tx CPUID Mask */ #define IXGBE_DCA_TXCTRL_CPUID_SHIFT_82599 24 /* Tx CPUID Shift */ #define IXGBE_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ -#define IXGBE_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */ +#define IXGBE_DCA_TXCTRL_DESC_RRO_EN (1 << 9) /* Tx rd Desc Relax Order */ +#define IXGBE_DCA_TXCTRL_DESC_WRO_EN (1 << 11) /* Tx Desc writeback RO bit */ +#define IXGBE_DCA_TXCTRL_DATA_RRO_EN (1 << 13) /* Tx rd data Relax Order */ #define IXGBE_DCA_MAX_QUEUES_82598 16 /* DCA regs only on 16 queues */ /* MSCA Bit Masks */ @@ -1382,6 +1391,7 @@ enum { #define IXGBE_EICR_LINKSEC 0x00200000 /* PN Threshold */ #define IXGBE_EICR_MNG 0x00400000 /* Manageability Event Interrupt */ #define IXGBE_EICR_TS 0x00800000 /* Thermal Sensor Event */ +#define IXGBE_EICR_TIMESYNC 0x01000000 /* Timesync Event */ #define IXGBE_EICR_GPI_SDP0 0x01000000 /* Gen Purpose Interrupt on SDP0 */ #define IXGBE_EICR_GPI_SDP1 0x02000000 /* Gen Purpose Interrupt on SDP1 */ #define IXGBE_EICR_GPI_SDP2 0x04000000 /* Gen Purpose Interrupt on SDP2 */ @@ -1399,6 +1409,7 @@ enum { #define IXGBE_EICS_MAILBOX IXGBE_EICR_MAILBOX /* VF to PF Mailbox Int */ #define IXGBE_EICS_LSC IXGBE_EICR_LSC /* Link Status Change */ #define IXGBE_EICS_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */ +#define IXGBE_EICS_TIMESYNC IXGBE_EICR_TIMESYNC /* Timesync Event */ #define IXGBE_EICS_GPI_SDP0 IXGBE_EICR_GPI_SDP0 /* SDP0 Gen Purpose Int */ #define IXGBE_EICS_GPI_SDP1 IXGBE_EICR_GPI_SDP1 /* SDP1 Gen Purpose Int */ #define IXGBE_EICS_GPI_SDP2 IXGBE_EICR_GPI_SDP2 /* SDP2 Gen Purpose Int */ @@ -1417,6 +1428,7 @@ enum { #define IXGBE_EIMS_LSC IXGBE_EICR_LSC /* Link Status Change */ #define IXGBE_EIMS_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */ #define IXGBE_EIMS_TS IXGBE_EICR_TS /* Thermal Sensor Event */ +#define IXGBE_EIMS_TIMESYNC IXGBE_EICR_TIMESYNC /* Timesync Event */ #define IXGBE_EIMS_GPI_SDP0 IXGBE_EICR_GPI_SDP0 /* SDP0 Gen Purpose Int */ #define IXGBE_EIMS_GPI_SDP1 IXGBE_EICR_GPI_SDP1 /* SDP1 Gen Purpose Int */ #define IXGBE_EIMS_GPI_SDP2 IXGBE_EICR_GPI_SDP2 /* SDP2 Gen Purpose Int */ @@ -1434,6 +1446,7 @@ enum { #define IXGBE_EIMC_MAILBOX IXGBE_EICR_MAILBOX /* VF to PF Mailbox Int */ #define IXGBE_EIMC_LSC IXGBE_EICR_LSC /* Link Status Change */ #define IXGBE_EIMC_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */ +#define IXGBE_EIMC_TIMESYNC IXGBE_EICR_TIMESYNC /* Timesync Event */ #define IXGBE_EIMC_GPI_SDP0 IXGBE_EICR_GPI_SDP0 /* SDP0 Gen Purpose Int */ #define IXGBE_EIMC_GPI_SDP1 IXGBE_EICR_GPI_SDP1 /* SDP1 Gen Purpose Int */ #define IXGBE_EIMC_GPI_SDP2 IXGBE_EICR_GPI_SDP2 /* SDP2 Gen Purpose Int */ @@ -1519,6 +1532,7 @@ enum { #define IXGBE_ETQF_1588 0x40000000 /* bit 30 */ #define IXGBE_ETQF_FILTER_EN 0x80000000 /* bit 31 */ #define IXGBE_ETQF_POOL_ENABLE (1 << 26) /* bit 26 */ +#define IXGBE_ETQF_POOL_SHIFT 20 #define IXGBE_ETQS_RX_QUEUE 0x007F0000 /* bits 22:16 */ #define IXGBE_ETQS_RX_QUEUE_SHIFT 16 @@ -1573,8 +1587,17 @@ enum { #define IXGBE_ESDP_SDP4 0x00000010 /* SDP4 Data Value */ #define IXGBE_ESDP_SDP5 0x00000020 /* SDP5 Data Value */ #define IXGBE_ESDP_SDP6 0x00000040 /* SDP6 Data Value */ -#define IXGBE_ESDP_SDP4_DIR 0x00000004 /* SDP4 IO direction */ +#define IXGBE_ESDP_SDP7 0x00000080 /* SDP7 Data Value */ +#define IXGBE_ESDP_SDP0_DIR 0x00000100 /* SDP0 IO direction */ +#define IXGBE_ESDP_SDP1_DIR 0x00000200 /* SDP1 IO direction */ +#define IXGBE_ESDP_SDP3_DIR 0x00000800 /* SDP3 IO direction */ +#define IXGBE_ESDP_SDP4_DIR 0x00001000 /* SDP4 IO direction */ #define IXGBE_ESDP_SDP5_DIR 0x00002000 /* SDP5 IO direction */ +#define IXGBE_ESDP_SDP6_DIR 0x00004000 /* SDP6 IO direction */ +#define IXGBE_ESDP_SDP7_DIR 0x00008000 /* SDP7 IO direction */ +#define IXGBE_ESDP_SDP0_NATIVE 0x00010000 /* SDP0 IO mode */ +#define IXGBE_ESDP_SDP1_NATIVE 0x00020000 /* SDP1 IO mode */ + /* LEDCTL Bit Masks */ #define IXGBE_LED_IVRT_BASE 0x00000040 @@ -1777,7 +1800,9 @@ enum { #define IXGBE_DEVICE_CAPS 0x2C #define IXGBE_SERIAL_NUMBER_MAC_ADDR 0x11 #define IXGBE_PCIE_MSIX_82599_CAPS 0x72 +#define IXGBE_MAX_MSIX_VECTORS_82599 0x40 #define IXGBE_PCIE_MSIX_82598_CAPS 0x62 +#define IXGBE_MAX_MSIX_VECTORS_82598 0x13 /* MSI-X capability fields masks */ #define IXGBE_PCIE_MSIX_TBL_SZ_MASK 0x7FF @@ -1872,6 +1897,7 @@ enum { #define IXGBE_PCI_LINK_SPEED 0xF #define IXGBE_PCI_LINK_SPEED_2500 0x1 #define IXGBE_PCI_LINK_SPEED_5000 0x2 +#define IXGBE_PCI_LINK_SPEED_8000 0x3 #define IXGBE_PCI_HEADER_TYPE_REGISTER 0x0E #define IXGBE_PCI_HEADER_TYPE_MULTIFUNC 0x80 #define IXGBE_PCI_DEVICE_CONTROL2_16ms 0x0005 @@ -1934,6 +1960,10 @@ enum { #define IXGBE_RXDCTL_RLPML_EN 0x00008000 #define IXGBE_RXDCTL_VME 0x40000000 /* VLAN mode enable */ +#define IXGBE_TSAUXC_EN_CLK 0x00000004 +#define IXGBE_TSAUXC_SYNCLK 0x00000008 +#define IXGBE_TSAUXC_SDP0_INT 0x00000040 + #define IXGBE_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */ #define IXGBE_TSYNCTXCTL_ENABLED 0x00000010 /* Tx timestamping enabled */ @@ -1977,8 +2007,7 @@ enum { #define IXGBE_MFLCN_DPF 0x00000002 /* Discard Pause Frame */ #define IXGBE_MFLCN_RPFCE 0x00000004 /* Receive Priority FC Enable */ #define IXGBE_MFLCN_RFCE 0x00000008 /* Receive FC Enable */ -#define IXGBE_MFLCN_RPFCM 0x00000004 /* Receive Priority FC Mode */ -#define IXGBE_MFLCN_RPFCE_MASK 0x00000FF0 /* Rx Priority FC bitmap mask */ +#define IXGBE_MFLCN_RPFCE_MASK 0x00000FF4 /* Rx Priority FC bitmap mask */ #define IXGBE_MFLCN_RPFCE_SHIFT 4 /* Rx Priority FC bitmap shift */ /* Multiple Receive Queue Control */ @@ -2513,13 +2542,14 @@ typedef u32 ixgbe_physical_layer; #define IXGBE_PHYSICAL_LAYER_10GBASE_KR 0x0800 #define IXGBE_PHYSICAL_LAYER_10GBASE_XAUI 0x1000 #define IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA 0x2000 +#define IXGBE_PHYSICAL_LAYER_1000BASE_SX 0x4000 /* Flow Control Data Sheet defined values * Calculation and defines taken from 802.1bb Annex O */ /* BitTimes (BT) conversion */ -#define IXGBE_BT2KB(BT) ((BT + 1023) / (8 * 1024)) +#define IXGBE_BT2KB(BT) ((BT + (8 * 1024 - 1)) / (8 * 1024)) #define IXGBE_B2BT(BT) (BT * 8) /* Calculate Delay to respond to PFC */ @@ -2550,24 +2580,31 @@ typedef u32 ixgbe_physical_layer; #define IXGBE_PCI_DELAY 10000 /* Calculate X540 delay value in bit times */ -#define IXGBE_FILL_RATE (36 / 25) - -#define IXGBE_DV_X540(LINK, TC) (IXGBE_FILL_RATE * \ - (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \ - (2 * IXGBE_CABLE_DC) + \ - (2 * IXGBE_ID_X540) + \ - IXGBE_HD + IXGBE_B2BT(TC))) +#define IXGBE_DV_X540(_max_frame_link, _max_frame_tc) \ + ((36 * \ + (IXGBE_B2BT(_max_frame_link) + \ + IXGBE_PFC_D + \ + (2 * IXGBE_CABLE_DC) + \ + (2 * IXGBE_ID_X540) + \ + IXGBE_HD) / 25 + 1) + \ + 2 * IXGBE_B2BT(_max_frame_tc)) /* Calculate 82599, 82598 delay value in bit times */ -#define IXGBE_DV(LINK, TC) (IXGBE_FILL_RATE * \ - (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \ - (2 * IXGBE_CABLE_DC) + (2 * IXGBE_ID) + \ - IXGBE_HD + IXGBE_B2BT(TC))) +#define IXGBE_DV(_max_frame_link, _max_frame_tc) \ + ((36 * \ + (IXGBE_B2BT(_max_frame_link) + \ + IXGBE_PFC_D + \ + (2 * IXGBE_CABLE_DC) + \ + (2 * IXGBE_ID) + \ + IXGBE_HD) / 25 + 1) + \ + 2 * IXGBE_B2BT(_max_frame_tc)) /* Calculate low threshold delay values */ -#define IXGBE_LOW_DV_X540(TC) (2 * IXGBE_B2BT(TC) + \ - (IXGBE_FILL_RATE * IXGBE_PCI_DELAY)) -#define IXGBE_LOW_DV(TC) (2 * IXGBE_LOW_DV_X540(TC)) +#define IXGBE_LOW_DV_X540(_max_frame_tc) \ + (2 * IXGBE_B2BT(_max_frame_tc) + \ + (36 * IXGBE_PCI_DELAY / 25) + 1) +#define IXGBE_LOW_DV(_max_frame_tc) \ + (2 * IXGBE_LOW_DV_X540(_max_frame_tc)) /* Software ATR hash keys */ #define IXGBE_ATR_BUCKET_HASH_KEY 0x3DAD14E2 @@ -2711,6 +2748,8 @@ enum ixgbe_sfp_type { ixgbe_sfp_type_da_act_lmt_core1 = 8, ixgbe_sfp_type_1g_cu_core0 = 9, ixgbe_sfp_type_1g_cu_core1 = 10, + ixgbe_sfp_type_1g_sx_core0 = 11, + ixgbe_sfp_type_1g_sx_core1 = 12, ixgbe_sfp_type_not_present = 0xFFFE, ixgbe_sfp_type_unknown = 0xFFFF }; @@ -2760,6 +2799,7 @@ enum ixgbe_bus_speed { ixgbe_bus_speed_133 = 133, ixgbe_bus_speed_2500 = 2500, ixgbe_bus_speed_5000 = 5000, + ixgbe_bus_speed_8000 = 8000, ixgbe_bus_speed_reserved }; @@ -2796,7 +2836,7 @@ struct ixgbe_bus_info { /* Flow control parameters */ struct ixgbe_fc_info { u32 high_water[IXGBE_DCB_MAX_TRAFFIC_CLASS]; /* Flow Ctrl High-water */ - u32 low_water; /* Flow Control Low-water */ + u32 low_water[IXGBE_DCB_MAX_TRAFFIC_CLASS]; /* Flow Ctrl Low-water */ u16 pause_time; /* Flow Control Pause timer */ bool send_xon; /* Flow control send XON */ bool strict_ieee; /* Strict IEEE mode */ @@ -2955,6 +2995,7 @@ struct ixgbe_mac_operations { s32 (*clear_rar)(struct ixgbe_hw *, u32); s32 (*insert_mac_addr)(struct ixgbe_hw *, u8 *, u32); s32 (*set_vmdq)(struct ixgbe_hw *, u32, u32); + s32 (*set_vmdq_san_mac)(struct ixgbe_hw *, u32); s32 (*clear_vmdq)(struct ixgbe_hw *, u32, u32); s32 (*init_rx_addrs)(struct ixgbe_hw *); s32 (*update_uc_addr_list)(struct ixgbe_hw *, u8 *, u32, @@ -2971,7 +3012,7 @@ struct ixgbe_mac_operations { void (*set_vlan_anti_spoofing)(struct ixgbe_hw *, bool, int); /* Flow Control */ - s32 (*fc_enable)(struct ixgbe_hw *, s32); + s32 (*fc_enable)(struct ixgbe_hw *); /* Manageability interface */ s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8); @@ -3027,11 +3068,11 @@ struct ixgbe_mac_info { u32 rx_pb_size; u32 max_tx_queues; u32 max_rx_queues; - u32 max_msix_vectors; - bool msix_vectors_from_pcie; u32 orig_autoc; - bool arc_subsystem_valid; + u8 san_mac_rar_index; u32 orig_autoc2; + u16 max_msix_vectors; + bool arc_subsystem_valid; bool orig_link_settings_stored; bool autotry_restart; u8 flags; @@ -3102,6 +3143,7 @@ struct ixgbe_hw { u8 revision_id; bool adapter_stopped; bool force_full_reset; + bool allow_unsupported_sfp; }; #define ixgbe_call_func(hw, func, params, error) \ @@ -3138,7 +3180,6 @@ struct ixgbe_hw { #define IXGBE_ERR_OVERTEMP -26 #define IXGBE_ERR_FC_NOT_NEGOTIATED -27 #define IXGBE_ERR_FC_NOT_SUPPORTED -28 -#define IXGBE_ERR_FLOW_CONTROL -29 #define IXGBE_ERR_SFP_SETUP_NOT_COMPLETE -30 #define IXGBE_ERR_PBA_SECTION -31 #define IXGBE_ERR_INVALID_ARGUMENT -32 diff --git a/sys/dev/ixgbe/ixgbe_vf.c b/sys/dev/ixgbe/ixgbe_vf.c index a3147aa..9e3dd72 100644 --- a/sys/dev/ixgbe/ixgbe_vf.c +++ b/sys/dev/ixgbe/ixgbe_vf.c @@ -366,6 +366,7 @@ s32 ixgbe_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; + s32 ret_val; UNREFERENCED_1PARAMETER(vind); msgbuf[0] = IXGBE_VF_SET_VLAN; @@ -373,7 +374,14 @@ s32 ixgbe_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) /* Setting the 8 bit field MSG INFO to TRUE indicates "add" */ msgbuf[0] |= vlan_on << IXGBE_VT_MSGINFO_SHIFT; - return mbx->ops.write_posted(hw, msgbuf, 2, 0); + ret_val = mbx->ops.write_posted(hw, msgbuf, 2, 0); + if (!ret_val) + ret_val = mbx->ops.read_posted(hw, msgbuf, 1, 0); + + if (!ret_val && (msgbuf[0] & IXGBE_VT_MSGTYPE_ACK)) + return IXGBE_SUCCESS; + + return ret_val | (msgbuf[0] & IXGBE_VT_MSGTYPE_NACK); } /** @@ -491,11 +499,17 @@ s32 ixgbe_check_mac_link_vf(struct ixgbe_hw *hw, ixgbe_link_speed *speed, else *link_up = FALSE; - if ((links_reg & IXGBE_LINKS_SPEED_10G_82599) == - IXGBE_LINKS_SPEED_10G_82599) + switch (links_reg & IXGBE_LINKS_SPEED_10G_82599) { + case IXGBE_LINKS_SPEED_10G_82599: *speed = IXGBE_LINK_SPEED_10GB_FULL; - else + break; + case IXGBE_LINKS_SPEED_1G_82599: *speed = IXGBE_LINK_SPEED_1GB_FULL; + break; + case IXGBE_LINKS_SPEED_100_82599: + *speed = IXGBE_LINK_SPEED_100_FULL; + break; + } return IXGBE_SUCCESS; } diff --git a/sys/dev/ixgbe/ixgbe_x540.c b/sys/dev/ixgbe/ixgbe_x540.c index cab014a..0635cda 100755 --- a/sys/dev/ixgbe/ixgbe_x540.c +++ b/sys/dev/ixgbe/ixgbe_x540.c @@ -98,6 +98,7 @@ s32 ixgbe_init_ops_X540(struct ixgbe_hw *hw) /* RAR, Multicast, VLAN */ mac->ops.set_vmdq = &ixgbe_set_vmdq_generic; + mac->ops.set_vmdq_san_mac = &ixgbe_set_vmdq_san_mac_generic; mac->ops.clear_vmdq = &ixgbe_clear_vmdq_generic; mac->ops.insert_mac_addr = &ixgbe_insert_mac_addr_generic; mac->rar_highwater = 1; @@ -262,6 +263,9 @@ mac_reset_top: hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, hw->mac.san_addr, 0, IXGBE_RAH_AV); + /* Save the SAN MAC RAR index */ + hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; + /* Reserve the last RAR for the SAN MAC address */ hw->mac.num_rar_entries--; } @@ -798,7 +802,7 @@ out: * @hw: pointer to hardware structure * @mask: Mask to specify which semaphore to release * - * Releases the SWFW semaphore throught the SW_FW_SYNC register + * Releases the SWFW semaphore through the SW_FW_SYNC register * for the specified function (CSR, PHY0, PHY1, EVM, Flash) **/ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u16 mask) diff --git a/sys/dev/ixgbe/ixv.c b/sys/dev/ixgbe/ixv.c index a0594b9..ea7b673 100644 --- a/sys/dev/ixgbe/ixv.c +++ b/sys/dev/ixgbe/ixv.c @@ -42,7 +42,7 @@ /********************************************************************* * Driver version *********************************************************************/ -char ixv_driver_version[] = "1.1.2"; +char ixv_driver_version[] = "1.1.4"; /********************************************************************* * PCI Device ID Table @@ -386,7 +386,7 @@ ixv_attach(device_t dev) /* Get Hardware Flow Control setting */ hw->fc.requested_mode = ixgbe_fc_full; hw->fc.pause_time = IXV_FC_PAUSE; - hw->fc.low_water = IXV_FC_LO; + hw->fc.low_water[0] = IXV_FC_LO; hw->fc.high_water[0] = IXV_FC_HI; hw->fc.send_xon = TRUE; @@ -2298,7 +2298,7 @@ ixv_initialize_transmit_units(struct adapter *adapter) adapter->num_tx_desc * sizeof(struct ixgbe_legacy_tx_desc)); txctrl = IXGBE_READ_REG(hw, IXGBE_VFDCA_TXCTRL(i)); - txctrl &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; + txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; IXGBE_WRITE_REG(hw, IXGBE_VFDCA_TXCTRL(i), txctrl); break; } @@ -4003,7 +4003,7 @@ ixv_set_flowcntl(SYSCTL_HANDLER_ARGS) adapter->hw.fc.requested_mode = ixgbe_fc_none; } - ixgbe_fc_enable(&adapter->hw, 0); + ixgbe_fc_enable(&adapter->hw); return error; } diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 109d66f..64582eb 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -77,8 +77,8 @@ __FBSDID("$FreeBSD$"); #endif #ifdef DEVICE_POLLING -extern void hardclock_device_poll(void); -#endif /* DEVICE_POLLING */ +#include +#endif static void initclocks(void *dummy); SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL); @@ -442,6 +442,9 @@ hardclock_cpu(int usermode) td->td_flags |= flags; thread_unlock(td); +#ifdef DEVICE_POLLING + pollif_sched(); +#endif /* DEVICE_POLLING */ #ifdef HWPMC_HOOKS if (PMC_CPU_HAS_SAMPLES(PCPU_GET(cpuid))) PMC_CALL_HOOK_UNLOCKED(curthread, PMC_FN_DO_SAMPLES, NULL); @@ -468,9 +471,6 @@ hardclock(int usermode, uintfptr_t pc) profclock(usermode, pc); statclock(usermode); } -#ifdef DEVICE_POLLING - hardclock_device_poll(); /* this is very short and quick */ -#endif /* DEVICE_POLLING */ #ifdef SW_WATCHDOG if (watchdog_enabled > 0 && --watchdog_ticks <= 0) watchdog_fire(); diff --git a/sys/kern/kern_poll.c b/sys/kern/kern_poll.c deleted file mode 100644 index 8441126..0000000 --- a/sys/kern/kern_poll.c +++ /dev/null @@ -1,568 +0,0 @@ -/*- - * Copyright (c) 2001-2002 Luigi Rizzo - * - * Supported by: the Xorp Project (www.xorp.org) - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_device_polling.h" - -#include -#include -#include -#include -#include -#include -#include -#include /* needed by net/if.h */ -#include -#include -#include - -#include /* for IFF_* flags */ -#include /* for NETISR_POLL */ -#include - -void hardclock_device_poll(void); /* hook from hardclock */ - -static struct mtx poll_mtx; - -/* - * Polling support for [network] device drivers. - * - * Drivers which support this feature can register with the - * polling code. - * - * If registration is successful, the driver must disable interrupts, - * and further I/O is performed through the handler, which is invoked - * (at least once per clock tick) with 3 arguments: the "arg" passed at - * register time (a struct ifnet pointer), a command, and a "count" limit. - * - * The command can be one of the following: - * POLL_ONLY: quick move of "count" packets from input/output queues. - * POLL_AND_CHECK_STATUS: as above, plus check status registers or do - * other more expensive operations. This command is issued periodically - * but less frequently than POLL_ONLY. - * - * The count limit specifies how much work the handler can do during the - * call -- typically this is the number of packets to be received, or - * transmitted, etc. (drivers are free to interpret this number, as long - * as the max time spent in the function grows roughly linearly with the - * count). - * - * Polling is enabled and disabled via setting IFCAP_POLLING flag on - * the interface. The driver ioctl handler should register interface - * with polling and disable interrupts, if registration was successful. - * - * A second variable controls the sharing of CPU between polling/kernel - * network processing, and other activities (typically userlevel tasks): - * kern.polling.user_frac (between 0 and 100, default 50) sets the share - * of CPU allocated to user tasks. CPU is allocated proportionally to the - * shares, by dynamically adjusting the "count" (poll_burst). - * - * Other parameters can should be left to their default values. - * The following constraints hold - * - * 1 <= poll_each_burst <= poll_burst <= poll_burst_max - * 0 <= poll_each_burst - * MIN_POLL_BURST_MAX <= poll_burst_max <= MAX_POLL_BURST_MAX - */ - -#define MIN_POLL_BURST_MAX 10 -#define MAX_POLL_BURST_MAX 1000 - -static uint32_t poll_burst = 5; -static uint32_t poll_burst_max = 150; /* good for 100Mbit net and HZ=1000 */ -static uint32_t poll_each_burst = 5; - -SYSCTL_NODE(_kern, OID_AUTO, polling, CTLFLAG_RW, 0, - "Device polling parameters"); - -SYSCTL_UINT(_kern_polling, OID_AUTO, burst, CTLFLAG_RD, - &poll_burst, 0, "Current polling burst size"); - -static int netisr_poll_scheduled; -static int netisr_pollmore_scheduled; -static int poll_shutting_down; - -static int poll_burst_max_sysctl(SYSCTL_HANDLER_ARGS) -{ - uint32_t val = poll_burst_max; - int error; - - error = sysctl_handle_int(oidp, &val, 0, req); - if (error || !req->newptr ) - return (error); - if (val < MIN_POLL_BURST_MAX || val > MAX_POLL_BURST_MAX) - return (EINVAL); - - mtx_lock(&poll_mtx); - poll_burst_max = val; - if (poll_burst > poll_burst_max) - poll_burst = poll_burst_max; - if (poll_each_burst > poll_burst_max) - poll_each_burst = MIN_POLL_BURST_MAX; - mtx_unlock(&poll_mtx); - - return (0); -} -SYSCTL_PROC(_kern_polling, OID_AUTO, burst_max, CTLTYPE_UINT | CTLFLAG_RW, - 0, sizeof(uint32_t), poll_burst_max_sysctl, "I", "Max Polling burst size"); - -static int poll_each_burst_sysctl(SYSCTL_HANDLER_ARGS) -{ - uint32_t val = poll_each_burst; - int error; - - error = sysctl_handle_int(oidp, &val, 0, req); - if (error || !req->newptr ) - return (error); - if (val < 1) - return (EINVAL); - - mtx_lock(&poll_mtx); - if (val > poll_burst_max) { - mtx_unlock(&poll_mtx); - return (EINVAL); - } - poll_each_burst = val; - mtx_unlock(&poll_mtx); - - return (0); -} -SYSCTL_PROC(_kern_polling, OID_AUTO, each_burst, CTLTYPE_UINT | CTLFLAG_RW, - 0, sizeof(uint32_t), poll_each_burst_sysctl, "I", - "Max size of each burst"); - -static uint32_t poll_in_idle_loop=0; /* do we poll in idle loop ? */ -SYSCTL_UINT(_kern_polling, OID_AUTO, idle_poll, CTLFLAG_RW, - &poll_in_idle_loop, 0, "Enable device polling in idle loop"); - -static uint32_t user_frac = 50; -static int user_frac_sysctl(SYSCTL_HANDLER_ARGS) -{ - uint32_t val = user_frac; - int error; - - error = sysctl_handle_int(oidp, &val, 0, req); - if (error || !req->newptr ) - return (error); - if (val < 0 || val > 99) - return (EINVAL); - - mtx_lock(&poll_mtx); - user_frac = val; - mtx_unlock(&poll_mtx); - - return (0); -} -SYSCTL_PROC(_kern_polling, OID_AUTO, user_frac, CTLTYPE_UINT | CTLFLAG_RW, - 0, sizeof(uint32_t), user_frac_sysctl, "I", - "Desired user fraction of cpu time"); - -static uint32_t reg_frac_count = 0; -static uint32_t reg_frac = 20 ; -static int reg_frac_sysctl(SYSCTL_HANDLER_ARGS) -{ - uint32_t val = reg_frac; - int error; - - error = sysctl_handle_int(oidp, &val, 0, req); - if (error || !req->newptr ) - return (error); - if (val < 1 || val > hz) - return (EINVAL); - - mtx_lock(&poll_mtx); - reg_frac = val; - if (reg_frac_count >= reg_frac) - reg_frac_count = 0; - mtx_unlock(&poll_mtx); - - return (0); -} -SYSCTL_PROC(_kern_polling, OID_AUTO, reg_frac, CTLTYPE_UINT | CTLFLAG_RW, - 0, sizeof(uint32_t), reg_frac_sysctl, "I", - "Every this many cycles check registers"); - -static uint32_t short_ticks; -SYSCTL_UINT(_kern_polling, OID_AUTO, short_ticks, CTLFLAG_RD, - &short_ticks, 0, "Hardclock ticks shorter than they should be"); - -static uint32_t lost_polls; -SYSCTL_UINT(_kern_polling, OID_AUTO, lost_polls, CTLFLAG_RD, - &lost_polls, 0, "How many times we would have lost a poll tick"); - -static uint32_t pending_polls; -SYSCTL_UINT(_kern_polling, OID_AUTO, pending_polls, CTLFLAG_RD, - &pending_polls, 0, "Do we need to poll again"); - -static int residual_burst = 0; -SYSCTL_INT(_kern_polling, OID_AUTO, residual_burst, CTLFLAG_RD, - &residual_burst, 0, "# of residual cycles in burst"); - -static uint32_t poll_handlers; /* next free entry in pr[]. */ -SYSCTL_UINT(_kern_polling, OID_AUTO, handlers, CTLFLAG_RD, - &poll_handlers, 0, "Number of registered poll handlers"); - -static uint32_t phase; -SYSCTL_UINT(_kern_polling, OID_AUTO, phase, CTLFLAG_RD, - &phase, 0, "Polling phase"); - -static uint32_t suspect; -SYSCTL_UINT(_kern_polling, OID_AUTO, suspect, CTLFLAG_RD, - &suspect, 0, "suspect event"); - -static uint32_t stalled; -SYSCTL_UINT(_kern_polling, OID_AUTO, stalled, CTLFLAG_RD, - &stalled, 0, "potential stalls"); - -static uint32_t idlepoll_sleeping; /* idlepoll is sleeping */ -SYSCTL_UINT(_kern_polling, OID_AUTO, idlepoll_sleeping, CTLFLAG_RD, - &idlepoll_sleeping, 0, "idlepoll is sleeping"); - - -#define POLL_LIST_LEN 128 -struct pollrec { - poll_handler_t *handler; - struct ifnet *ifp; -}; - -static struct pollrec pr[POLL_LIST_LEN]; - -static void -poll_shutdown(void *arg, int howto) -{ - - poll_shutting_down = 1; -} - -static void -init_device_poll(void) -{ - - mtx_init(&poll_mtx, "polling", NULL, MTX_DEF); - EVENTHANDLER_REGISTER(shutdown_post_sync, poll_shutdown, NULL, - SHUTDOWN_PRI_LAST); -} -SYSINIT(device_poll, SI_SUB_CLOCKS, SI_ORDER_MIDDLE, init_device_poll, NULL); - - -/* - * Hook from hardclock. Tries to schedule a netisr, but keeps track - * of lost ticks due to the previous handler taking too long. - * Normally, this should not happen, because polling handler should - * run for a short time. However, in some cases (e.g. when there are - * changes in link status etc.) the drivers take a very long time - * (even in the order of milliseconds) to reset and reconfigure the - * device, causing apparent lost polls. - * - * The first part of the code is just for debugging purposes, and tries - * to count how often hardclock ticks are shorter than they should, - * meaning either stray interrupts or delayed events. - */ -void -hardclock_device_poll(void) -{ - static struct timeval prev_t, t; - int delta; - - if (poll_handlers == 0 || poll_shutting_down) - return; - - microuptime(&t); - delta = (t.tv_usec - prev_t.tv_usec) + - (t.tv_sec - prev_t.tv_sec)*1000000; - if (delta * hz < 500000) - short_ticks++; - else - prev_t = t; - - if (pending_polls > 100) { - /* - * Too much, assume it has stalled (not always true - * see comment above). - */ - stalled++; - pending_polls = 0; - phase = 0; - } - - if (phase <= 2) { - if (phase != 0) - suspect++; - phase = 1; - netisr_poll_scheduled = 1; - netisr_pollmore_scheduled = 1; - netisr_sched_poll(); - phase = 2; - } - if (pending_polls++ > 0) - lost_polls++; -} - -/* - * ether_poll is called from the idle loop. - */ -static void -ether_poll(int count) -{ - int i; - - mtx_lock(&poll_mtx); - - if (count > poll_each_burst) - count = poll_each_burst; - - for (i = 0 ; i < poll_handlers ; i++) - pr[i].handler(pr[i].ifp, POLL_ONLY, count); - - mtx_unlock(&poll_mtx); -} - -/* - * netisr_pollmore is called after other netisr's, possibly scheduling - * another NETISR_POLL call, or adapting the burst size for the next cycle. - * - * It is very bad to fetch large bursts of packets from a single card at once, - * because the burst could take a long time to be completely processed, or - * could saturate the intermediate queue (ipintrq or similar) leading to - * losses or unfairness. To reduce the problem, and also to account better for - * time spent in network-related processing, we split the burst in smaller - * chunks of fixed size, giving control to the other netisr's between chunks. - * This helps in improving the fairness, reducing livelock (because we - * emulate more closely the "process to completion" that we have with - * fastforwarding) and accounting for the work performed in low level - * handling and forwarding. - */ - -static struct timeval poll_start_t; - -void -netisr_pollmore() -{ - struct timeval t; - int kern_load; - - mtx_lock(&poll_mtx); - if (!netisr_pollmore_scheduled) { - mtx_unlock(&poll_mtx); - return; - } - netisr_pollmore_scheduled = 0; - phase = 5; - if (residual_burst > 0) { - netisr_poll_scheduled = 1; - netisr_pollmore_scheduled = 1; - netisr_sched_poll(); - mtx_unlock(&poll_mtx); - /* will run immediately on return, followed by netisrs */ - return; - } - /* here we can account time spent in netisr's in this tick */ - microuptime(&t); - kern_load = (t.tv_usec - poll_start_t.tv_usec) + - (t.tv_sec - poll_start_t.tv_sec)*1000000; /* us */ - kern_load = (kern_load * hz) / 10000; /* 0..100 */ - if (kern_load > (100 - user_frac)) { /* try decrease ticks */ - if (poll_burst > 1) - poll_burst--; - } else { - if (poll_burst < poll_burst_max) - poll_burst++; - } - - pending_polls--; - if (pending_polls == 0) /* we are done */ - phase = 0; - else { - /* - * Last cycle was long and caused us to miss one or more - * hardclock ticks. Restart processing again, but slightly - * reduce the burst size to prevent that this happens again. - */ - poll_burst -= (poll_burst / 8); - if (poll_burst < 1) - poll_burst = 1; - netisr_poll_scheduled = 1; - netisr_pollmore_scheduled = 1; - netisr_sched_poll(); - phase = 6; - } - mtx_unlock(&poll_mtx); -} - -/* - * netisr_poll is typically scheduled once per tick. - */ -void -netisr_poll(void) -{ - int i, cycles; - enum poll_cmd arg = POLL_ONLY; - - mtx_lock(&poll_mtx); - if (!netisr_poll_scheduled) { - mtx_unlock(&poll_mtx); - return; - } - netisr_poll_scheduled = 0; - phase = 3; - if (residual_burst == 0) { /* first call in this tick */ - microuptime(&poll_start_t); - if (++reg_frac_count == reg_frac) { - arg = POLL_AND_CHECK_STATUS; - reg_frac_count = 0; - } - - residual_burst = poll_burst; - } - cycles = (residual_burst < poll_each_burst) ? - residual_burst : poll_each_burst; - residual_burst -= cycles; - - for (i = 0 ; i < poll_handlers ; i++) - pr[i].handler(pr[i].ifp, arg, cycles); - - phase = 4; - mtx_unlock(&poll_mtx); -} - -/* - * Try to register routine for polling. Returns 0 if successful - * (and polling should be enabled), error code otherwise. - * A device is not supposed to register itself multiple times. - * - * This is called from within the *_ioctl() functions. - */ -int -ether_poll_register(poll_handler_t *h, struct ifnet *ifp) -{ - int i; - - KASSERT(h != NULL, ("%s: handler is NULL", __func__)); - KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__)); - - mtx_lock(&poll_mtx); - if (poll_handlers >= POLL_LIST_LEN) { - /* - * List full, cannot register more entries. - * This should never happen; if it does, it is probably a - * broken driver trying to register multiple times. Checking - * this at runtime is expensive, and won't solve the problem - * anyways, so just report a few times and then give up. - */ - static int verbose = 10 ; - if (verbose >0) { - log(LOG_ERR, "poll handlers list full, " - "maybe a broken driver ?\n"); - verbose--; - } - mtx_unlock(&poll_mtx); - return (ENOMEM); /* no polling for you */ - } - - for (i = 0 ; i < poll_handlers ; i++) - if (pr[i].ifp == ifp && pr[i].handler != NULL) { - mtx_unlock(&poll_mtx); - log(LOG_DEBUG, "ether_poll_register: %s: handler" - " already registered\n", ifp->if_xname); - return (EEXIST); - } - - pr[poll_handlers].handler = h; - pr[poll_handlers].ifp = ifp; - poll_handlers++; - mtx_unlock(&poll_mtx); - if (idlepoll_sleeping) - wakeup(&idlepoll_sleeping); - return (0); -} - -/* - * Remove interface from the polling list. Called from *_ioctl(), too. - */ -int -ether_poll_deregister(struct ifnet *ifp) -{ - int i; - - KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__)); - - mtx_lock(&poll_mtx); - - for (i = 0 ; i < poll_handlers ; i++) - if (pr[i].ifp == ifp) /* found it */ - break; - if (i == poll_handlers) { - log(LOG_DEBUG, "ether_poll_deregister: %s: not found!\n", - ifp->if_xname); - mtx_unlock(&poll_mtx); - return (ENOENT); - } - poll_handlers--; - if (i < poll_handlers) { /* Last entry replaces this one. */ - pr[i].handler = pr[poll_handlers].handler; - pr[i].ifp = pr[poll_handlers].ifp; - } - mtx_unlock(&poll_mtx); - return (0); -} - -static void -poll_idle(void) -{ - struct thread *td = curthread; - struct rtprio rtp; - - rtp.prio = RTP_PRIO_MAX; /* lowest priority */ - rtp.type = RTP_PRIO_IDLE; - PROC_SLOCK(td->td_proc); - rtp_to_pri(&rtp, td); - PROC_SUNLOCK(td->td_proc); - - for (;;) { - if (poll_in_idle_loop && poll_handlers > 0) { - idlepoll_sleeping = 0; - ether_poll(poll_each_burst); - thread_lock(td); - mi_switch(SW_VOL, NULL); - thread_unlock(td); - } else { - idlepoll_sleeping = 1; - tsleep(&idlepoll_sleeping, 0, "pollid", hz * 3); - } - } -} - -static struct proc *idlepoll; -static struct kproc_desc idlepoll_kp = { - "idlepoll", - poll_idle, - &idlepoll -}; -SYSINIT(idlepoll, SI_SUB_KTHREAD_VM, SI_ORDER_ANY, kproc_start, - &idlepoll_kp); diff --git a/sys/modules/igb/Makefile b/sys/modules/igb/Makefile index a66c32a..45b7ec2 100644 --- a/sys/modules/igb/Makefile +++ b/sys/modules/igb/Makefile @@ -6,7 +6,7 @@ SRCS += if_igb.c $(SHARED_SRCS) SHARED_SRCS = e1000_api.c e1000_phy.c e1000_nvm.c e1000_mac.c e1000_manage.c SHARED_SRCS += e1000_80003es2lan.c e1000_82542.c e1000_82541.c e1000_82543.c SHARED_SRCS += e1000_82540.c e1000_ich8lan.c e1000_82571.c e1000_osdep.c -SHARED_SRCS += e1000_82575.c e1000_vf.c e1000_mbx.c +SHARED_SRCS += e1000_82575.c e1000_vf.c e1000_mbx.c e1000_i210.c CFLAGS += -I${.CURDIR}/../../dev/e1000 -DSMP diff --git a/sys/net/if.c b/sys/net/if.c index b950e00..e37f59a 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -33,6 +33,7 @@ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_inet.h" +#include "opt_device_polling.h" #include #include @@ -3415,6 +3416,11 @@ void if_start(struct ifnet *ifp) { +#ifdef DEVICE_POLLING + if ((ifp->if_capenable & IFCAP_POLLING) && + !_IF_QFULL(&ifp->if_snd)) + return; +#endif (*(ifp)->if_start)(ifp); } diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index a097ab4e..4d1b8ea 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -561,7 +561,7 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, int shared) * mbuf chain m with the ethernet header at the front. */ static void -ether_input(struct ifnet *ifp, struct mbuf *m) +ether_input_internal(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; @@ -757,6 +757,45 @@ ether_input(struct ifnet *ifp, struct mbuf *m) } /* + * Ethernet input dispatch; by default, direct dispatch here regardless of + * global configuration. + */ +static void +ether_nh_input(struct mbuf *m) +{ + + ether_input_internal(m->m_pkthdr.rcvif, m); +} + +static struct netisr_handler ether_nh = { + .nh_name = "ether", + .nh_handler = ether_nh_input, + .nh_proto = NETISR_ETHER, + .nh_policy = NETISR_POLICY_FLOW, +}; + +static void +ether_init(__unused void *arg) +{ + + netisr_register(ðer_nh); +} +SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL); + +static void +ether_input(struct ifnet *ifp, struct mbuf *m) +{ + + /* + * We will rely on rcvif being set properly in the deferred context, + * so assert it is correct here. + */ + KASSERT(m->m_pkthdr.rcvif == ifp, ("%s: ifnet mismatch", __func__)); + + netisr_dispatch(NETISR_ETHER, m); +} + +/* * Upper layer processing for a received Ethernet packet. */ void diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 64776a3..3a99c4a 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -899,11 +899,16 @@ void if_deregister_com_alloc(u_char type); LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) #ifdef DEVICE_POLLING -enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; +enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS, POLL_TX }; typedef int poll_handler_t(struct ifnet *ifp, enum poll_cmd cmd, int count); int ether_poll_register(poll_handler_t *h, struct ifnet *ifp); int ether_poll_deregister(struct ifnet *ifp); +typedef int poll_handler_mq_t(struct ifnet *ifp, void *queue, + enum poll_cmd cmd, int count, struct mbuf **); +int ether_poll_register_mq(poll_handler_mq_t *mqh, + struct ifnet *ifp, int queue_id, int max_id, void *context); +int ether_poll_deregister_mq(struct ifnet *ifp, int queue_id); #endif /* DEVICE_POLLING */ #endif /* _KERNEL */ diff --git a/sys/net/netisr.c b/sys/net/netisr.c index cde6325..6a13cbb 100644 --- a/sys/net/netisr.c +++ b/sys/net/netisr.c @@ -662,6 +662,25 @@ netisr_process_workstream_proto(struct netisr_workstream *nwsp, u_int proto) return (handled); } +#ifdef DEVICE_POLLING +/* + * Kernel polling borrows netisr threads to run interface polling in; this + * function allows kernel polling to request that current CPU netisr thread + * be scheduled even if no packets are pending for protocols. + */ +void +netisr_curcpu_sched(void) +{ + struct netisr_workstream *nwsp; + + /* Signal the current CPU netisr. + */ + nwsp = DPCPU_PTR(nws); + if (nwsp->nws_intr_event != NULL) + NWS_SIGNAL(nwsp); +} +#endif + /* * SWI handler for netisr -- processes packets in a set of workstreams that * it owns, woken up by calls to NWS_SIGNAL(). If this workstream is already @@ -680,9 +699,8 @@ swi_net(void *arg) nwsp = arg; #ifdef DEVICE_POLLING - KASSERT(nws_count == 1, - ("%s: device_polling but nws_count != 1", __func__)); - netisr_poll(); +restart: + pollif_recv(); #endif #ifdef NETISR_LOCKING NETISR_RLOCK(&tracker); @@ -707,7 +725,8 @@ out: NETISR_RUNLOCK(&tracker); #endif #ifdef DEVICE_POLLING - netisr_pollmore(); + if (pollif_send()) + goto restart; #endif } @@ -946,22 +965,6 @@ netisr_dispatch(u_int proto, struct mbuf *m) return (netisr_dispatch_src(proto, 0, m)); } -#ifdef DEVICE_POLLING -/* - * Kernel polling borrows a netisr thread to run interface polling in; this - * function allows kernel polling to request that the netisr thread be - * scheduled even if no packets are pending for protocols. - */ -void -netisr_sched_poll(void) -{ - struct netisr_workstream *nwsp; - - nwsp = DPCPU_ID_PTR(nws_array[0], nws); - NWS_SIGNAL(nwsp); -} -#endif - static void netisr_start_swi(u_int cpuid, struct pcpu *pc) { @@ -980,6 +983,11 @@ netisr_start_swi(u_int cpuid, struct pcpu *pc) if (error) panic("%s: swi_add %d", __func__, error); pc->pc_netisr = nwsp->nws_intr_event; +#ifdef DEVICE_POLLING + /* Polling need threads bound to CPU. + */ + netisr_bindthreads = 1; +#endif if (netisr_bindthreads) { error = intr_event_bind(nwsp->nws_intr_event, cpuid); if (error != 0) @@ -990,6 +998,9 @@ netisr_start_swi(u_int cpuid, struct pcpu *pc) nws_array[nws_count] = nwsp->nws_cpu; nws_count++; NETISR_WUNLOCK(); +#ifdef DEVICE_POLLING + pollif_init_cpu(cpuid); +#endif } /* @@ -1019,19 +1030,6 @@ netisr_init(void *arg) netisr_defaultqlimit, netisr_maxqlimit); netisr_defaultqlimit = netisr_maxqlimit; } -#ifdef DEVICE_POLLING - /* - * The device polling code is not yet aware of how to deal with - * multiple netisr threads, so for the time being compiling in device - * polling disables parallel netisr workers. - */ - if (netisr_maxthreads != 1 || netisr_bindthreads != 0) { - printf("netisr_init: forcing maxthreads to 1 and " - "bindthreads to 0 for device polling\n"); - netisr_maxthreads = 1; - netisr_bindthreads = 0; - } -#endif netisr_start_swi(curcpu, pcpu_find(curcpu)); } @@ -1045,13 +1043,12 @@ static void netisr_start(void *arg) { struct pcpu *pc; + u_int cpuid; - SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { + CPU_FOREACH(cpuid) { + pc = cpuid_to_pcpu[cpuid]; if (nws_count >= netisr_maxthreads) break; - /* XXXRW: Is skipping absent CPUs still required here? */ - if (CPU_ABSENT(pc->pc_cpuid)) - continue; /* Worker will already be present for boot CPU. */ if (pc->pc_netisr != NULL) continue; diff --git a/sys/net/netisr.h b/sys/net/netisr.h index cd692f6..8417116 100644 --- a/sys/net/netisr.h +++ b/sys/net/netisr.h @@ -223,12 +223,12 @@ u_int netisr_default_flow2cpu(u_int flowid); u_int netisr_get_cpucount(void); u_int netisr_get_cpuid(u_int cpunumber); -/* - * Interfaces between DEVICE_POLLING and netisr. - */ -void netisr_sched_poll(void); -void netisr_poll(void); -void netisr_pollmore(void); +void netisr_curcpu_sched(void); + +void pollif_init_cpu(u_int cpuid); +int pollif_recv(void); +int pollif_send(void); +void pollif_sched(void); #endif /* !_KERNEL */ #endif /* !_NET_NETISR_H_ */ diff --git a/sys/net/netisr_pollif.c b/sys/net/netisr_pollif.c new file mode 100644 index 0000000..1191a78 --- /dev/null +++ b/sys/net/netisr_pollif.c @@ -0,0 +1,711 @@ +/*- + * Copyright (c) 2012 Fabien Thomas + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. + */ +#include +#include "opt_device_polling.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#if defined(__i386__) +#include +#elif defined(__amd64__) +#include +#endif + +#define POLLIF_TIME (int64_t)get_cyclecount() + +#define POLLIF_SYSCTL_PREFIX "kern.pollif." +#define POLLIF_MAPSIZE 128 + +SYSCTL_NODE(_kern, OID_AUTO, pollif, CTLFLAG_RW, 0, "pollif parameters"); + +static int pollif_handlers = 0; +SYSCTL_UINT(_kern_pollif, OID_AUTO, handlers, CTLFLAG_RD, + &pollif_handlers, 0, "Number of registered poll handlers"); + +#define POLLIF_PREFETCH +#ifdef POLLIF_PREFETCH +static __inline +void prefetch(void *x) +{ +#if defined(__i386__) || defined(__amd64__) + __asm volatile("prefetchnta %0" :: "m" (*(unsigned long *)x)); + /* % */ +#endif +} +#endif + +/* TODO: check min/max. */ +static int pollif_ppr = 16; +TUNABLE_INT(POLLIF_SYSCTL_PREFIX "packet_per_round", &pollif_ppr); +SYSCTL_INT(_kern_pollif, OID_AUTO, packet_per_round, CTLFLAG_TUN|CTLFLAG_RW, + &pollif_ppr, 0, "Packets per round for each interface"); + +/* TODO: check min/max. */ +static int pollif_sched_div = 0; +TUNABLE_INT(POLLIF_SYSCTL_PREFIX "sched_div", &pollif_sched_div); +SYSCTL_INT(_kern_pollif, OID_AUTO, sched_div, CTLFLAG_TUN|CTLFLAG_RW, + &pollif_sched_div, 0, "Scheduling divisor related to HZ"); + +static int pollif_regcheck = 10; +TUNABLE_INT(POLLIF_SYSCTL_PREFIX "register_check", &pollif_regcheck); +SYSCTL_INT(_kern_pollif, OID_AUTO, register_check, CTLFLAG_TUN|CTLFLAG_RW, + &pollif_regcheck, 0, "Minimum time between interface registers check (ms)"); + +static int pollif_deadline = 80; +TUNABLE_INT(POLLIF_SYSCTL_PREFIX "deadline", &pollif_deadline); +SYSCTL_INT(_kern_pollif, OID_AUTO, deadline, CTLFLAG_TUN|CTLFLAG_RW, + &pollif_deadline, 0, "Packet processing deadline (0-100)"); + +/* Syntax: :,... with as first netisr to use. + * Mapping are updated only when polling is enabled on the interface. + */ +static char pollif_map[POLLIF_MAPSIZE]; + +typedef struct poll_stat { + u_int cpu_load; + u_int pass; + uint64_t run_overflow; +} poll_stat_t; + +typedef struct poll_ifstat { + uint64_t resched_rx; +} poll_ifstat_t; + +/* + * Interface list. + */ +typedef struct poll_if { + SLIST_ENTRY(poll_if) link; + poll_handler_t *handler; /* Interface polling handler. */ + poll_handler_mq_t *handler_mq; /* MQ polling handler. */ + struct ifnet *ifp; /* Interface pointer. */ + int queue_id; /* Queue identifier. */ + void *context; /* Queue unique identifier. */ + int regcheck_hz; /* Last time registers have been checked. */ + poll_ifstat_t stat; /* Interface stats. */ +} poll_if_t; + +SLIST_HEAD(poll_if_head, poll_if); + +#define POLL_LOCK_INIT(a) rm_init(a, "pollif") +#define POLL_LOCK_DESTROY(a) rm_destroy(a) +#define POLL_RLOCK(a) rm_rlock(a, &tracker) +#define POLL_RUNLOCK(a) rm_runlock(a, &tracker) +#define POLL_WLOCK(a) rm_wlock(a) +#define POLL_WUNLOCK(a) rm_wunlock(a) +#define POLL_RTRACKER struct rm_priotracker tracker + +/* + * Per CPU info. + */ +typedef struct poll_wq { +#define PIWQ_INIT 0 +#define PIWQ_START 1 +#define PIWQ_RUN 2 +#define PIWQ_IDLE 3 +#define PIWQ_PAUSE 4 +#define PIWQ_CLEANUP 5 + int state; /* Current state. */ + struct rmlock lock; /* Lock. */ + struct poll_if_head iflist; /* List of interface. */ + int cpuid; /* Initial cpuid. */ + u_int pass; /* Current pass. */ + int sched_req; /* Pending sched request. */ + int64_t sched_cc; /* Last period cycle count. */ + int64_t sched_pass; /* Last pass cycle count. */ + int64_t sched_sum; /* Sum of all pass. */ + int64_t sched_hz; /* Cycle count for scheduler period. */ + int64_t sched_deadline; /* Last scheduled time. */ + int rcv_more; /* Receive state need more processing. */ + poll_stat_t stat; /* Stats. */ +} poll_wq_t; + +static poll_wq_t pollif_cpu[MAXCPU] __aligned(CACHE_LINE_SIZE); + +static u_int pollif_maxcpu = 0; +static u_int pollif_cpuid[MAXCPU]; + +static void +reset_stats(poll_stat_t *s) +{ + + bzero(s, sizeof(*s)); +} + +static void +reset_ifstats(poll_ifstat_t *s) +{ + + bzero(s, sizeof(*s)); +} + +static int +sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct sbuf *sb; + int i, error; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *p; + POLL_RTRACKER; + + sb = sbuf_new(NULL, NULL, 8192, SBUF_FIXEDLEN); + for (i = 0; i < pollif_maxcpu; i++) { + pwq = &pollif_cpu[pollif_cpuid[i]]; + + pif_head = &pwq->iflist; + POLL_RLOCK(&pwq->lock); + sbuf_printf(sb, "\nWork queue %d:\n", i); + sbuf_printf(sb, "CPU load = %3u %%\n", + pwq->stat.cpu_load); + sbuf_printf(sb, "pass = %u\n", + pwq->stat.pass); + sbuf_printf(sb, "run overflow = %llu\n", + (unsigned long long)pwq->stat.run_overflow); + SLIST_FOREACH(p, pif_head, link) { + sbuf_printf(sb, "Interface %s.%d\n", + p->ifp->if_xname, p->queue_id); + sbuf_printf(sb, " resched rx = %llu\n", + (unsigned long long)p->stat.resched_rx); + } + POLL_RUNLOCK(&pwq->lock); + } + sbuf_finish(sb); + error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); + + return (error); +} +SYSCTL_PROC(_kern_pollif, OID_AUTO, stats, CTLTYPE_STRING | CTLFLAG_RD, + NULL, 0, sysctl_stats, "A", "pollif statistics"); + +static int stats_clearflag; +static int +sysctl_stats_clear(SYSCTL_HANDLER_ARGS) +{ + int i, error; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *p; + POLL_RTRACKER; + + error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); + if (!error && req->newptr) { + stats_clearflag = 0; + for (i = 0; i < pollif_maxcpu; i++) { + pwq = &pollif_cpu[pollif_cpuid[i]]; + + pif_head = &pwq->iflist; + POLL_RLOCK(&pwq->lock); + reset_stats(&pwq->stat); + SLIST_FOREACH(p, pif_head, link) + reset_ifstats(&p->stat); + POLL_RUNLOCK(&pwq->lock); + } + } + return (error); +} +SYSCTL_PROC(_kern_pollif, OID_AUTO, stats_clear, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, &stats_clearflag, 0, + sysctl_stats_clear, "I", "Clear pollif stats"); + +static int +map_base(char *ifname, char *map) +{ + char *p, buf[POLLIF_MAPSIZE]; + char *ifn, *isr; + int nisr; + + strlcpy(buf, map, sizeof(buf)); + for (p = buf; p != NULL; ) { + ifn = p; + p = strchr(p, ':'); + if (p == NULL) + return -1; + *p++ = '\0'; + isr = p; + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + nisr = strtol(isr, NULL, 10); + if (nisr < 0 || nisr > pollif_maxcpu) + return -1; + if (strcasecmp(ifname, ifn) == 0) + return nisr; + } + + return -1; +} + +static int +sysctl_map(SYSCTL_HANDLER_ARGS) +{ + int error; + + error = sysctl_handle_string(oidp, pollif_map, sizeof(pollif_map), req); + if (error != 0 || req->newptr == NULL) + return (error); + + /* TODO: format check. */ + return (0); +} +SYSCTL_PROC(_kern_pollif, OID_AUTO, map, CTLTYPE_STRING | CTLFLAG_RW, + NULL, 0, sysctl_map, "A", "Interface placement"); + +void +pollif_sched(void) +{ + poll_wq_t *pwq; + int64_t cc; + + if (pollif_handlers == 0) + return; + + CTR0(KTR_SPARE2, "SCHED BEGIN"); + pwq = &pollif_cpu[PCPU_GET(cpuid)]; + + /* Check if a workqueue exist on this CPU. */ + if (pwq->state == PIWQ_INIT) + return; + + KASSERT(PCPU_GET(cpuid) == pwq->cpuid, ("host thread not bound")); + + cc = POLLIF_TIME; + pwq->sched_hz = cc - pwq->sched_cc; + pwq->sched_cc = cc; + + /* Schedule to run. */ + pwq->sched_req++; + CTR1(KTR_SPARE2, "SCHED sched_req=%d", + pwq->sched_req); + netisr_curcpu_sched(); + + CTR0(KTR_SPARE2, "SCHED END"); +} + +int +pollif_recv(void) +{ + int more, moreif; + int64_t cc; + enum poll_cmd arg; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *pif; + struct mbuf *mc, *m; + POLL_RTRACKER; + + if (pollif_handlers == 0) + return (0); + + CTR0(KTR_SPARE2, "RECV BEGIN"); + pwq = &pollif_cpu[PCPU_GET(cpuid)]; + pif_head = &pwq->iflist; + + switch (pwq->state) { + case PIWQ_START: + /* Do in context one time init. */ +#if (defined(__i386__) || defined(__amd64__)) && defined(FPU_KERN_NORMAL) + fpu_kern_thread(FPU_KERN_NORMAL); +#endif + pwq->sched_cc = POLLIF_TIME; + pwq->state = PIWQ_PAUSE; + break; + + case PIWQ_CLEANUP: + return (0); + } + + cc = POLLIF_TIME; + + /* New period ? */ + if (pwq->sched_req >= pollif_sched_div) { + CTR1(KTR_SPARE2, "RECV -> RUN sched_req=%u", pwq->sched_req); + if (pwq->state == PIWQ_RUN) + pwq->stat.run_overflow++; + + pwq->stat.pass = (pwq->stat.pass + pwq->pass) / 2; + if (pwq->sched_hz > 0 && pollif_sched_div > 0) { + pwq->stat.cpu_load = (pwq->stat.cpu_load + + 100 * pwq->sched_sum / + (pollif_sched_div * pwq->sched_hz)) / 2; + } + + pwq->pass = 1; + pwq->sched_sum = 0; + pwq->sched_deadline = pollif_sched_div * pwq->sched_hz * + pollif_deadline / 100; + pwq->state = PIWQ_RUN; + pwq->sched_req = 0; + } + + /* If called and idle restart processing. */ + if (pwq->state == PIWQ_IDLE) { + CTR0(KTR_SPARE2, "RECV IDLE -> RUN"); + pwq->state = PIWQ_RUN; + } + + /* If paused wait end of deadline. */ + if (pwq->state != PIWQ_RUN) + return (0); + + KASSERT(PCPU_GET(cpuid) == pwq->cpuid, ("host thread not bound")); + + CTR3(KTR_SPARE2, "RECV pass=%d cc=%u sched_hz=%u", + pwq->pass, (unsigned)cc, (unsigned)pwq->sched_hz); + + POLL_RLOCK(&pwq->lock); + + more = 0; + pwq->sched_pass = cc; + + /* Receive/Legacy pass. */ + SLIST_FOREACH(pif, pif_head, link) { + + if (ticks > pif->regcheck_hz) { + CTR1(KTR_SPARE2, "RECV deep polling for %s", pif->ifp->if_xname); + pif->regcheck_hz = ticks + pollif_regcheck * (hz / 1000); + arg = POLL_AND_CHECK_STATUS; + } else + arg = POLL_ONLY; + + mc = NULL; + if (pif->handler_mq == NULL) + moreif = pif->handler(pif->ifp, arg, pollif_ppr); + else + moreif = pif->handler_mq(pif->ifp, + pif->context, arg, + pollif_ppr, &mc); + more |= moreif; + if (moreif) + pif->stat.resched_rx++; + + /* Inject packets. */ + if (mc != NULL) { + while (mc != NULL) { + m = mc; + mc = m->m_nextpkt; + m->m_nextpkt = NULL; +#ifdef POLLIF_PREFETCH + if (mc != NULL) + prefetch(mtod(mc, caddr_t)); +#endif + (*pif->ifp->if_input)(pif->ifp, m); + } + } + } + + pwq->rcv_more = more; + POLL_RUNLOCK(&pwq->lock); + CTR0(KTR_SPARE2, "RECV END"); + + return (more); +} + +int +pollif_send(void) +{ + int more; + int64_t cc, ccp; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *pif; + POLL_RTRACKER; + + if (pollif_handlers == 0) + return (0); + + CTR0(KTR_SPARE2, "SEND BEGIN"); + pwq = &pollif_cpu[PCPU_GET(cpuid)]; + pif_head = &pwq->iflist; + + if (pwq->state != PIWQ_RUN) + return (0); + + KASSERT(PCPU_GET(cpuid) == pwq->cpuid, ("host thread not bound")); + + POLL_RLOCK(&pwq->lock); + more = pwq->rcv_more; + + /* MQ transmit pass: start transmit. */ + SLIST_FOREACH(pif, pif_head, link) { + if (pif->handler_mq != NULL) { + pif->handler_mq(pif->ifp, pif->context, + POLL_TX, pollif_maxcpu * pollif_ppr, NULL); + } + } + + /* Check deadline, update run stats. */ + cc = POLLIF_TIME; + ccp = cc - pwq->sched_pass; + pwq->sched_sum += ccp; + pwq->pass++; + CTR3(KTR_SPARE2, "SEND rcv_more=%d actual=%u max=%u", + more, (unsigned)pwq->sched_sum, (unsigned)pwq->sched_deadline); + if (more) { + /* Check for deadline. */ + if (pollif_deadline > 0 && pwq->sched_sum + ccp > pwq->sched_deadline) { + /* If no thread to run continue. */ + if (sched_runnable() != 0) { + CTR0(KTR_SPARE2, "SEND deadline reached state=PAUSE"); + pwq->state = PIWQ_PAUSE; + more = 0; + } else { + CTR0(KTR_SPARE2, "SEND no thread to run state=IDLE"); + pwq->state = PIWQ_IDLE; + } + } + } else { + CTR0(KTR_SPARE2, "SEND no more packet state=IDLE"); + pwq->state = PIWQ_IDLE; + } + + + POLL_RUNLOCK(&pwq->lock); + CTR0(KTR_SPARE2, "SEND END"); + + return (more); +} + +static int +ether_poll_register_(poll_handler_t *h, poll_handler_mq_t *mqh, + struct ifnet *ifp, int queue_id, int max_id, void *context) +{ + static int rrcpu = 0; + + int cpu; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *p, *pif; + + KASSERT(h != NULL || mqh != NULL, ("%s: handler is NULL", __func__)); + KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__)); + + pif = (poll_if_t *)malloc(sizeof(poll_if_t), M_TEMP, M_DONTWAIT); + if (pif == NULL) { + log(LOG_ERR, "pollif: cannot allocate poll interface structure\n"); + return ENOMEM; + } + + pif->handler = h; + pif->handler_mq = mqh; + pif->ifp = ifp; + pif->queue_id = queue_id; + pif->context = context; + pif->regcheck_hz = ticks; + reset_ifstats(&pif->stat); + + /* Placement of the interface: + * - User mapping + * - 0..pollif_maxcpu if number of queue multiple of pollif_maxcpu. + * - Round-robin + */ + cpu = map_base(ifp->if_xname, pollif_map); + if (cpu >= 0) + cpu += queue_id; + else { + if (mqh != NULL && max_id % pollif_maxcpu) + cpu = queue_id; + else + cpu = rrcpu++; + } + cpu %= pollif_maxcpu; + + pwq = &pollif_cpu[pollif_cpuid[cpu]]; + pif_head = &pwq->iflist; + POLL_WLOCK(&pwq->lock); + SLIST_FOREACH(p, pif_head, link) { + if (p->ifp == ifp && + p->queue_id == queue_id) { + if (bootverbose) + printf("pollif: ignoring duplicate register of interface %s/%d\n", + ifp->if_xname, queue_id); + POLL_WUNLOCK(&pwq->lock); + free(pif, M_TEMP); + return (EINVAL); + } + } + SLIST_INSERT_HEAD(pif_head, pif, link); + pollif_handlers++; + if (bootverbose) + printf("pollif: adding interface %s/%d to CPU%d workqueue\n", + ifp->if_xname, queue_id, pollif_cpuid[cpu]); + POLL_WUNLOCK(&pwq->lock); + + return (0); +} + +static int +ether_poll_deregister_(struct ifnet *ifp, int queue_id) +{ + int i; + struct poll_if_head *pif_head; + poll_wq_t *pwq; + poll_if_t *p, *p_t; + + KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__)); + + for (i = 0; i < pollif_maxcpu; i++) { + pwq = &pollif_cpu[pollif_cpuid[i]]; + + pif_head = &pwq->iflist; + POLL_WLOCK(&pwq->lock); + SLIST_FOREACH_SAFE(p, pif_head, link, p_t) { + if (p->ifp == ifp && + p->queue_id == queue_id) { + if (bootverbose) + printf("pollif: unregister interface %s/%d\n", + ifp->if_xname, queue_id); + SLIST_REMOVE(pif_head, p, poll_if, link); + free(p, M_TEMP); + pollif_handlers--; + POLL_WUNLOCK(&pwq->lock); + return (0); + } + } + POLL_WUNLOCK(&pwq->lock); + } + + log(LOG_ERR, "pollif: duplicate interface removal\n"); + return (EINVAL); +} + +int ether_poll_register_mq(poll_handler_mq_t *mqh, + struct ifnet *ifp, int queue_id, int max_id, void *context) +{ + + return ether_poll_register_(NULL, mqh, + ifp, queue_id, max_id, context); +} + +int +ether_poll_register(poll_handler_t *h, struct ifnet *ifp) +{ + + return ether_poll_register_(h, NULL, + ifp, 0, 1, NULL); +} + +int +ether_poll_deregister_mq(struct ifnet *ifp, int queue_id) +{ + + return ether_poll_deregister_(ifp, queue_id); +} + +int +ether_poll_deregister(struct ifnet *ifp) +{ + + return ether_poll_deregister_(ifp, 0); +} + +static void +pollif_cleanup(void) +{ + int i; + poll_wq_t *pwq; + + for (i = 0; i < pollif_maxcpu; i++) { + pwq = &pollif_cpu[pollif_cpuid[i]]; + pwq->state = PIWQ_CLEANUP; + POLL_LOCK_DESTROY(&pwq->lock); + } +} +SYSUNINIT(pollif_cleanup, SI_SUB_INIT_IF, SI_ORDER_MIDDLE, pollif_cleanup, NULL); + +static void +pollif_init(void) +{ + u_int i; + poll_wq_t *pwq; + + for (i = 0; i < MAXCPU; i++) { + pwq = &pollif_cpu[i]; + pwq->state = PIWQ_INIT; + pwq->pass = 1; + POLL_LOCK_INIT(&pwq->lock); + SLIST_INIT(&pwq->iflist); + reset_stats(&pwq->stat); + } + /* Set default sched_div. */ + if (pollif_sched_div == 0) { + pollif_sched_div = hz / 100; + if (pollif_sched_div == 0) + pollif_sched_div = 1; + } +} + +void +pollif_init_cpu(u_int cpuid) +{ + int maxcpu = MAXCPU; + poll_wq_t *pwq; + + TUNABLE_INT_FETCH("kern.pollif.maxthreads", &maxcpu); + if (maxcpu > mp_ncpus) + maxcpu = mp_ncpus; + + if (pollif_maxcpu == 0) + pollif_init(); + + if (bootverbose) + printf("pollif: creating workqueue for cpu%d\n", cpuid); + + /* Assume that CPU init order and topology match. */ + if (pollif_maxcpu < maxcpu) { + pwq = &pollif_cpu[cpuid]; + pollif_cpuid[pollif_maxcpu++] = cpuid; + pwq->cpuid = cpuid; + pwq->state = PIWQ_START; + } +}