net/txgbe: support VF multicast MAC filter
[dpdk.git] / drivers / net / txgbe / base / txgbe_hw.c
index 7a6c309..3cee8b8 100644 (file)
@@ -6,6 +6,7 @@
 #include "txgbe_mbx.h"
 #include "txgbe_phy.h"
 #include "txgbe_dcb.h"
+#include "txgbe_vf.h"
 #include "txgbe_eeprom.h"
 #include "txgbe_mng.h"
 #include "txgbe_hw.h"
@@ -560,6 +561,52 @@ s32 txgbe_stop_hw(struct txgbe_hw *hw)
        return 0;
 }
 
+/**
+ *  txgbe_led_on - Turns on the software controllable LEDs.
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn on
+ **/
+s32 txgbe_led_on(struct txgbe_hw *hw, u32 index)
+{
+       u32 led_reg = rd32(hw, TXGBE_LEDCTL);
+
+       DEBUGFUNC("txgbe_led_on");
+
+       if (index > 4)
+               return TXGBE_ERR_PARAM;
+
+       /* To turn on the LED, set mode to ON. */
+       led_reg |= TXGBE_LEDCTL_SEL(index);
+       led_reg |= TXGBE_LEDCTL_ORD(index);
+       wr32(hw, TXGBE_LEDCTL, led_reg);
+       txgbe_flush(hw);
+
+       return 0;
+}
+
+/**
+ *  txgbe_led_off - Turns off the software controllable LEDs.
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn off
+ **/
+s32 txgbe_led_off(struct txgbe_hw *hw, u32 index)
+{
+       u32 led_reg = rd32(hw, TXGBE_LEDCTL);
+
+       DEBUGFUNC("txgbe_led_off");
+
+       if (index > 4)
+               return TXGBE_ERR_PARAM;
+
+       /* To turn off the LED, set mode to OFF. */
+       led_reg &= ~(TXGBE_LEDCTL_SEL(index));
+       led_reg &= ~(TXGBE_LEDCTL_ORD(index));
+       wr32(hw, TXGBE_LEDCTL, led_reg);
+       txgbe_flush(hw);
+
+       return 0;
+}
+
 /**
  *  txgbe_validate_mac_addr - Validate MAC address
  *  @mac_addr: pointer to MAC address.
@@ -1016,6 +1063,205 @@ out:
        return err;
 }
 
+/**
+ *  txgbe_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
+ **/
+s32 txgbe_negotiate_fc(struct txgbe_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))) {
+               DEBUGOUT("Local or link partner's advertised flow control "
+                             "settings are NULL. Local: %x, link partner: %x\n",
+                             adv_reg, lp_reg);
+               return TXGBE_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 == txgbe_fc_full) {
+                       hw->fc.current_mode = txgbe_fc_full;
+                       DEBUGOUT("Flow Control = FULL.\n");
+               } else {
+                       hw->fc.current_mode = txgbe_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 = txgbe_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 = txgbe_fc_rx_pause;
+               DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
+       } else {
+               hw->fc.current_mode = txgbe_fc_none;
+               DEBUGOUT("Flow Control = NONE.\n");
+       }
+       return 0;
+}
+
+/**
+ *  txgbe_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 txgbe_fc_autoneg_fiber(struct txgbe_hw *hw)
+{
+       u32 pcs_anadv_reg, pcs_lpab_reg;
+       s32 err = TXGBE_ERR_FC_NOT_NEGOTIATED;
+
+       /*
+        * 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
+        */
+
+       pcs_anadv_reg = rd32_epcs(hw, SR_MII_MMD_AN_ADV);
+       pcs_lpab_reg = rd32_epcs(hw, SR_MII_MMD_LP_BABL);
+
+       err =  txgbe_negotiate_fc(hw, pcs_anadv_reg,
+                                     pcs_lpab_reg,
+                                     SR_MII_MMD_AN_ADV_PAUSE_SYM,
+                                     SR_MII_MMD_AN_ADV_PAUSE_ASM,
+                                     SR_MII_MMD_AN_ADV_PAUSE_SYM,
+                                     SR_MII_MMD_AN_ADV_PAUSE_ASM);
+
+       return err;
+}
+
+/**
+ *  txgbe_fc_autoneg_backplane - Enable flow control IEEE clause 37
+ *  @hw: pointer to hardware structure
+ *
+ *  Enable flow control according to IEEE clause 37.
+ **/
+STATIC s32 txgbe_fc_autoneg_backplane(struct txgbe_hw *hw)
+{
+       u32 anlp1_reg, autoc_reg;
+       s32 err = TXGBE_ERR_FC_NOT_NEGOTIATED;
+
+       /*
+        * Read the 10g AN autoc and LP ability registers and resolve
+        * local flow control settings accordingly
+        */
+       autoc_reg = rd32_epcs(hw, SR_AN_MMD_ADV_REG1);
+       anlp1_reg = rd32_epcs(hw, SR_AN_MMD_LP_ABL1);
+
+       err = txgbe_negotiate_fc(hw, autoc_reg,
+               anlp1_reg,
+               SR_AN_MMD_ADV_REG1_PAUSE_SYM,
+               SR_AN_MMD_ADV_REG1_PAUSE_ASM,
+               SR_AN_MMD_ADV_REG1_PAUSE_SYM,
+               SR_AN_MMD_ADV_REG1_PAUSE_ASM);
+
+       return err;
+}
+
+/**
+ *  txgbe_fc_autoneg_copper - Enable flow control IEEE clause 37
+ *  @hw: pointer to hardware structure
+ *
+ *  Enable flow control according to IEEE clause 37.
+ **/
+STATIC s32 txgbe_fc_autoneg_copper(struct txgbe_hw *hw)
+{
+       u16 technology_ability_reg = 0;
+       u16 lp_technology_ability_reg = 0;
+
+       hw->phy.read_reg(hw, TXGBE_MD_AUTO_NEG_ADVT,
+                            TXGBE_MD_DEV_AUTO_NEG,
+                            &technology_ability_reg);
+       hw->phy.read_reg(hw, TXGBE_MD_AUTO_NEG_LP,
+                            TXGBE_MD_DEV_AUTO_NEG,
+                            &lp_technology_ability_reg);
+
+       return txgbe_negotiate_fc(hw, (u32)technology_ability_reg,
+                                 (u32)lp_technology_ability_reg,
+                                 TXGBE_TAF_SYM_PAUSE, TXGBE_TAF_ASM_PAUSE,
+                                 TXGBE_TAF_SYM_PAUSE, TXGBE_TAF_ASM_PAUSE);
+}
+
+/**
+ *  txgbe_fc_autoneg - Configure flow control
+ *  @hw: pointer to hardware structure
+ *
+ *  Compares our advertised flow control capabilities to those advertised by
+ *  our link partner, and determines the proper flow control mode to use.
+ **/
+void txgbe_fc_autoneg(struct txgbe_hw *hw)
+{
+       s32 err = TXGBE_ERR_FC_NOT_NEGOTIATED;
+       u32 speed;
+       bool link_up;
+
+       DEBUGFUNC("txgbe_fc_autoneg");
+
+       /*
+        * 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.disable_fc_autoneg) {
+               DEBUGOUT("Flow control autoneg is disabled");
+               goto out;
+       }
+
+       hw->mac.check_link(hw, &speed, &link_up, false);
+       if (!link_up) {
+               DEBUGOUT("The link is down");
+               goto out;
+       }
+
+       switch (hw->phy.media_type) {
+       /* Autoneg flow control on fiber adapters */
+       case txgbe_media_type_fiber_qsfp:
+       case txgbe_media_type_fiber:
+               if (speed == TXGBE_LINK_SPEED_1GB_FULL)
+                       err = txgbe_fc_autoneg_fiber(hw);
+               break;
+
+       /* Autoneg flow control on backplane adapters */
+       case txgbe_media_type_backplane:
+               err = txgbe_fc_autoneg_backplane(hw);
+               break;
+
+       /* Autoneg flow control on copper adapters */
+       case txgbe_media_type_copper:
+               if (txgbe_device_supports_autoneg_fc(hw))
+                       err = txgbe_fc_autoneg_copper(hw);
+               break;
+
+       default:
+               break;
+       }
+
+out:
+       if (err == 0) {
+               hw->fc.fc_was_autonegged = true;
+       } else {
+               hw->fc.fc_was_autonegged = false;
+               hw->fc.current_mode = hw->fc.requested_mode;
+       }
+}
+
 /**
  *  txgbe_acquire_swfw_sync - Acquire SWFW semaphore
  *  @hw: pointer to hardware structure
@@ -2246,6 +2492,9 @@ s32 txgbe_init_shared_code(struct txgbe_hw *hw)
        case txgbe_mac_raptor:
                status = txgbe_init_ops_pf(hw);
                break;
+       case txgbe_mac_raptor_vf:
+               status = txgbe_init_ops_vf(hw);
+               break;
        default:
                status = TXGBE_ERR_DEVICE_NOT_SUPPORTED;
                break;
@@ -2541,8 +2790,12 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
        phy->setup_link_speed = txgbe_setup_phy_link_speed;
        phy->read_i2c_byte = txgbe_read_i2c_byte;
        phy->write_i2c_byte = txgbe_write_i2c_byte;
+       phy->read_i2c_sff8472 = txgbe_read_i2c_sff8472;
        phy->read_i2c_eeprom = txgbe_read_i2c_eeprom;
        phy->write_i2c_eeprom = txgbe_write_i2c_eeprom;
+       phy->identify_sfp = txgbe_identify_module;
+       phy->read_i2c_byte_unlocked = txgbe_read_i2c_byte_unlocked;
+       phy->write_i2c_byte_unlocked = txgbe_write_i2c_byte_unlocked;
        phy->reset = txgbe_reset_phy;
 
        /* MAC */
