diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c index 74ccfb7f629f..bbce4012ca53 100644 --- a/sys/dev/ixgbe/if_ix.c +++ b/sys/dev/ixgbe/if_ix.c @@ -307,6 +307,9 @@ static u32 ixgbe_handle_fw_event(void *); static int ixgbe_enable_lse(struct ixgbe_softc *sc); static int ixgbe_disable_lse(struct ixgbe_softc *sc); +static void ixgbe_cache_ldo_policy(struct ixgbe_softc *sc); +static void ixgbe_apply_ldo_link_state(struct ixgbe_softc *sc, bool link_up); + /************************************************************************ * FreeBSD Device Interface Entry Points ************************************************************************/ @@ -361,6 +364,62 @@ ixgbe_device_iov_init(device_t dev, uint16_t num_vfs, } #endif +/************************************************************************ + * ixgbe_cache_ldo_policy + ************************************************************************/ +static void +ixgbe_cache_ldo_policy(struct ixgbe_softc *sc) +{ + struct ixgbe_link_default_override_tlv ldo = {0}; + struct ixgbe_hw *hw = &sc->hw; + s32 error; + + sc->ldo_valid = false; + sc->ldo_port_disabled = false; + + error = ixgbe_get_link_default_override(hw, &ldo); + if (error) { + device_printf(sc->dev, + "Failed to read E610 Link Default Override TLV, error: %d\n", + error); + return; + } + + sc->ldo_valid = true; + sc->ldo_port_disabled = + !!(ldo.link_options & IXGBE_LINK_OVERRIDE_PORT_DIS); +} /* ixgbe_cache_ldo_policy */ + +/************************************************************************ + * ixgbe_apply_ldo_link_state + ************************************************************************/ +static void +ixgbe_apply_ldo_link_state(struct ixgbe_softc *sc, bool link_up) +{ + struct ixgbe_hw *hw = &sc->hw; + s32 error; + + if (!sc->ldo_valid || !sc->ldo_port_disabled) + return; + + error = ixgbe_force_link_state(hw, link_up); + if (!error) + return; + + if (!link_up && error == IXGBE_ERR_ACI_ERROR && + hw->aci.last_status == IXGBE_ACI_RC_EMODE) { + device_printf(sc->dev, + "Firmware vetoed link shutdown, " + "last ACI status: %d\n", hw->aci.last_status); + return; + } + + device_printf(sc->dev, + "Failed to force link %s, error: %d, " + "last aci status: %d\n", link_up ? "up" : "down", error, + hw->aci.last_status); +} /* ixgbe_apply_ldo_link_state */ + static device_method_t ixgbe_if_methods[] = { DEVMETHOD(ifdi_attach_pre, ixgbe_if_attach_pre), DEVMETHOD(ifdi_attach_post, ixgbe_if_attach_post), @@ -1175,6 +1234,7 @@ ixgbe_if_attach_pre(if_ctx_t ctx) hw->phy.smart_speed = ixgbe_smart_speed; ixgbe_init_device_features(sc); + ixgbe_cache_ldo_policy(sc); /* Verify adapter fan is still functional (if applicable) */ if (sc->feat_en & IXGBE_FEATURE_FAN_FAIL) { @@ -2061,9 +2121,14 @@ ixgbe_config_link(if_ctx_t ctx) "error: %d", err); } - if (hw->mac.ops.setup_link) + if (hw->mac.ops.setup_link) { err = hw->mac.ops.setup_link(hw, autoneg, sc->link_up); + if (err) + return; + } + + ixgbe_apply_ldo_link_state(sc, true); } } /* ixgbe_config_link */ @@ -3730,6 +3795,20 @@ ixgbe_add_debug_sysctls(struct ixgbe_softc *sc) if (sc->feat_en & IXGBE_FEATURE_FW_LOGGING) ixgbe_add_fw_logging_tunables(sc, sc->debug_sysctls); + + if (sc->hw.mac.type == ixgbe_mac_E610) { + struct sysctl_oid_list *debug_list; + debug_list = SYSCTL_CHILDREN(sc->debug_sysctls); + + SYSCTL_ADD_BOOL(ctx, debug_list, OID_AUTO, "ldo_valid", + CTLFLAG_RD, &sc->ldo_valid, 0, + "E610 Link Default Override policy valid"); + + SYSCTL_ADD_BOOL(ctx, debug_list, OID_AUTO, + "ldo_port_disabled", CTLFLAG_RD, &sc->ldo_port_disabled, + 0, "E610 Link Default Override PORT_DIS set"); + } + } /* ixgbe_add_debug_sysctls */ /************************************************************************ @@ -5120,6 +5199,8 @@ ixgbe_if_stop(if_ctx_t ctx) iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); sc->link_active = false; } + ixgbe_if_update_admin_status(ctx); + ixgbe_apply_ldo_link_state(sc, false); /* reprogram the RAR[0] in case user changed it. */ ixgbe_set_rar(&sc->hw, 0, sc->hw.mac.addr, 0, IXGBE_RAH_AV); diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h index 28d9adeeb7a5..41beaeaec7d7 100644 --- a/sys/dev/ixgbe/ixgbe.h +++ b/sys/dev/ixgbe/ixgbe.h @@ -443,6 +443,8 @@ struct ixgbe_softc { int advertise; /* link speeds */ int enable_aim; /* adaptive interrupt moderation */ bool link_active; + bool ldo_valid; + bool ldo_port_disabled; u16 num_segs; u32 link_speed; bool link_up; diff --git a/sys/dev/ixgbe/ixgbe_e610.c b/sys/dev/ixgbe/ixgbe_e610.c index 058c79928cd3..507dc7d16920 100644 --- a/sys/dev/ixgbe/ixgbe_e610.c +++ b/sys/dev/ixgbe/ixgbe_e610.c @@ -1643,6 +1643,38 @@ s32 ixgbe_aci_get_link_info(struct ixgbe_hw *hw, bool ena_lse, return IXGBE_SUCCESS; } +/** + * ixgbe_force_link_state - force phy link to get up/down + * @hw: pointer to hardware structure + * @link_up: if true: force link up, if false: force link down + * + * Send 0x0601 with the IXGBE_ACI_PHY_ENA_LINK bit set/cleared. + * + * Return: the exit code of the operation. + */ +s32 ixgbe_force_link_state(struct ixgbe_hw *hw, bool link_up) +{ + struct ixgbe_aci_cmd_set_phy_cfg_data pcfg = {0}; + struct ixgbe_aci_cmd_get_phy_caps_data pcaps; + s32 err; + + err = ixgbe_aci_get_phy_caps(hw, false, IXGBE_ACI_REPORT_ACTIVE_CFG, + &pcaps); + if (err) { + return err; + } + + ixgbe_copy_phy_caps_to_cfg(&pcaps, &pcfg); + + if (link_up) + pcfg.caps |= IXGBE_ACI_PHY_ENA_LINK; + else + pcfg.caps &= ~IXGBE_ACI_PHY_ENA_LINK; + pcfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT; + + return ixgbe_aci_set_phy_cfg(hw, &pcfg); +} + /** * ixgbe_aci_set_event_mask - set event mask * @hw: pointer to the HW struct @@ -5973,6 +6005,53 @@ static s32 ixgbe_get_pfa_module_tlv(struct ixgbe_hw *hw, u16 *module_tlv, return IXGBE_ERR_DOES_NOT_EXIST; } +/** + * ixgbe_get_link_default_override - Get link default override from NVM + * @hw: pointer to hardware structure + * @ldo: pointer to link default override struct to populate + * + * Read the Link Default Override TLV (0x134) from the NVM PFA and return + * the full per-port override configuration. The caller must consume only + * IXGBE_LINK_OVERRIDE_PORT_DIS from link_options for force-link policy. + * All port data is read in a single bulk NVM access. + * + * Return: the exit code of the operation. + */ +s32 ixgbe_get_link_default_override(struct ixgbe_hw *hw, + struct ixgbe_link_default_override_tlv *ldo) +{ + u16 min_tlv_len; + u16 tlv, tlv_len, tlv_start; + s32 status; + + DEBUGFUNC("ixgbe_get_link_default_override"); + + if (!ldo) + return IXGBE_ERR_PARAM; + + status = ixgbe_get_pfa_module_tlv(hw, &tlv, &tlv_len, + E610_SR_LINK_DEFAULT_OVERRIDE_PTR); + if (status) + return status; + + /* tlv_len covers value words only, don't add header offset. */ + min_tlv_len = (hw->bus.func + 1) * E610_SR_PFA_LINK_OVERRIDE_WORDS; + if (tlv_len < min_tlv_len) + return IXGBE_ERR_INVAL_SIZE; + + tlv_start = tlv + E610_SR_PFA_LINK_OVERRIDE_OFFSET + + hw->bus.func * E610_SR_PFA_LINK_OVERRIDE_WORDS; + + /* Read all port-specific words in a single NVM access */ + status = ixgbe_read_ee_aci_buffer_E610(hw, tlv_start, + E610_SR_PFA_LINK_OVERRIDE_WORDS, + (u16 *)ldo); + if (status) + return status; + + return IXGBE_SUCCESS; +} + /** * ixgbe_read_pba_string_E610 - Reads part number string from NVM * @hw: pointer to hardware structure diff --git a/sys/dev/ixgbe/ixgbe_e610.h b/sys/dev/ixgbe/ixgbe_e610.h index 7af5506d85e8..a9e496098e52 100644 --- a/sys/dev/ixgbe/ixgbe_e610.h +++ b/sys/dev/ixgbe/ixgbe_e610.h @@ -75,6 +75,7 @@ s32 ixgbe_update_link_info(struct ixgbe_hw *hw); s32 ixgbe_get_link_status(struct ixgbe_hw *hw, bool *link_up); s32 ixgbe_aci_get_link_info(struct ixgbe_hw *hw, bool ena_lse, struct ixgbe_link_status *link); +s32 ixgbe_force_link_state(struct ixgbe_hw *hw, bool link_up); s32 ixgbe_aci_set_event_mask(struct ixgbe_hw *hw, u8 port_num, u16 mask); s32 ixgbe_configure_lse(struct ixgbe_hw *hw, bool activate, u16 mask); @@ -232,6 +233,9 @@ s32 ixgbe_write_ee_aci_buffer_E610(struct ixgbe_hw *hw, u16 offset, s32 ixgbe_calc_eeprom_checksum_E610(struct ixgbe_hw *hw); s32 ixgbe_update_eeprom_checksum_E610(struct ixgbe_hw *hw); s32 ixgbe_validate_eeprom_checksum_E610(struct ixgbe_hw *hw, u16 *checksum_val); +/* Reads LDO TLV 0x134; callers consume only IXGBE_LINK_OVERRIDE_PORT_DIS. */ +s32 ixgbe_get_link_default_override(struct ixgbe_hw *hw, + struct ixgbe_link_default_override_tlv *ldo); s32 ixgbe_read_pba_string_E610(struct ixgbe_hw *hw, u8 *pba_num, u32 pba_num_size); #endif /* _IXGBE_E610_H_ */ diff --git a/sys/dev/ixgbe/ixgbe_type_e610.h b/sys/dev/ixgbe/ixgbe_type_e610.h index da46e503f660..005fec423a20 100644 --- a/sys/dev/ixgbe/ixgbe_type_e610.h +++ b/sys/dev/ixgbe/ixgbe_type_e610.h @@ -140,6 +140,23 @@ #define E610_SR_NVM_CTRL_WORD 0x00 #define E610_SR_PBA_BLOCK_PTR 0x16 +/* Link Default Override TLV in PFA */ +#define E610_SR_LINK_DEFAULT_OVERRIDE_PTR 0x134 +#define E610_SR_PFA_LINK_OVERRIDE_WORDS 10 +#define E610_SR_PFA_LINK_OVERRIDE_PHY_WORDS 8 +#define E610_SR_PFA_LINK_OVERRIDE_OFFSET 2 + +/* Link Options bits */ +#define IXGBE_LINK_OVERRIDE_PORT_DIS BIT(2) + +#pragma pack(1) +struct ixgbe_link_default_override_tlv { + u16 link_options; /* Port Options 0: only PORT_DIS is consumed */ + u16 phy_options; /* Port Options 1 */ + u16 phy_types[E610_SR_PFA_LINK_OVERRIDE_PHY_WORDS]; /* words 2-9 */ +}; +#pragma pack() + /* The Orom version topology */ #define IXGBE_OROM_VER_PATCH_SHIFT 0 #define IXGBE_OROM_VER_PATCH_MASK (0xff << IXGBE_OROM_VER_PATCH_SHIFT)