@@ -2555,6 +2808,7 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
        mac->acquire_swfw_sync = txgbe_acquire_swfw_sync;
        mac->release_swfw_sync = txgbe_release_swfw_sync;
        mac->reset_hw = txgbe_reset_hw;
+       mac->update_mc_addr_list = txgbe_update_mc_addr_list;
 
        mac->disable_sec_rx_path = txgbe_disable_sec_rx_path;
        mac->enable_sec_rx_path = txgbe_enable_sec_rx_path;
@@ -2588,6 +2842,7 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
        /* Flow Control */
        mac->fc_enable = txgbe_fc_enable;
        mac->setup_fc = txgbe_setup_fc;
+       mac->fc_autoneg = txgbe_fc_autoneg;
 
        /* Link */
        mac->get_link_capabilities = txgbe_get_link_capabilities_raptor;
@@ -3399,6 +3654,93 @@ mac_reset_top:
        return status;
 }
 
+/**
+ * txgbe_fdir_check_cmd_complete - poll to check whether FDIRPICMD is complete
+ * @hw: pointer to hardware structure
+ * @fdircmd: current value of FDIRCMD register
+ */
+static s32 txgbe_fdir_check_cmd_complete(struct txgbe_hw *hw, u32 *fdircmd)
+{
+       int i;
+
+       for (i = 0; i < TXGBE_FDIRCMD_CMD_POLL; i++) {
+               *fdircmd = rd32(hw, TXGBE_FDIRPICMD);
+               if (!(*fdircmd & TXGBE_FDIRPICMD_OP_MASK))
+                       return 0;
+               usec_delay(10);
+       }
+
+       return TXGBE_ERR_FDIR_CMD_INCOMPLETE;
+}
+
+/**
+ *  txgbe_reinit_fdir_tables - Reinitialize Flow Director tables.
+ *  @hw: pointer to hardware structure
+ **/
+s32 txgbe_reinit_fdir_tables(struct txgbe_hw *hw)
+{
+       s32 err;
+       int i;
+       u32 fdirctrl = rd32(hw, TXGBE_FDIRCTL);
+       u32 fdircmd;
+       fdirctrl &= ~TXGBE_FDIRCTL_INITDONE;
+
+       DEBUGFUNC("txgbe_reinit_fdir_tables");
+
+       /*
+        * Before starting reinitialization process,
+        * FDIRPICMD.OP must be zero.
+        */
+       err = txgbe_fdir_check_cmd_complete(hw, &fdircmd);
+       if (err) {
+               DEBUGOUT("Flow Director previous command did not complete, aborting table re-initialization.\n");
+               return err;
+       }
+
+       wr32(hw, TXGBE_FDIRFREE, 0);
+       txgbe_flush(hw);
+       /*
+        * adapters flow director init flow cannot be restarted,
+        * Workaround silicon errata by performing the following steps
+        * before re-writing the FDIRCTL control register with the same value.
+        * - write 1 to bit 8 of FDIRPICMD register &
+        * - write 0 to bit 8 of FDIRPICMD register
+        */
+       wr32m(hw, TXGBE_FDIRPICMD, TXGBE_FDIRPICMD_CLR, TXGBE_FDIRPICMD_CLR);
+       txgbe_flush(hw);
+       wr32m(hw, TXGBE_FDIRPICMD, TXGBE_FDIRPICMD_CLR, 0);
+       txgbe_flush(hw);
+       /*
+        * Clear FDIR Hash register to clear any leftover hashes
+        * waiting to be programmed.
+        */
+       wr32(hw, TXGBE_FDIRPIHASH, 0x00);
+       txgbe_flush(hw);
+
+       wr32(hw, TXGBE_FDIRCTL, fdirctrl);
+       txgbe_flush(hw);
+
+       /* Poll init-done after we write FDIRCTL register */
+       for (i = 0; i < TXGBE_FDIR_INIT_DONE_POLL; i++) {
+               if (rd32m(hw, TXGBE_FDIRCTL, TXGBE_FDIRCTL_INITDONE))
+                       break;
+               msec_delay(1);
+       }
+       if (i >= TXGBE_FDIR_INIT_DONE_POLL) {
+               DEBUGOUT("Flow Director Signature poll time exceeded!\n");
+               return TXGBE_ERR_FDIR_REINIT_FAILED;
+       }
+
+       /* Clear FDIR statistics registers (read to clear) */
+       rd32(hw, TXGBE_FDIRUSED);
+       rd32(hw, TXGBE_FDIRFAIL);
+       rd32(hw, TXGBE_FDIRMATCH);
+       rd32(hw, TXGBE_FDIRMISS);
+       rd32(hw, TXGBE_FDIRLEN);
+
+       return 0;
+}
+
 /**
  *  txgbe_start_hw_raptor - Prepare hardware for Tx/Rx
  *  @hw: pointer to hardware structure