From 6feac2e3ccf4b4d8651a254d516ae86b1b1b3e4f Mon Sep 17 00:00:00 2001 From: Rahul Lakkireddy Date: Tue, 7 Dec 2021 02:48:02 +0530 Subject: 14290 cxgbe: need link speed change when autoneg is disabled Reviewed by: Jason King Reviewed by: Ryan Goodfellow Approved by: Richard Lowe --- usr/src/uts/common/io/cxgbe/common/common.h | 37 +- usr/src/uts/common/io/cxgbe/common/t4_hw.c | 1040 ++++++++++---------- .../uts/common/io/cxgbe/firmware/t4fw_interface.h | 2 +- usr/src/uts/common/io/cxgbe/t4nex/adapter.h | 68 +- usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c | 593 ++++++----- usr/src/uts/common/io/cxgbe/t4nex/t4_nexus.c | 33 +- 6 files changed, 956 insertions(+), 817 deletions(-) diff --git a/usr/src/uts/common/io/cxgbe/common/common.h b/usr/src/uts/common/io/cxgbe/common/common.h index b8d77ebda3..76c7778f45 100644 --- a/usr/src/uts/common/io/cxgbe/common/common.h +++ b/usr/src/uts/common/io/cxgbe/common/common.h @@ -110,6 +110,7 @@ enum { FEC_RS = 1 << 0, /* Reed-Solomon */ FEC_BASER_RS = 1 << 1, /* Base-R, aka Firecode */ FEC_NONE = 1 << 2, /* no FEC */ + FEC_FORCE = 1 << 3, /* Force specified FEC */ /* * Pseudo FECs that translate to real FECs. The firmware knows nothing @@ -507,20 +508,11 @@ enum fw_caps { struct link_config { fw_port_cap32_t pcaps; /* link capabilities */ - fw_port_cap32_t def_acaps; /* default advertised capabilities */ fw_port_cap32_t acaps; /* advertised capabilities */ fw_port_cap32_t lpacaps; /* peer advertised capabilities */ - fw_port_cap32_t speed_caps; /* speed(s) user has requested */ - u32 speed; /* actual link speed (Mb/s) */ - - cc_pause_t requested_fc; /* flow control user has requested */ - cc_pause_t fc; /* actual link flow control */ - - cc_fec_t requested_fec; /* Forward Error Correction: */ - cc_fec_t fec; /* requested and actual in use */ - - unsigned char autoneg; /* autonegotiating? */ + fw_port_cap32_t link_caps; /* current link capabilities */ + fw_port_cap32_t admin_caps; /* admin configured link capabilities */ unsigned char link_ok; /* link up? */ unsigned char link_down_rc; /* link down reason */ @@ -595,21 +587,30 @@ int t4_slow_intr_handler(struct adapter *adapter); int t4_hash_mac_addr(const u8 *addr); -fw_port_cap32_t t4_link_acaps(struct adapter *adapter, unsigned int port, - struct link_config *lc); +unsigned int t4_link_fwcap_to_speed(fw_port_cap32_t caps); +int t4_link_set_autoneg(struct port_info *pi, u8 autoneg, + fw_port_cap32_t *new_caps); +int t4_link_set_pause(struct port_info *pi, cc_pause_t pause, + fw_port_cap32_t *new_caps); +int t4_link_set_fec(struct port_info *pi, cc_fec_t fec, + fw_port_cap32_t *new_caps); +int t4_link_set_speed(struct port_info *pi, fw_port_cap32_t speed, u8 en, + fw_port_cap32_t *new_caps); int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, unsigned int port, struct link_config *lc, - bool sleep_ok, int timeout); + fw_port_cap32_t rcap, bool sleep_ok, int timeout); static inline int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox, - unsigned int port, struct link_config *lc) + unsigned int port, struct link_config *lc, + fw_port_cap32_t rcap) { - return t4_link_l1cfg_core(adapter, mbox, port, lc, + return t4_link_l1cfg_core(adapter, mbox, port, lc, rcap, true, FW_CMD_MAX_TIMEOUT); } static inline int t4_link_l1cfg_ns(struct adapter *adapter, unsigned int mbox, - unsigned int port, struct link_config *lc) + unsigned int port, struct link_config *lc, + fw_port_cap32_t rcap) { - return t4_link_l1cfg_core(adapter, mbox, port, lc, + return t4_link_l1cfg_core(adapter, mbox, port, lc, rcap, false, FW_CMD_MAX_TIMEOUT); } diff --git a/usr/src/uts/common/io/cxgbe/common/t4_hw.c b/usr/src/uts/common/io/cxgbe/common/t4_hw.c index 9a877521fc..f83131720e 100644 --- a/usr/src/uts/common/io/cxgbe/common/t4_hw.c +++ b/usr/src/uts/common/io/cxgbe/common/t4_hw.c @@ -221,17 +221,6 @@ static void fw_asrt(struct adapter *adap, struct fw_debug_cmd *asrt) #define X_CIM_PF_NOACCESS 0xeeeeeeee -/* - * If the Host OS Driver needs locking arround accesses to the mailbox, this - * can be turned on via the T4_OS_NEEDS_MBOX_LOCKING CPP define ... - */ -/* makes single-statement usage a bit cleaner ... */ -#ifdef T4_OS_NEEDS_MBOX_LOCKING -#define T4_OS_MBOX_LOCKING(x) x -#else -#define T4_OS_MBOX_LOCKING(x) do {} while (0) -#endif - /* * If the OS Driver wants busy waits to keep a watchdog happy, tap it during * busy loops which don't sleep. @@ -351,9 +340,9 @@ void t4_record_mbox_marker(struct adapter *adapter, int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, int size, void *rpl, bool sleep_ok, int timeout) { -#ifdef T4_OS_NEEDS_MBOX_LOCKING +#ifdef T4_OS_LOG_MBOX_CMDS u16 access = 0; -#endif +#endif /* T4_OS_LOG_MBOX_CMDS */ u32 v; u64 res; int i, ret; @@ -362,7 +351,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, u32 ctl_reg = PF_REG(mbox, A_CIM_PF_MAILBOX_CTRL); u32 ctl; __be64 cmd_rpl[MBOX_LEN/8]; - T4_OS_MBOX_LOCKING(t4_os_list_t entry); + struct t4_mbox_list entry; u32 pcie_fw; if ((size & 15) || size > MBOX_LEN) @@ -376,14 +365,13 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, timeout = -timeout; } -#ifdef T4_OS_NEEDS_MBOX_LOCKING /* * Queue ourselves onto the mailbox access list. When our entry is at * the front of the list, we have rights to access the mailbox. So we * wait [for a while] till we're at the front [or bail out with an * EBUSY] ... */ - t4_os_atomic_add_tail(&entry, &adap->mbox_list, &adap->mbox_lock); + t4_mbox_list_add(adap, &entry); for (i = 0; ; i++) { /* @@ -395,7 +383,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, */ pcie_fw = t4_read_reg(adap, A_PCIE_FW); if (i > 4*timeout || (pcie_fw & F_PCIE_FW_ERR)) { - t4_os_atomic_list_del(&entry, &adap->mbox_lock); + t4_mbox_list_del(adap, &entry); t4_report_fw_error(adap); ret = (pcie_fw & F_PCIE_FW_ERR) ? -ENXIO : -EBUSY; T4_RECORD_MBOX(adap, cmd, size, ret, 0); @@ -406,7 +394,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, * If we're at the head, break out and start the mailbox * protocol. */ - if (t4_os_list_first_entry(&adap->mbox_list) == &entry) + if (t4_mbox_list_first_entry(adap) == &entry) break; /* @@ -419,8 +407,9 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, udelay(MBOX_CMD_DELAY); } } +#ifdef T4_OS_LOG_MBOX_CMDS access = i; -#endif /* T4_OS_NEEDS_MBOX_LOCKING */ +#endif /* T4_OS_LOG_MBOX_CMDS */ /* * Attempt to gain access to the mailbox. @@ -437,8 +426,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, * mailbox atomic access list and report the error to our caller. */ if (v != X_MBOWNER_PL) { - T4_OS_MBOX_LOCKING(t4_os_atomic_list_del(&entry, - &adap->mbox_lock)); + t4_mbox_list_del(adap, &entry); t4_report_fw_error(adap); ret = (v == X_MBOWNER_FW) ? -EBUSY : -ETIMEDOUT; T4_RECORD_MBOX(adap, cmd, size, access, ret); @@ -511,8 +499,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, */ get_mbox_rpl(adap, cmd_rpl, size/8, data_reg); t4_write_reg(adap, ctl_reg, V_MBOWNER(X_MBOWNER_NONE)); - T4_OS_MBOX_LOCKING(t4_os_atomic_list_del(&entry, - &adap->mbox_lock)); + t4_mbox_list_del(adap, &entry); T4_RECORD_MBOX(adap, cmd_rpl, size, access, i + 1); @@ -540,11 +527,11 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, * the error and also check to see if the firmware reported any * errors ... */ - T4_OS_MBOX_LOCKING(t4_os_atomic_list_del(&entry, &adap->mbox_lock)); + t4_mbox_list_del(adap, &entry); ret = (pcie_fw & F_PCIE_FW_ERR) ? -ENXIO : -ETIMEDOUT; T4_RECORD_MBOX(adap, cmd, size, access, ret); - CH_ERR(adap, "command %#x in mailbox %d timed out\n", + CH_ERR(adap, "command 0x%x in mailbox %d timed out\n", *(const u8 *)cmd, mbox); t4_report_fw_error(adap); @@ -4519,364 +4506,6 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) } } -/* The ADVERT_MASK is used to mask out all of the Advertised Firmware Port - * Capabilities which we control with separate controls -- see, for instance, - * Pause Frames and Forward Error Correction. In order to determine what the - * full set of Advertised Port Capabilities are, the base Advertised Port - * Capabilities (masked by ADVERT_MASK) must be combined with the Advertised - * Port Capabilities associated with those other controls. See - * t4_link_acaps() for how this is done. - */ -#define ADVERT_MASK (V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED) | \ - FW_PORT_CAP32_ANEG) - -/** - * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits - * @caps16: a 16-bit Port Capabilities value - * - * Returns the equivalent 32-bit Port Capabilities value. - */ -static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) -{ - fw_port_cap32_t caps32 = 0; - - #define CAP16_TO_CAP32(__cap) \ - do { \ - if (caps16 & FW_PORT_CAP_##__cap) \ - caps32 |= FW_PORT_CAP32_##__cap; \ - } while (0) - - CAP16_TO_CAP32(SPEED_100M); - CAP16_TO_CAP32(SPEED_1G); - CAP16_TO_CAP32(SPEED_25G); - CAP16_TO_CAP32(SPEED_10G); - CAP16_TO_CAP32(SPEED_40G); - CAP16_TO_CAP32(SPEED_100G); - CAP16_TO_CAP32(FC_RX); - CAP16_TO_CAP32(FC_TX); - CAP16_TO_CAP32(ANEG); - CAP16_TO_CAP32(FORCE_PAUSE); - CAP16_TO_CAP32(MDIAUTO); - CAP16_TO_CAP32(MDISTRAIGHT); - CAP16_TO_CAP32(FEC_RS); - CAP16_TO_CAP32(FEC_BASER_RS); - CAP16_TO_CAP32(802_3_PAUSE); - CAP16_TO_CAP32(802_3_ASM_DIR); - - #undef CAP16_TO_CAP32 - - return caps32; -} - -/** - * fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits - * @caps32: a 32-bit Port Capabilities value - * - * Returns the equivalent 16-bit Port Capabilities value. Note that - * not all 32-bit Port Capabilities can be represented in the 16-bit - * Port Capabilities and some fields/values may not make it. - */ -static fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32) -{ - fw_port_cap16_t caps16 = 0; - - #define CAP32_TO_CAP16(__cap) \ - do { \ - if (caps32 & FW_PORT_CAP32_##__cap) \ - caps16 |= FW_PORT_CAP_##__cap; \ - } while (0) - - CAP32_TO_CAP16(SPEED_100M); - CAP32_TO_CAP16(SPEED_1G); - CAP32_TO_CAP16(SPEED_10G); - CAP32_TO_CAP16(SPEED_25G); - CAP32_TO_CAP16(SPEED_40G); - CAP32_TO_CAP16(SPEED_100G); - CAP32_TO_CAP16(FC_RX); - CAP32_TO_CAP16(FC_TX); - CAP32_TO_CAP16(802_3_PAUSE); - CAP32_TO_CAP16(802_3_ASM_DIR); - CAP32_TO_CAP16(ANEG); - CAP32_TO_CAP16(FORCE_PAUSE); - CAP32_TO_CAP16(MDIAUTO); - CAP32_TO_CAP16(MDISTRAIGHT); - CAP32_TO_CAP16(FEC_RS); - CAP32_TO_CAP16(FEC_BASER_RS); - - #undef CAP32_TO_CAP16 - - return caps16; -} - -/* Translate Firmware Port Capabilities Pause specification to Common Code */ -static inline cc_pause_t fwcap_to_cc_pause(fw_port_cap32_t fw_pause) -{ - cc_pause_t cc_pause = 0; - - if (fw_pause & FW_PORT_CAP32_FC_RX) - cc_pause |= PAUSE_RX; - if (fw_pause & FW_PORT_CAP32_FC_TX) - cc_pause |= PAUSE_TX; - - return cc_pause; -} - -/* Translate Common Code Pause specification into Firmware Port Capabilities */ -static inline fw_port_cap32_t cc_to_fwcap_pause(cc_pause_t cc_pause) -{ - fw_port_cap32_t fw_pause = 0; - - /* Translate orthogonal RX/TX Pause Controls for L1 Configure - * commands, etc. - */ - if (cc_pause & PAUSE_RX) - fw_pause |= FW_PORT_CAP32_FC_RX; - if (cc_pause & PAUSE_TX) - fw_pause |= FW_PORT_CAP32_FC_TX; - if (!(cc_pause & PAUSE_AUTONEG)) - fw_pause |= FW_PORT_CAP32_FORCE_PAUSE; - - return fw_pause; -} - -/* Translate Firmware Forward Error Correction specification to Common Code */ -static inline cc_fec_t fwcap_to_cc_fec(fw_port_cap32_t fw_fec) -{ - cc_fec_t cc_fec = 0; - - if (fw_fec & FW_PORT_CAP32_FEC_RS) - cc_fec |= FEC_RS; - if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) - cc_fec |= FEC_BASER_RS; - - if (cc_fec == 0) - cc_fec = FEC_NONE; - - return (cc_fec); -} - -/* Translate Common Code Forward Error Correction specification to Firmware */ -static inline boolean_t -cc_to_fwcap_fec(fw_port_cap32_t *fw_fecp, cc_fec_t cc_fec, - struct link_config *lc) -{ - fw_port_cap32_t fw_fec = 0; - - if ((cc_fec & FEC_AUTO) != 0) { - if ((lc->pcaps & FW_PORT_CAP32_SPEED_100G) == 0) - fw_fec |= FW_PORT_CAP32_FEC_BASER_RS; - - if ((lc->pcaps & FW_PORT_CAP32_FORCE_FEC) != 0) - fw_fec |= FW_PORT_CAP32_FEC_NO_FEC; - - fw_fec |= FW_PORT_CAP32_FEC_RS; - - *fw_fecp = fw_fec; - return (B_TRUE); - } - - if ((cc_fec & FEC_RS) != 0) - fw_fec |= FW_PORT_CAP32_FEC_RS; - - if ((cc_fec & FEC_BASER_RS) != 0 && - (lc->pcaps & FW_PORT_CAP32_SPEED_100G) == 0) - fw_fec |= FW_PORT_CAP32_FEC_BASER_RS; - - if ((cc_fec & FEC_NONE) != 0) { - if ((lc->pcaps & FW_PORT_CAP32_FORCE_FEC) != 0) { - fw_fec |= FW_PORT_CAP32_FORCE_FEC; - fw_fec |= FW_PORT_CAP32_FEC_NO_FEC; - } - - *fw_fecp = fw_fec; - return (B_TRUE); - } - - if (fw_fec == 0) - return (B_FALSE); - - if ((lc->pcaps & FW_PORT_CAP32_FORCE_FEC) != 0) - fw_fec |= FW_PORT_CAP32_FORCE_FEC; - - *fw_fecp = fw_fec; - return (B_TRUE); -} - -/** - * t4_link_acaps - compute Link Advertised Port Capabilities - * @adapter: the adapter - * @port: the Port ID - * @lc: the Port's Link Configuration - * - * Synthesize the Advertised Port Capabilities we'll be using based on - * the base Advertised Port Capabilities (which have been filtered by - * ADVERT_MASK) plus the individual controls for things like Pause - * Frames, Forward Error Correction, MDI, etc. - */ -fw_port_cap32_t t4_link_acaps(struct adapter *adapter, unsigned int port, - struct link_config *lc) -{ - unsigned int fw_mdi = - (V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO) & lc->pcaps); - fw_port_cap32_t fw_fc, fw_fec, acaps; - cc_fec_t cc_fec; - - /* Convert driver coding of Pause Frame Flow Control settings into the - * Firmware's API. - */ - fw_fc = cc_to_fwcap_pause(lc->requested_fc); - - /* Convert Common Code Forward Error Control settings into the - * Firmware's API. If the current Requested FEC has "Automatic" - * (IEEE 802.3) specified, then we use whatever the Firmware - * sent us as part of it's IEEE 802.3-based interpratation of - * the Transceiver Module EPROM FEC parameters. Otherwise we - * use whatever is in the current Requested FEC settings. - */ - if (fec_supported(lc->pcaps)) { - if (lc->requested_fec & FEC_AUTO) - cc_fec = fwcap_to_cc_fec(lc->def_acaps); - else - cc_fec = lc->requested_fec; - - if (!cc_to_fwcap_fec(&fw_fec, cc_fec, lc)) - return (0); - } else { - fw_fec = 0; - cc_fec = FEC_NONE; - } - - /* Figure out what our Requested Port Capabilities are going to be. - * Note parallel structure in t4_handle_get_port_info() and - * init_link_config(). - */ - if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { - acaps = lc->acaps | fw_fc | fw_fec; - lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; - lc->fec = cc_fec; - } else if (lc->autoneg == AUTONEG_DISABLE) { - acaps = lc->speed_caps | fw_fc | fw_fec | fw_mdi; - lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; - lc->fec = cc_fec; - } else - acaps = lc->acaps | fw_fc | fw_fec | fw_mdi; - - /* Some Requested Port Capabilities are trivially wrong if they exceed - * the Physical Port Capabilities. We can check that here and provide - * moderately useful feedback in the system log. - * - * Note that older Firmware doesn't have FW_PORT_CAP32_FORCE_PAUSE, so - * we need to exclude this from this check in order to maintain - * compatibility ... - */ - if ((acaps & ~lc->pcaps) & ~FW_PORT_CAP32_FORCE_PAUSE) { - CH_ERR(adapter, - "Requested Port Capabilities %#x exceed Physical Port Capabilities %#x\n", - acaps, lc->pcaps); - return 0; - } - - return acaps; -} - -/** - * t4_link_l1cfg_core - apply link configuration to MAC/PHY - * @adapter: the adapter - * @mbox: the Firmware Mailbox to use - * @port: the Port ID - * @lc: the Port's Link Configuration - * @sleep_ok: if true we may sleep while awaiting command completion - * @timeout: time to wait for command to finish before timing out - * (negative implies @sleep_ok=false) - * - * Set up a port's MAC and PHY according to a desired link configuration. - * - If the PHY can auto-negotiate first decide what to advertise, then - * enable/disable auto-negotiation as desired, and reset. - * - If the PHY does not auto-negotiate just reset it. - * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, - * otherwise do it later based on the outcome of auto-negotiation. - */ -int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, - unsigned int port, struct link_config *lc, - bool sleep_ok, int timeout) -{ - unsigned int fw_caps = adapter->params.fw_caps_support; - fw_port_cap32_t rcap; - struct fw_port_cmd cmd; - int ret; - - /* Filter out nonsense. - */ - if (!(lc->pcaps & FW_PORT_CAP32_ANEG) && - lc->autoneg == AUTONEG_ENABLE) - return -EINVAL; - - /* Compute our Requested Port Capabilities and send that on to the - * Firmware. - */ - rcap = t4_link_acaps(adapter, port, lc); - if(!rcap) - return -EINVAL; - memset(&cmd, 0, sizeof(cmd)); - cmd.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | - F_FW_CMD_REQUEST | F_FW_CMD_EXEC | - V_FW_PORT_CMD_PORTID(port)); - cmd.action_to_len16 = - cpu_to_be32(V_FW_PORT_CMD_ACTION(fw_caps == FW_CAPS16 - ? FW_PORT_ACTION_L1_CFG - : FW_PORT_ACTION_L1_CFG32) | - FW_LEN16(cmd)); - if (fw_caps == FW_CAPS16) - cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap)); - else - cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap); - ret = t4_wr_mbox_meat_timeout(adapter, mbox, &cmd, sizeof(cmd), NULL, - sleep_ok, timeout); - - /* Unfortunately, even if the Requested Port Capabilities "fit" within - * the Physical Port Capabilities, some combinations of features may - * still not be legal. For example, 40Gb/s and Reed-Solomon Forward - * Error Correction. So if the Firmware rejects the L1 Configure - * request, flag that here. - */ - if (ret) { - CH_ERR(adapter, - "Requested Port Capabilities %#x rejected, error %d\n", - rcap, -ret); - return ret; - } - return 0; -} - -/** - * t4_restart_aneg - restart autonegotiation - * @adap: the adapter - * @mbox: mbox to use for the FW command - * @port: the port id - * - * Restarts autonegotiation for the selected port. - */ -int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port) -{ - unsigned int fw_caps = adap->params.fw_caps_support; - struct fw_port_cmd c; - - memset(&c, 0, sizeof(c)); - c.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | - F_FW_CMD_REQUEST | F_FW_CMD_EXEC | - V_FW_PORT_CMD_PORTID(port)); - c.action_to_len16 = - cpu_to_be32(V_FW_PORT_CMD_ACTION(fw_caps == FW_CAPS16 - ? FW_PORT_ACTION_L1_CFG - : FW_PORT_ACTION_L1_CFG32) | - FW_LEN16(c)); - if (fw_caps == FW_CAPS16) - c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP_ANEG); - else - c.u.l1cfg32.rcap32 = cpu_to_be32(FW_PORT_CAP32_ANEG); - return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); -} - typedef void (*int_handler_t)(struct adapter *adap); struct intr_info { @@ -9216,34 +8845,9 @@ int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, } /** - * t4_link_down_rc_str - return a string for a Link Down Reason Code - * @link_down_rc: Link Down Reason Code - * - * Returns a string representation of the Link Down Reason Code. + * Return the highest speed set in the port capabilities, in Mb/s. */ -const char *t4_link_down_rc_str(unsigned char link_down_rc) -{ - static const char * const reason[] = { - "Link Down", - "Remote Fault", - "Auto-negotiation Failure", - "Reserved", - "Insufficient Airflow", - "Unable To Determine Reason", - "No RX Signal Detected", - "Reserved", - }; - - if (link_down_rc >= ARRAY_SIZE(reason)) - return "Bad Reason Code"; - - return reason[link_down_rc]; -} - -/** - * Return the highest speed set in the port capabilities, in Mb/s. - */ -static unsigned int fwcap_to_speed(fw_port_cap32_t caps) +unsigned int t4_link_fwcap_to_speed(fw_port_cap32_t caps) { #define TEST_SPEED_RETURN(__caps_speed, __speed) \ do { \ @@ -9267,14 +8871,14 @@ static unsigned int fwcap_to_speed(fw_port_cap32_t caps) } /** - * fwcap_to_fwspeed - return highest speed in Port Capabilities + * t4_link_fwcap_to_fwspeed - return highest speed in Port Capabilities * @acaps: advertised Port Capabilities * * Get the highest speed for the port from the advertised Port * Capabilities. It will be either the highest speed from the list of * speeds or whatever user has set using ethtool. */ -static fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) +static fw_port_cap32_t t4_link_fwcap_to_fwspeed(fw_port_cap32_t acaps) { #define TEST_SPEED_RETURN(__caps_speed) \ do { \ @@ -9297,6 +8901,487 @@ static fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) return 0; } +/** + * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits + * @caps16: a 16-bit Port Capabilities value + * + * Returns the equivalent 32-bit Port Capabilities value. + */ +static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) +{ + fw_port_cap32_t caps32 = 0; + + #define CAP16_TO_CAP32(__cap) \ + do { \ + if (caps16 & FW_PORT_CAP_##__cap) \ + caps32 |= FW_PORT_CAP32_##__cap; \ + } while (0) + + CAP16_TO_CAP32(SPEED_100M); + CAP16_TO_CAP32(SPEED_1G); + CAP16_TO_CAP32(SPEED_25G); + CAP16_TO_CAP32(SPEED_10G); + CAP16_TO_CAP32(SPEED_40G); + CAP16_TO_CAP32(SPEED_100G); + CAP16_TO_CAP32(FC_RX); + CAP16_TO_CAP32(FC_TX); + CAP16_TO_CAP32(ANEG); + CAP16_TO_CAP32(FORCE_PAUSE); + CAP16_TO_CAP32(MDIAUTO); + CAP16_TO_CAP32(MDISTRAIGHT); + CAP16_TO_CAP32(FEC_RS); + CAP16_TO_CAP32(FEC_BASER_RS); + CAP16_TO_CAP32(802_3_PAUSE); + CAP16_TO_CAP32(802_3_ASM_DIR); + + #undef CAP16_TO_CAP32 + + return caps32; +} + +/** + * fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits + * @caps32: a 32-bit Port Capabilities value + * + * Returns the equivalent 16-bit Port Capabilities value. Note that + * not all 32-bit Port Capabilities can be represented in the 16-bit + * Port Capabilities and some fields/values may not make it. + */ +static fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32) +{ + fw_port_cap16_t caps16 = 0; + + #define CAP32_TO_CAP16(__cap) \ + do { \ + if (caps32 & FW_PORT_CAP32_##__cap) \ + caps16 |= FW_PORT_CAP_##__cap; \ + } while (0) + + CAP32_TO_CAP16(SPEED_100M); + CAP32_TO_CAP16(SPEED_1G); + CAP32_TO_CAP16(SPEED_10G); + CAP32_TO_CAP16(SPEED_25G); + CAP32_TO_CAP16(SPEED_40G); + CAP32_TO_CAP16(SPEED_100G); + CAP32_TO_CAP16(FC_RX); + CAP32_TO_CAP16(FC_TX); + CAP32_TO_CAP16(802_3_PAUSE); + CAP32_TO_CAP16(802_3_ASM_DIR); + CAP32_TO_CAP16(ANEG); + CAP32_TO_CAP16(FORCE_PAUSE); + CAP32_TO_CAP16(MDIAUTO); + CAP32_TO_CAP16(MDISTRAIGHT); + CAP32_TO_CAP16(FEC_RS); + CAP32_TO_CAP16(FEC_BASER_RS); + + #undef CAP32_TO_CAP16 + + return caps16; +} + +int t4_link_set_autoneg(struct port_info *pi, u8 autoneg, + fw_port_cap32_t *new_caps) +{ + struct link_config *lc = &pi->link_cfg; + fw_port_cap32_t caps = *new_caps; + + if (autoneg) { + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) + return -ENOTSUP; + + caps |= FW_PORT_CAP32_ANEG; + } else { + caps &= ~FW_PORT_CAP32_ANEG; + } + + caps &= ~V_FW_PORT_CAP32_MDI(M_FW_PORT_CAP32_MDI); + if (lc->pcaps & FW_PORT_CAP32_MDIAUTO) + caps |= FW_PORT_CAP32_MDIAUTO; + + *new_caps = caps; + return 0; +} + +int t4_link_set_pause(struct port_info *pi, cc_pause_t pause, + fw_port_cap32_t *new_caps) +{ + struct link_config *lc = &pi->link_cfg; + fw_port_cap32_t caps = *new_caps; + + caps &= ~V_FW_PORT_CAP32_FC(M_FW_PORT_CAP32_FC); + caps &= ~V_FW_PORT_CAP32_802_3(M_FW_PORT_CAP32_802_3); + + if ((pause & PAUSE_TX) && (pause & PAUSE_RX)) { + caps |= FW_PORT_CAP32_FC_TX | FW_PORT_CAP32_FC_RX; + if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE) + caps |= FW_PORT_CAP32_802_3_PAUSE; + } else if (pause & PAUSE_TX) { + caps |= FW_PORT_CAP32_FC_TX; + if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR) + caps |= FW_PORT_CAP32_802_3_ASM_DIR; + } else if (pause & PAUSE_RX) { + caps |= FW_PORT_CAP32_FC_RX; + if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE) + caps |= FW_PORT_CAP32_802_3_PAUSE; + if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR) + caps |= FW_PORT_CAP32_802_3_ASM_DIR; + } + + if (!(pause & PAUSE_AUTONEG)) + caps |= FW_PORT_CAP32_FORCE_PAUSE; + + *new_caps = caps; + return 0; +} + +#define T4_LINK_FEC_MASK V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC) + +static fw_port_cap32_t t4_link_supported_speed_to_fec(u32 speed) +{ + fw_port_cap32_t caps = 0; + + switch (speed) { + case 100000: + caps |= FW_PORT_CAP32_FEC_RS; + break; + case 50000: + caps |= FW_PORT_CAP32_FEC_BASER_RS; + break; + case 25000: + caps |= FW_PORT_CAP32_FEC_RS | + FW_PORT_CAP32_FEC_BASER_RS; + break; + default: + break; + } + + caps |= FW_PORT_CAP32_FEC_NO_FEC; + return caps; +} + +static void t4_link_update_fec(struct port_info *pi, u32 max_speed, + cc_fec_t fec, fw_port_cap32_t *new_caps) +{ + fw_port_cap32_t caps = *new_caps; + + caps &= ~T4_LINK_FEC_MASK; + if (fec & FEC_RS) { + switch (max_speed) { + case 100000: + case 25000: + caps |= FW_PORT_CAP32_FEC_RS; + break; + default: + CH_ERR(pi->adapter, + "Ignoring unsupported RS FEC for speed %u\n", + max_speed); + break; + } + } + + if (fec & FEC_BASER_RS) { + switch (max_speed) { + case 50000: + case 25000: + caps |= FW_PORT_CAP32_FEC_BASER_RS; + break; + default: + CH_ERR(pi->adapter, + "Ignoring unsupported BASER FEC for speed %u\n", + max_speed); + break; + } + } + + if (fec & FEC_NONE) + caps |= FW_PORT_CAP32_FEC_NO_FEC; + + if (!(caps & T4_LINK_FEC_MASK)) { + /* No explicit encoding is requested. + * So, default back to AUTO. + */ + caps |= t4_link_supported_speed_to_fec(max_speed); + caps &= ~FW_PORT_CAP32_FORCE_FEC; + } + + if (fec & FEC_FORCE) + caps |= FW_PORT_CAP32_FORCE_FEC; + + *new_caps = caps; +} + +int t4_link_set_fec(struct port_info *pi, cc_fec_t fec, + fw_port_cap32_t *new_caps) +{ + struct link_config *lc = &pi->link_cfg; + u32 max_speed; + + if (!(lc->pcaps & T4_LINK_FEC_MASK)) + return -ENOTSUP; + + max_speed = t4_link_fwcap_to_speed(lc->link_caps); + /* Link might be down. In that case consider the max + * speed advertised + */ + if (!max_speed) + max_speed = t4_link_fwcap_to_speed(lc->acaps); + + t4_link_update_fec(pi, max_speed, fec, new_caps); + return 0; +} + +#define T4_LINK_SPEED_MASK V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED) + +int t4_link_set_speed(struct port_info *pi, fw_port_cap32_t speed, u8 en, + fw_port_cap32_t *new_caps) +{ + fw_port_cap32_t tcaps, caps = *new_caps; + struct link_config *lc = &pi->link_cfg; + + if (((lc->pcaps & T4_LINK_SPEED_MASK) & speed) != speed) + return -ENOTSUP; + + if (en) + caps |= speed; + else + caps &= ~speed; + + /* If no speeds are left, then pick the next highest speed. */ + if (!(caps & T4_LINK_SPEED_MASK)) { + tcaps = CAP32_SPEED(lc->pcaps); + tcaps &= ~speed; + tcaps &= (speed - 1); + if (tcaps == 0) + return -EINVAL; + + caps |= t4_link_fwcap_to_fwspeed(tcaps); + } + + *new_caps = caps; + return 0; +} + +static void t4_link_sanitize_speed_caps(struct link_config *lc, + fw_port_cap32_t *new_caps) +{ + fw_port_cap32_t tcaps, caps = *new_caps; + + /* Sanitize Speeds when AN is disabled */ + if (!(caps & FW_PORT_CAP32_ANEG)) { + tcaps = CAP32_SPEED(caps); + caps &= ~T4_LINK_SPEED_MASK; + caps |= t4_link_fwcap_to_fwspeed(tcaps); + } + + *new_caps = caps; +} + +static void t4_link_sanitize_fec_caps(struct link_config *lc, + fw_port_cap32_t *new_caps) +{ + fw_port_cap32_t tcaps, caps = *new_caps; + u32 max_speed; + + /* Sanitize FECs when supported */ + if (CAP32_FEC(lc->pcaps)) { + max_speed = t4_link_fwcap_to_speed(caps); + tcaps = t4_link_supported_speed_to_fec(max_speed); + if (caps & FW_PORT_CAP32_FORCE_FEC) { + /* If the current chosen FEC params are + * completely invalid, then disable FEC. + * Else, pick only the FECs requested + * by user or the defaults supported by + * the speed. + */ + if (!(tcaps & CAP32_FEC(caps))) + tcaps = FW_PORT_CAP32_FEC_NO_FEC; + else + tcaps &= CAP32_FEC(caps); + } + } else { + /* Always force NO_FEC when FECs are not supported */ + tcaps = FW_PORT_CAP32_FEC_NO_FEC; + } + + if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) { + tcaps |= FW_PORT_CAP32_FORCE_FEC; + } else { + /* Older firmware doesn't allow driver to send request + * to try multiple FECs for FEC_AUTO case. So, clear + * the FEC caps for FEC_AUTO case because the older + * firmware will try all supported FECs on its own. + */ + caps &= ~FW_PORT_CAP32_FORCE_FEC; + if (tcaps & (tcaps - 1)) + tcaps = 0; + } + + caps &= ~T4_LINK_FEC_MASK; + caps |= tcaps; + + *new_caps = caps; +} + +static void t4_link_sanitize_caps(struct link_config *lc, + fw_port_cap32_t *new_caps) +{ + fw_port_cap32_t caps = *new_caps; + + t4_link_sanitize_speed_caps(lc, &caps); + t4_link_sanitize_fec_caps(lc, &caps); + + /* Remove all unsupported caps */ + if ((lc->pcaps | caps) != lc->pcaps) + caps &= lc->pcaps; + + *new_caps = caps; +} + +/** + * t4_link_l1cfg_core - apply link configuration to MAC/PHY + * @adapter: the adapter + * @mbox: the Firmware Mailbox to use + * @port: the Port ID + * @lc: the Port's Link Configuration + * @rcap: new link configuration + * @sleep_ok: if true we may sleep while awaiting command completion + * @timeout: time to wait for command to finish before timing out + * (negative implies @sleep_ok=false) + * + * Set up a port's MAC and PHY according to a desired link configuration. + * - If the PHY can auto-negotiate first decide what to advertise, then + * enable/disable auto-negotiation as desired, and reset. + * - If the PHY does not auto-negotiate just reset it. + * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, + * otherwise do it later based on the outcome of auto-negotiation. + */ +int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, + unsigned int port, struct link_config *lc, + fw_port_cap32_t rcap, bool sleep_ok, int timeout) +{ + unsigned int fw_caps = adapter->params.fw_caps_support; + struct fw_port_cmd cmd; + int ret; + + t4_link_sanitize_caps(lc, &rcap); + + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | + F_FW_CMD_REQUEST | F_FW_CMD_EXEC | + V_FW_PORT_CMD_PORTID(port)); + cmd.action_to_len16 = + cpu_to_be32(V_FW_PORT_CMD_ACTION(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_L1_CFG + : FW_PORT_ACTION_L1_CFG32) | + FW_LEN16(cmd)); + if (fw_caps == FW_CAPS16) + cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap)); + else + cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap); + ret = t4_wr_mbox_meat_timeout(adapter, mbox, &cmd, sizeof(cmd), NULL, + sleep_ok, timeout); + + /* Unfortunately, even if the Requested Port Capabilities "fit" within + * the Physical Port Capabilities, some combinations of features may + * still not be legal. For example, 40Gb/s and Reed-Solomon Forward + * Error Correction. So if the Firmware rejects the L1 Configure + * request, flag that here. + */ + if (ret) { + CH_ERR(adapter, + "Requested Port Capabilities 0x%x rejected, error %d\n", + rcap, -ret); + return ret; + } + + return 0; +} + +/** + * t4_restart_aneg - restart autonegotiation + * @adap: the adapter + * @mbox: mbox to use for the FW command + * @port: the port id + * + * Restarts autonegotiation for the selected port. + */ +int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port) +{ + unsigned int fw_caps = adap->params.fw_caps_support; + struct fw_port_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) | + F_FW_CMD_REQUEST | F_FW_CMD_EXEC | + V_FW_PORT_CMD_PORTID(port)); + c.action_to_len16 = + cpu_to_be32(V_FW_PORT_CMD_ACTION(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_L1_CFG + : FW_PORT_ACTION_L1_CFG32) | + FW_LEN16(c)); + if (fw_caps == FW_CAPS16) + c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP_ANEG); + else + c.u.l1cfg32.rcap32 = cpu_to_be32(FW_PORT_CAP32_ANEG); + return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + * t4_init_link_config - initialize a link's SW state + * @pi: the port info + * @pcaps: link Port Capabilities + * @acaps: link current Advertised Port Capabilities + * + * Initializes the SW state maintained for each link, including the link's + * capabilities and default speed/flow-control/autonegotiation settings. + */ +static void t4_init_link_config(struct port_info *pi, fw_port_cap32_t pcaps, + fw_port_cap32_t acaps) +{ + u32 max_speed = t4_link_fwcap_to_speed(acaps); + struct link_config *lc = &pi->link_cfg; + fw_port_cap32_t new_caps = acaps; + + /* If initializing for the first time or if port module changed, + * then overwrite the saved link params with the new port module + * caps. + */ + if (lc->admin_caps == 0 || lc->pcaps != pcaps) { + t4_link_update_fec(pi, max_speed, FEC_AUTO, &new_caps); + lc->admin_caps = new_caps; + } + + lc->pcaps = pcaps; + lc->acaps = acaps; + lc->lpacaps = 0; + lc->link_caps = 0; +} + +/** + * t4_link_down_rc_str - return a string for a Link Down Reason Code + * @link_down_rc: Link Down Reason Code + * + * Returns a string representation of the Link Down Reason Code. + */ +const char *t4_link_down_rc_str(unsigned char link_down_rc) +{ + static const char * const reason[] = { + "Link Down", + "Remote Fault", + "Auto-negotiation Failure", + "Reserved", + "Insufficient Airflow", + "Unable To Determine Reason", + "No RX Signal Detected", + "Reserved", + }; + + if (link_down_rc >= ARRAY_SIZE(reason)) + return "Bad Reason Code"; + + return reason[link_down_rc]; +} + /** * lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities * @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value @@ -9349,9 +9434,7 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) int link_ok, linkdnrc; enum fw_port_type port_type; enum fw_port_module_type mod_type; - unsigned int speed, fc, fec; fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; - boolean_t fec_changed; /* * Extract the various fields from the Port Information message. @@ -9391,10 +9474,6 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) return; } - fec = fwcap_to_cc_fec(linkattr); - fc = fwcap_to_cc_pause(linkattr); - speed = fwcap_to_speed(linkattr); - /* * Reset state for communicating new Transceiver Module status and * whether the OS-dependent layer wants us to redo the current @@ -9404,28 +9483,6 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) lc->redo_l1cfg = false; if (mod_type != pi->mod_type) { - /* - * With the newer SFP28 and QSFP28 Transceiver Module Types, - * various fundamental Port Capabilities which used to be - * immutable can now change radically. We can now have - * Speeds, Auto-Negotiation, Forward Error Correction, etc. - * all change based on what Transceiver Module is inserted. - * So we need to record the Physical "Port" Capabilities on - * every Transceiver Module change. - */ - lc->pcaps = pcaps; - - /* - * When a new Transceiver Module is inserted, the Firmware - * will examine its i2c EPROM to determine its type and - * general operating parameters including things like Forward - * Error Control, etc. Various IEEE 802.3 standards dictate - * how to interpret these i2c values to determine default - * "sutomatic" settings. We record these for future use when - * the user explicitly requests these standards-based values. - */ - lc->def_acaps = acaps; - /* * Some versions of the early T6 Firmware "cheated" when * handling different Transceiver Modules by changing the @@ -9450,13 +9507,13 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) */ lc->new_module = t4_is_inserted_mod_type(mod_type); + if (lc->new_module) + t4_init_link_config(pi, pcaps, acaps); t4_os_portmod_changed(adapter, pi->port_id); } - fec_changed = fec != (lc->requested_fec == FEC_AUTO ? - lc->fec : lc->requested_fec); - if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc || fec_changed) { + if (link_ok != lc->link_ok || acaps != lc->acaps || + lpacaps != lc->lpacaps || linkattr != lc->link_caps) { /* something changed */ if (!link_ok && lc->link_ok) { lc->link_down_rc = linkdnrc; @@ -9464,39 +9521,11 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) "Port %d link down, reason: %s\n", pi->tx_chan, t4_link_down_rc_str(linkdnrc)); } - lc->link_ok = link_ok; - lc->speed = speed; - lc->fc = fc; - lc->fec = fec; - if (fec_changed) { - /* - * If the fec is not as requested we need - * to save the l1 config. - */ - lc->redo_l1cfg = B_TRUE; - } + lc->link_ok = link_ok; + lc->acaps = acaps; lc->lpacaps = lpacaps; - lc->acaps = acaps & ADVERT_MASK; - - /* If we're not physically capable of Auto-Negotiation, note - * this as Auto-Negotiation disabled. Otherwise, we track - * what Auto-Negotiation settings we have. Note parallel - * structure in t4_link_l1cfg_core() and init_link_config(). - */ - if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { - lc->autoneg = AUTONEG_DISABLE; - } else if (lc->acaps & FW_PORT_CAP32_ANEG) { - lc->autoneg = AUTONEG_ENABLE; - } else { - /* When Autoneg is disabled, user needs to set - * single speed. - * Similar to cxgb4_ethtool.c: set_link_ksettings - */ - lc->acaps = 0; - lc->speed_caps = fwcap_to_fwspeed(acaps); - lc->autoneg = AUTONEG_DISABLE; - } + lc->link_caps = linkattr; t4_os_link_changed(adapter, pi->port_id, link_ok); } @@ -9507,7 +9536,6 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) * Link Parameters are set, do that now. */ if (lc->new_module && lc->redo_l1cfg) { - struct link_config old_lc; int ret; /* @@ -9516,10 +9544,9 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) * routines not to change the link_config when an error * occurs ... */ - old_lc = *lc; - ret = t4_link_l1cfg_ns(adapter, adapter->mbox, pi->lport, lc); + ret = t4_link_l1cfg_ns(adapter, adapter->mbox, pi->lport, lc, + lc->admin_caps); if (ret) { - *lc = old_lc; CH_WARN(adapter, "Attempt to update new Transceiver Module settings failed\n"); } @@ -9611,7 +9638,7 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, } *link_okp = link_ok; - *speedp = fwcap_to_speed(linkattr); + *speedp = t4_link_fwcap_to_speed(linkattr); *mtup = mtu; return 0; @@ -9681,57 +9708,6 @@ static void get_pci_mode(struct adapter *adapter, } } -/** - * init_link_config - initialize a link's SW state - * @lc: pointer to structure holding the link state - * @pcaps: link Port Capabilities - * @acaps: link current Advertised Port Capabilities - * - * Initializes the SW state maintained for each link, including the link's - * capabilities and default speed/flow-control/autonegotiation settings. - */ -static void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps, - fw_port_cap32_t acaps) -{ - lc->pcaps = pcaps; - lc->def_acaps = acaps; - lc->lpacaps = 0; - lc->speed_caps = 0; - lc->speed = 0; - lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - - if (fec_supported(pcaps)) { - /* - * For Forward Error Control, we default to whatever the - * Firmware tells us the Link is currently advertising. - * We also retain any overrides set. - */ - if (lc->requested_fec == 0) - lc->requested_fec = FEC_AUTO; - lc->fec = fwcap_to_cc_fec(lc->def_acaps); - } else { - lc->requested_fec = FEC_NONE; - lc->fec = FEC_NONE; - } - - /* If the Port is capable of Auto-Negtotiation, initialize it as - * "enabled" and copy over all of the Physical Port Capabilities - * to the Advertised Port Capabilities. Otherwise mark it as - * Auto-Negotiate disabled and select the highest supported speed - * for the link. Note parallel structure in t4_link_l1cfg_core() - * and t4_handle_get_port_info(). - */ - if (lc->pcaps & FW_PORT_CAP32_ANEG) { - lc->acaps = lc->pcaps & ADVERT_MASK; - lc->autoneg = AUTONEG_ENABLE; - lc->requested_fc |= PAUSE_AUTONEG; - } else { - lc->acaps = 0; - lc->autoneg = AUTONEG_DISABLE; - lc->speed_caps = fwcap_to_fwspeed(acaps); - } -} - /** * t4_wait_dev_ready - wait till to reads of registers work * @@ -10781,7 +10757,7 @@ int t4_init_portinfo_viid(struct port_info *pi, int mbox, pi->mdio_addr = mdio_addr; pi->mod_type = FW_PORT_MOD_TYPE_NA; - init_link_config(&pi->link_cfg, pcaps, acaps); + t4_init_link_config(pi, pcaps, acaps); return 0; } diff --git a/usr/src/uts/common/io/cxgbe/firmware/t4fw_interface.h b/usr/src/uts/common/io/cxgbe/firmware/t4fw_interface.h index b998e85bae..7416b5a208 100644 --- a/usr/src/uts/common/io/cxgbe/firmware/t4fw_interface.h +++ b/usr/src/uts/common/io/cxgbe/firmware/t4fw_interface.h @@ -7259,7 +7259,7 @@ enum fw_port_mdi32 { (((x) >> S_FW_PORT_CAP32_MDI) & M_FW_PORT_CAP32_MDI) #define S_FW_PORT_CAP32_FEC 23 -#define M_FW_PORT_CAP32_FEC 0x5f +#define M_FW_PORT_CAP32_FEC 0x1f #define V_FW_PORT_CAP32_FEC(x) ((x) << S_FW_PORT_CAP32_FEC) #define G_FW_PORT_CAP32_FEC(x) \ (((x) >> S_FW_PORT_CAP32_FEC) & M_FW_PORT_CAP32_FEC) diff --git a/usr/src/uts/common/io/cxgbe/t4nex/adapter.h b/usr/src/uts/common/io/cxgbe/t4nex/adapter.h index e37059f60c..081fe870b1 100644 --- a/usr/src/uts/common/io/cxgbe/t4nex/adapter.h +++ b/usr/src/uts/common/io/cxgbe/t4nex/adapter.h @@ -480,6 +480,10 @@ typedef int (*cpl_handler_t)(struct sge_iq *, const struct rss_header *, mblk_t *); typedef int (*fw_msg_handler_t)(struct adapter *, const __be64 *); +struct t4_mbox_list { + STAILQ_ENTRY(t4_mbox_list) link; +}; + struct adapter { SLIST_ENTRY(adapter) link; dev_info_t *dip; @@ -566,6 +570,10 @@ struct adapter { id_t volt_sensor; ddi_ufm_handle_t *ufm_hdl; + + /* support for single-threading access to adapter mailbox registers */ + kmutex_t mbox_lock; + STAILQ_HEAD(, t4_mbox_list) mbox_list; }; enum { @@ -638,17 +646,38 @@ struct memwin { /* One for errors, one for firmware events */ #define T4_EXTRA_INTR 2 -/* Presently disabling locking around mbox access - * We may need to reenable it later - */ -typedef int t4_os_lock_t; +typedef kmutex_t t4_os_lock_t; + static inline void t4_os_lock(t4_os_lock_t *lock) { - + mutex_enter(lock); } + static inline void t4_os_unlock(t4_os_lock_t *lock) { + mutex_exit(lock); +} + +static inline void t4_mbox_list_add(struct adapter *adap, + struct t4_mbox_list *entry) +{ + t4_os_lock(&adap->mbox_lock); + STAILQ_INSERT_TAIL(&adap->mbox_list, entry, link); + t4_os_unlock(&adap->mbox_lock); +} + +static inline void t4_mbox_list_del(struct adapter *adap, + struct t4_mbox_list *entry) +{ + t4_os_lock(&adap->mbox_lock); + STAILQ_REMOVE(&adap->mbox_list, entry, t4_mbox_list, link); + t4_os_unlock(&adap->mbox_lock); +} +static inline struct t4_mbox_list * +t4_mbox_list_first_entry(struct adapter *adap) +{ + return STAILQ_FIRST(&adap->mbox_list); } static inline uint32_t @@ -751,6 +780,12 @@ is_40G_port(const struct port_info *pi) return ((pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_40G) != 0); } +static inline bool +is_50G_port(const struct port_info *pi) +{ + return ((pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_50G) != 0); +} + static inline bool is_100G_port(const struct port_info *pi) { @@ -761,25 +796,8 @@ static inline bool is_10XG_port(const struct port_info *pi) { return (is_10G_port(pi) || is_40G_port(pi) || - is_25G_port(pi) || is_100G_port(pi)); -} - -static inline char * -print_port_speed(const struct port_info *pi) -{ - if (!pi) - return "-"; - - if (is_100G_port(pi)) - return "100G"; - else if (is_40G_port(pi)) - return "40G"; - else if (is_25G_port(pi)) - return "25G"; - else if (is_10G_port(pi)) - return "10G"; - else - return "1G"; + is_25G_port(pi) || is_50G_port(pi) || + is_100G_port(pi)); } #ifdef TCP_OFFLOAD_ENABLE @@ -855,7 +873,7 @@ static inline void t4_db_dropped(struct adapter *adap) {} /* t4_nexus.c */ int t4_os_find_pci_capability(struct adapter *sc, int cap); -void t4_os_portmod_changed(const struct adapter *sc, int idx); +void t4_os_portmod_changed(struct adapter *sc, int idx); int adapter_full_init(struct adapter *sc); int adapter_full_uninit(struct adapter *sc); int port_full_init(struct port_info *pi); diff --git a/usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c b/usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c index 7dbd90089a..5860b6892f 100644 --- a/usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c +++ b/usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c @@ -127,7 +127,7 @@ t4_mc_getstat(void *arg, uint_t stat, uint64_t *val) switch (stat) { case MAC_STAT_IFSPEED: if (lc->link_ok != 0) { - *val = lc->speed; + *val = t4_link_fwcap_to_speed(lc->link_caps); *val *= 1000000; } else *val = 0; @@ -236,6 +236,10 @@ t4_mc_getstat(void *arg, uint_t stat, uint64_t *val) *val = !!(lc->pcaps & FW_PORT_CAP32_SPEED_100G); break; + case ETHER_STAT_CAP_50GFDX: + *val = !!(lc->pcaps & FW_PORT_CAP32_SPEED_50G); + break; + case ETHER_STAT_CAP_40GFDX: *val = !!(lc->pcaps & FW_PORT_CAP32_SPEED_40G); break; @@ -252,26 +256,22 @@ t4_mc_getstat(void *arg, uint_t stat, uint64_t *val) *val = !!(lc->pcaps & FW_PORT_CAP32_SPEED_1G); break; - case ETHER_STAT_CAP_1000HDX: - return (ENOTSUP); - case ETHER_STAT_CAP_100FDX: *val = !!(lc->pcaps & FW_PORT_CAP32_SPEED_100M); break; + case ETHER_STAT_CAP_1000HDX: case ETHER_STAT_CAP_100HDX: - return (ENOTSUP); - case ETHER_STAT_CAP_10FDX: case ETHER_STAT_CAP_10HDX: return (ENOTSUP); case ETHER_STAT_CAP_ASMPAUSE: - *val = 0; + *val = !!(lc->pcaps & FW_PORT_CAP32_FC_RX); break; case ETHER_STAT_CAP_PAUSE: - *val = 1; + *val = !!(lc->pcaps & FW_PORT_CAP32_FC_TX); break; case ETHER_STAT_CAP_AUTONEG: @@ -302,90 +302,153 @@ t4_mc_getstat(void *arg, uint_t stat, uint64_t *val) /* Advertised asymmetric pause capability */ case ETHER_STAT_ADV_CAP_ASMPAUSE: - *val = (((lc->requested_fc & PAUSE_TX) ? 1 : 0) ^ - (lc->requested_fc & PAUSE_RX)); + if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR) + *val = !!(lc->admin_caps & FW_PORT_CAP32_802_3_ASM_DIR); + else + *val = (!!(lc->admin_caps & FW_PORT_CAP32_FC_TX)) ^ + (!!(lc->admin_caps & FW_PORT_CAP32_FC_RX)); break; /* Advertised pause capability */ case ETHER_STAT_ADV_CAP_PAUSE: - *val = (lc->requested_fc & PAUSE_TX) ? 1 : 0; + if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE) + *val = !!(lc->admin_caps & FW_PORT_CAP32_802_3_PAUSE); + else + *val = !!(lc->admin_caps & FW_PORT_CAP32_FC_TX); break; case ETHER_STAT_ADV_CAP_100GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_100G); + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_100G); + break; + + case ETHER_STAT_ADV_CAP_50GFDX: + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_50G); break; case ETHER_STAT_ADV_CAP_40GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_40G); + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_40G); break; case ETHER_STAT_ADV_CAP_25GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_25G); + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_25G); break; case ETHER_STAT_ADV_CAP_10GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_10G); + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_10G); break; case ETHER_STAT_ADV_CAP_1000FDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_1G); + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_1G); + break; + + case ETHER_STAT_ADV_CAP_100FDX: + *val = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_100M); break; case ETHER_STAT_ADV_CAP_AUTONEG: - *val = !!(lc->acaps & FW_PORT_CAP32_ANEG); + *val = !!(lc->admin_caps & FW_PORT_CAP32_ANEG); break; case ETHER_STAT_ADV_CAP_1000HDX: - case ETHER_STAT_ADV_CAP_100FDX: case ETHER_STAT_ADV_CAP_100HDX: case ETHER_STAT_ADV_CAP_10FDX: case ETHER_STAT_ADV_CAP_10HDX: return (ENOTSUP); /* TODO */ + case ETHER_STAT_LP_CAP_ASMPAUSE: + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR) + *val = !!(lc->lpacaps & FW_PORT_CAP32_802_3_ASM_DIR); + else + *val = (!!(lc->lpacaps & FW_PORT_CAP32_FC_TX)) ^ + (!!(lc->lpacaps & FW_PORT_CAP32_FC_RX)); + break; + + case ETHER_STAT_LP_CAP_PAUSE: + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE) + *val = !!(lc->lpacaps & FW_PORT_CAP32_802_3_PAUSE); + else + *val = !!(lc->lpacaps & FW_PORT_CAP32_FC_TX); + break; case ETHER_STAT_LP_CAP_100GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_100G); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_100G); + break; + + case ETHER_STAT_LP_CAP_50GFDX: + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_50G); break; case ETHER_STAT_LP_CAP_40GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_40G); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_40G); break; case ETHER_STAT_LP_CAP_25GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_25G); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_25G); break; case ETHER_STAT_LP_CAP_10GFDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_10G); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_10G); break; case ETHER_STAT_LP_CAP_1000FDX: - *val = !!(lc->acaps & FW_PORT_CAP32_SPEED_1G); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_1G); + break; + + case ETHER_STAT_LP_CAP_100FDX: + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_SPEED_100M); break; case ETHER_STAT_LP_CAP_AUTONEG: - *val = !!(lc->acaps & FW_PORT_CAP32_ANEG); + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) + return (ENOTSUP); + + *val = !!(lc->lpacaps & FW_PORT_CAP32_ANEG); break; case ETHER_STAT_LP_CAP_1000HDX: - case ETHER_STAT_LP_CAP_100FDX: case ETHER_STAT_LP_CAP_100HDX: case ETHER_STAT_LP_CAP_10FDX: case ETHER_STAT_LP_CAP_10HDX: - case ETHER_STAT_LP_CAP_ASMPAUSE: - case ETHER_STAT_LP_CAP_PAUSE: return (ENOTSUP); case ETHER_STAT_LINK_ASMPAUSE: - *val = 0; + *val = (!!(lc->link_caps & FW_PORT_CAP32_FC_TX)) ^ + (!!(lc->link_caps & FW_PORT_CAP32_FC_RX)); break; case ETHER_STAT_LINK_PAUSE: - *val = 1; + *val = !!(lc->link_caps & FW_PORT_CAP32_FC_TX); break; case ETHER_STAT_LINK_AUTONEG: - *val = lc->autoneg == AUTONEG_ENABLE; + *val = !!(lc->link_caps & FW_PORT_CAP32_ANEG); break; case ETHER_STAT_LINK_DUPLEX: @@ -938,52 +1001,112 @@ t4_mc_getcapab(void *arg, mac_capab_t cap, void *data) return (status); } -static link_fec_t -fec_to_link_fec(cc_fec_t cc_fec) +static void t4_mac_link_caps_to_flowctrl(fw_port_cap32_t caps, + link_flowctrl_t *fc) { - link_fec_t link_fec = 0; + u8 pause_tx = 0, pause_rx = 0; - if ((cc_fec & (FEC_RS | FEC_BASER_RS)) == (FEC_RS | FEC_BASER_RS)) - return (LINK_FEC_AUTO); + if (caps & FW_PORT_CAP32_FC_TX) + pause_tx = 1; - if ((cc_fec & FEC_NONE) != 0) - link_fec |= LINK_FEC_NONE; + if (caps & FW_PORT_CAP32_FC_RX) + pause_rx = 1; + + if (pause_rx & pause_tx) + *fc = LINK_FLOWCTRL_BI; + else if (pause_tx) + *fc = LINK_FLOWCTRL_TX; + else if (pause_rx) + *fc = LINK_FLOWCTRL_RX; + else + *fc = LINK_FLOWCTRL_NONE; +} + +static int t4_mac_flowctrl_to_link_caps(struct port_info *pi, + link_flowctrl_t fc, + fw_port_cap32_t *new_caps) +{ + cc_pause_t pause = 0; + + switch (fc) { + case LINK_FLOWCTRL_BI: + pause |= PAUSE_TX | PAUSE_RX; + break; + case LINK_FLOWCTRL_TX: + pause |= PAUSE_TX; + break; + case LINK_FLOWCTRL_RX: + pause |= PAUSE_RX; + break; + default: + break; + } + + if (pi->link_cfg.admin_caps & FW_PORT_CAP32_ANEG) + pause |= PAUSE_AUTONEG; - if ((cc_fec & FEC_AUTO) != 0) - link_fec |= LINK_FEC_AUTO; + return t4_link_set_pause(pi, pause, new_caps); +} - if ((cc_fec & FEC_RS) != 0) +static link_fec_t t4_mac_port_caps_to_fec_cap(fw_port_cap32_t caps) +{ + link_fec_t link_fec = 0; + + if (caps & FW_PORT_CAP32_FEC_RS) link_fec |= LINK_FEC_RS; - if ((cc_fec & FEC_BASER_RS) != 0) + if (caps & FW_PORT_CAP32_FEC_BASER_RS) link_fec |= LINK_FEC_BASE_R; - return (link_fec); + if (caps & FW_PORT_CAP32_FEC_NO_FEC) + link_fec |= LINK_FEC_NONE; + + if ((link_fec & (link_fec - 1)) && + !(caps & FW_PORT_CAP32_FORCE_FEC)) + return LINK_FEC_AUTO; + + return link_fec; } -static int -link_fec_to_fec(int v) +static void t4_mac_admin_caps_to_fec_cap(fw_port_cap32_t caps, + link_fec_t *fec) +{ + *fec = t4_mac_port_caps_to_fec_cap(caps); +} + +static void t4_mac_link_caps_to_fec_cap(fw_port_cap32_t caps, + link_fec_t *fec) { - int fec = 0; + link_fec_t link_fec; - if ((v & LINK_FEC_AUTO) != 0) { + caps &= ~FW_PORT_CAP32_FEC_NO_FEC; + link_fec = t4_mac_port_caps_to_fec_cap(caps); + *fec = link_fec ? link_fec : LINK_FEC_NONE; +} + +static int t4_mac_fec_cap_to_link_caps(struct port_info *pi, link_fec_t v, + fw_port_cap32_t *new_caps) +{ + cc_fec_t fec = 0; + + if (v == LINK_FEC_AUTO) { fec = FEC_AUTO; - v &= ~LINK_FEC_AUTO; - } else { - if ((v & LINK_FEC_NONE) != 0) { - fec = FEC_NONE; - v &= ~LINK_FEC_NONE; - } + goto out; + } - if ((v & LINK_FEC_RS) != 0) { - fec |= FEC_RS; - v &= ~LINK_FEC_RS; - } + if (v & LINK_FEC_NONE) { + v &= ~LINK_FEC_NONE; + fec |= FEC_NONE; + } - if ((v & LINK_FEC_BASE_R) != 0) { - fec |= FEC_BASER_RS; - v &= ~LINK_FEC_BASE_R; - } + if (v & LINK_FEC_RS) { + v &= ~LINK_FEC_RS; + fec |= FEC_RS; + } + + if (v & LINK_FEC_BASE_R) { + v &= ~LINK_FEC_BASE_R; + fec |= FEC_BASER_RS; } if (v != 0) @@ -991,7 +1114,10 @@ link_fec_to_fec(int v) ASSERT3S(fec, !=, 0); - return (fec); + fec |= FEC_FORCE; + +out: + return t4_link_set_fec(pi, fec, new_caps); } /* ARGSUSED */ @@ -1001,49 +1127,18 @@ t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, { struct port_info *pi = arg; struct adapter *sc = pi->adapter; - struct link_config lc_copy, *lc = &pi->link_cfg; - uint8_t v8 = *(uint8_t *)val; + struct link_config *lc = &pi->link_cfg; + fw_port_cap32_t new_caps = lc->admin_caps; + int relink = 0, rx_mode = 0, rc = 0; uint32_t v32 = *(uint32_t *)val; - int old, new = 0, relink = 0, rx_mode = 0, rc = 0; - boolean_t down_link = B_TRUE; + uint8_t v8 = *(uint8_t *)val; link_flowctrl_t fc; link_fec_t fec; - /* - * Save a copy of link_config. This can be used to restore link_config - * if t4_link_l1cfg() fails. - */ - bcopy(lc, &lc_copy, sizeof (struct link_config)); - switch (id) { case MAC_PROP_AUTONEG: - if (lc->pcaps & FW_PORT_CAP32_ANEG) { - old = lc->autoneg; - new = v8 ? AUTONEG_ENABLE : AUTONEG_DISABLE; - if (old != new) { - /* LINTED: E_CONSTANT_CONDITION */ - lc->autoneg = new; - relink = 1; - if (new == AUTONEG_DISABLE) { - /* Only 100M is available */ - lc->speed_caps = - FW_PORT_CAP32_SPEED_100M; - lc->acaps = - FW_PORT_CAP32_SPEED_100M; - } else { - /* - * Advertise autonegotiation capability - * along with supported speeds - */ - lc->acaps |= (FW_PORT_CAP32_ANEG | - (lc->pcaps & - (FW_PORT_CAP32_SPEED_100M | - FW_PORT_CAP32_SPEED_1G))); - lc->speed_caps = 0; - } - } - } else - rc = ENOTSUP; + rc = t4_link_set_autoneg(pi, v8, &new_caps); + relink = 1; break; case MAC_PROP_MTU: @@ -1059,87 +1154,56 @@ t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, case MAC_PROP_FLOWCTRL: fc = *(link_flowctrl_t *)val; - old = lc->requested_fc & (PAUSE_TX | PAUSE_RX); - - if (fc == LINK_FLOWCTRL_BI) - new = (PAUSE_TX | PAUSE_RX); - else if (fc == LINK_FLOWCTRL_TX) - new = PAUSE_TX; - else if (fc == LINK_FLOWCTRL_RX) - new = PAUSE_RX; - - if (new != old) { - lc->requested_fc &= ~(PAUSE_TX | PAUSE_RX); - lc->requested_fc |= new; - relink = 1; - } + rc = t4_mac_flowctrl_to_link_caps(pi, fc, &new_caps); + relink = 1; break; case MAC_PROP_EN_FEC_CAP: - if (!fec_supported(lc->pcaps)) { - rc = ENOTSUP; - break; - } - fec = *(link_fec_t *)val; - new = link_fec_to_fec(fec); - if (new < 0) { - rc = EINVAL; - } else if (new != lc->requested_fec) { - lc->requested_fec = new; - relink = 1; - /* - * For fec, do not preemptively force the link - * down. If changing fec causes the link state - * to transition, then appropriate asynchronous - * events are generated which correctly reflect - * the link state. - */ - down_link = B_FALSE; - } + rc = t4_mac_fec_cap_to_link_caps(pi, fec, &new_caps); + relink = 1; break; - case MAC_PROP_EN_10GFDX_CAP: - if (lc->pcaps & FW_PORT_CAP32_ANEG && is_10G_port(pi)) { - old = lc->acaps & FW_PORT_CAP32_SPEED_10G; - new = v8 ? FW_PORT_CAP32_SPEED_10G : 0; - if (new != old) { - lc->acaps &= ~FW_PORT_CAP32_SPEED_10G; - lc->acaps |= new; - relink = 1; - } - } else - rc = ENOTSUP; + case MAC_PROP_EN_100GFDX_CAP: + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_100G, v8, + &new_caps); + relink = 1; + break; + case MAC_PROP_EN_50GFDX_CAP: + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_50G, v8, + &new_caps); + relink = 1; + break; + + case MAC_PROP_EN_40GFDX_CAP: + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_40G, v8, + &new_caps); + relink = 1; + break; + + case MAC_PROP_EN_25GFDX_CAP: + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_25G, v8, + &new_caps); + relink = 1; + break; + + case MAC_PROP_EN_10GFDX_CAP: + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_10G, v8, + &new_caps); + relink = 1; break; case MAC_PROP_EN_1000FDX_CAP: - /* Forced 1G */ - if (lc->autoneg == AUTONEG_ENABLE) { - old = lc->acaps & FW_PORT_CAP32_SPEED_1G; - new = v8 ? FW_PORT_CAP32_SPEED_1G : 0; - - if (old != new) { - lc->acaps &= ~FW_PORT_CAP32_SPEED_1G; - lc->acaps |= new; - relink = 1; - } - } else - rc = ENOTSUP; + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_1G, v8, + &new_caps); + relink = 1; break; case MAC_PROP_EN_100FDX_CAP: - /* Forced 100M */ - if (lc->autoneg == AUTONEG_ENABLE) { - old = lc->acaps & FW_PORT_CAP32_SPEED_100M; - new = v8 ? FW_PORT_CAP32_SPEED_100M : 0; - if (old != new) { - lc->acaps &= ~FW_PORT_CAP32_SPEED_100M; - lc->acaps |= new; - relink = 1; - } - } else - rc = ENOTSUP; + rc = t4_link_set_speed(pi, FW_PORT_CAP32_SPEED_100M, v8, + &new_caps); + relink = 1; break; case MAC_PROP_PRIVATE: @@ -1148,25 +1212,25 @@ t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, default: rc = ENOTSUP; + break; } + if (rc != 0) + return (rc); + if (isset(&sc->open_device_map, pi->port_id) != 0) { if (relink != 0) { - if (down_link) - t4_os_link_changed(pi->adapter, pi->port_id, 0); rc = begin_synchronized_op(pi, 1, 1); if (rc != 0) return (rc); - rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, - &pi->link_cfg); + rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc, + new_caps); end_synchronized_op(pi, 1); if (rc != 0) { cxgb_printf(pi->dip, CE_WARN, - "start_link failed:%d", rc); - - /* Restore link_config */ - bcopy(&lc_copy, lc, - sizeof (struct link_config)); + "%s link config failed: %d", + __func__, rc); + return (rc); } } @@ -1180,11 +1244,15 @@ t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, if (rc != 0) { cxgb_printf(pi->dip, CE_WARN, "set_rxmode failed: %d", rc); + return (rc); } } } - return (rc); + if (relink != 0) + lc->admin_caps = new_caps; + + return (0); } static int @@ -1194,6 +1262,7 @@ t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, struct port_info *pi = arg; struct link_config *lc = &pi->link_cfg; uint8_t *u = val; + int rc = 0; switch (id) { case MAC_PROP_DUPLEX: @@ -1203,10 +1272,11 @@ t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, case MAC_PROP_SPEED: if (lc->link_ok != 0) { - *(uint64_t *)val = lc->speed; + *(uint64_t *)val = t4_link_fwcap_to_speed(lc->link_caps); *(uint64_t *)val *= 1000000; - } else + } else { *(uint64_t *)val = 0; + } break; case MAC_PROP_STATUS: @@ -1215,7 +1285,7 @@ t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, break; case MAC_PROP_AUTONEG: - *u = lc->autoneg == AUTONEG_ENABLE; + *u = !!(lc->link_caps & FW_PORT_CAP32_ANEG); break; case MAC_PROP_MTU: @@ -1223,59 +1293,71 @@ t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, break; case MAC_PROP_FLOWCTRL: - if ((lc->requested_fc & (PAUSE_TX | PAUSE_RX)) == - (PAUSE_TX | PAUSE_RX)) - *(link_flowctrl_t *)val = LINK_FLOWCTRL_BI; - else if (lc->requested_fc & PAUSE_TX) - *(link_flowctrl_t *)val = LINK_FLOWCTRL_TX; - else if (lc->requested_fc & PAUSE_RX) - *(link_flowctrl_t *)val = LINK_FLOWCTRL_RX; - else - *(link_flowctrl_t *)val = LINK_FLOWCTRL_NONE; + t4_mac_link_caps_to_flowctrl(lc->link_caps, val); break; case MAC_PROP_ADV_FEC_CAP: - if (!fec_supported(lc->pcaps)) - return (ENOTSUP); - - *(link_fec_t *)val = fec_to_link_fec(lc->fec); + t4_mac_link_caps_to_fec_cap(lc->link_caps, val); break; case MAC_PROP_EN_FEC_CAP: - if (!fec_supported(lc->pcaps)) - return (ENOTSUP); - - *(link_fec_t *)val = fec_to_link_fec(lc->requested_fec); + t4_mac_admin_caps_to_fec_cap(lc->admin_caps, val); break; case MAC_PROP_ADV_100GFDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_100G); + break; + case MAC_PROP_EN_100GFDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_100G); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_100G); + break; + + case MAC_PROP_ADV_50GFDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_50G); + break; + + case MAC_PROP_EN_50GFDX_CAP: + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_50G); break; case MAC_PROP_ADV_40GFDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_40G); + break; + case MAC_PROP_EN_40GFDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_40G); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_40G); break; case MAC_PROP_ADV_25GFDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_25G); + break; + case MAC_PROP_EN_25GFDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_25G); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_25G); break; case MAC_PROP_ADV_10GFDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_10G); + break; + case MAC_PROP_EN_10GFDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_10G); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_10G); break; case MAC_PROP_ADV_1000FDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_1G); + break; + case MAC_PROP_EN_1000FDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_1G); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_1G); break; case MAC_PROP_ADV_100FDX_CAP: + *u = !!(lc->link_caps & FW_PORT_CAP32_SPEED_100M); + break; + case MAC_PROP_EN_100FDX_CAP: - *u = !!(lc->acaps & FW_PORT_CAP32_SPEED_100M); + *u = !!(lc->admin_caps & FW_PORT_CAP32_SPEED_100M); break; case MAC_PROP_PRIVATE: @@ -1285,7 +1367,7 @@ t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size, return (ENOTSUP); } - return (0); + return (rc); } static void @@ -1326,30 +1408,59 @@ t4_mc_propinfo(void *arg, const char *name, mac_prop_id_t id, mac_prop_info_set_default_fec(ph, LINK_FEC_AUTO); break; + case MAC_PROP_EN_100GFDX_CAP: + if (lc->pcaps & FW_PORT_CAP32_SPEED_100G) + mac_prop_info_set_default_uint8(ph, 1); + else + mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); + break; + + case MAC_PROP_EN_50GFDX_CAP: + if (lc->pcaps & FW_PORT_CAP32_SPEED_50G) + mac_prop_info_set_default_uint8(ph, 1); + else + mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); + break; + + case MAC_PROP_EN_40GFDX_CAP: + if (lc->pcaps & FW_PORT_CAP32_SPEED_40G) + mac_prop_info_set_default_uint8(ph, 1); + else + mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); + break; + + case MAC_PROP_EN_25GFDX_CAP: + if (lc->pcaps & FW_PORT_CAP32_SPEED_25G) + mac_prop_info_set_default_uint8(ph, 1); + else + mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); + break; + case MAC_PROP_EN_10GFDX_CAP: - if (lc->pcaps & FW_PORT_CAP32_ANEG && - lc->pcaps & FW_PORT_CAP32_SPEED_10G) + if (lc->pcaps & FW_PORT_CAP32_SPEED_10G) mac_prop_info_set_default_uint8(ph, 1); else mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); break; case MAC_PROP_EN_1000FDX_CAP: - if (lc->pcaps & FW_PORT_CAP32_ANEG && - lc->pcaps & FW_PORT_CAP32_SPEED_1G) + if (lc->pcaps & FW_PORT_CAP32_SPEED_1G) mac_prop_info_set_default_uint8(ph, 1); else mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); break; case MAC_PROP_EN_100FDX_CAP: - if (lc->pcaps & FW_PORT_CAP32_ANEG && - lc->pcaps & FW_PORT_CAP32_SPEED_100M) + if (lc->pcaps & FW_PORT_CAP32_SPEED_100M) mac_prop_info_set_default_uint8(ph, 1); else mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ); break; + case MAC_PROP_ADV_100GFDX_CAP: + case MAC_PROP_ADV_50GFDX_CAP: + case MAC_PROP_ADV_40GFDX_CAP: + case MAC_PROP_ADV_25GFDX_CAP: case MAC_PROP_ADV_10GFDX_CAP: case MAC_PROP_ADV_1000FDX_CAP: case MAC_PROP_ADV_100FDX_CAP: @@ -1451,7 +1562,8 @@ t4_init_synchronized(struct port_info *pi) /* LINTED: E_ASSIGN_NARROW_CONV */ pi->xact_addr_filt = rc; - rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, &pi->link_cfg); + rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, &pi->link_cfg, + pi->link_cfg.admin_caps); if (rc != 0) { cxgb_printf(pi->dip, CE_WARN, "start_link failed: %d", rc); goto done; @@ -1501,7 +1613,6 @@ t4_uninit_synchronized(struct port_info *pi) clrbit(&sc->open_device_map, pi->port_id); pi->link_cfg.link_ok = 0; - pi->link_cfg.speed = 0; mac_link_update(pi->mh, LINK_STATE_UNKNOWN); return (0); @@ -1525,9 +1636,9 @@ propinfo(struct port_info *pi, const char *name, mac_prop_info_handle_t ph) else if (strcmp(name, T4PROP_HW_LSO) == 0) v = (pi->features & CXGBE_HW_LSO) ? 1 : 0; else if (strcmp(name, T4PROP_TX_PAUSE) == 0) - v = (lc->fc & PAUSE_TX) ? 1 : 0; + v = (lc->pcaps & FW_PORT_CAP32_FC_TX) ? 1 : 0; else if (strcmp(name, T4PROP_RX_PAUSE) == 0) - v = (lc->fc & PAUSE_RX) ? 1 : 0; + v = (lc->pcaps & FW_PORT_CAP32_FC_RX) ? 1 : 0; #if MAC_VERSION == 1 else if (strcmp(name, T4PROP_MTU) == 0) v = ETHERMTU; @@ -1554,9 +1665,9 @@ getprop(struct port_info *pi, const char *name, uint_t size, void *val) else if (strcmp(name, T4PROP_HW_LSO) == 0) v = (pi->features & CXGBE_HW_LSO) ? 1 : 0; else if (strcmp(name, T4PROP_TX_PAUSE) == 0) - v = (lc->fc & PAUSE_TX) ? 1 : 0; + v = (lc->link_caps & FW_PORT_CAP32_FC_TX) ? 1 : 0; else if (strcmp(name, T4PROP_RX_PAUSE) == 0) - v = (lc->fc & PAUSE_RX) ? 1 : 0; + v = (lc->link_caps & FW_PORT_CAP32_FC_RX) ? 1 : 0; #if MAC_VERSION == 1 else if (strcmp(name, T4PROP_MTU) == 0) v = pi->mtu; @@ -1571,17 +1682,13 @@ getprop(struct port_info *pi, const char *name, uint_t size, void *val) static int setprop(struct port_info *pi, const char *name, const void *val) { - struct adapter *sc = pi->adapter; - long v; + struct link_config *lc = &pi->link_cfg; + fw_port_cap32_t new_caps = lc->admin_caps; int i, rc = 0, relink = 0, rx_mode = 0; + struct adapter *sc = pi->adapter; struct sge_rxq *rxq; - struct link_config lc_old, *lc = &pi->link_cfg; - - /* - * Save a copy of link_config. This can be used to restore link_config - * if t4_link_l1cfg() fails. - */ - bcopy(lc, &lc_old, sizeof (struct link_config)); + cc_pause_t fc = 0; + long v; (void) ddi_strtol(val, NULL, 0, &v); @@ -1631,22 +1738,28 @@ setprop(struct port_info *pi, const char *name, const void *val) if (v != 0 && v != 1) return (EINVAL); - if (v != 0) - lc->requested_fc |= PAUSE_TX; - else - lc->requested_fc &= ~PAUSE_TX; + if ((new_caps & FW_PORT_CAP32_FC_TX) && (v == 1)) + fc |= PAUSE_TX; + if (new_caps & FW_PORT_CAP32_FC_RX) + fc |= PAUSE_RX; + if (lc->admin_caps & FW_PORT_CAP32_ANEG) + fc |= PAUSE_AUTONEG; + t4_link_set_pause(pi, fc, &new_caps); relink = 1; } else if (strcmp(name, T4PROP_RX_PAUSE) == 0) { if (v != 0 && v != 1) return (EINVAL); - if (v != 0) - lc->requested_fc |= PAUSE_RX; - else - lc->requested_fc &= ~PAUSE_RX; + if (new_caps & FW_PORT_CAP32_FC_TX) + fc |= PAUSE_TX; + if ((new_caps & FW_PORT_CAP32_FC_RX) && (v == 1)) + fc |= PAUSE_RX; + if (lc->admin_caps & FW_PORT_CAP32_ANEG) + fc |= PAUSE_AUTONEG; + t4_link_set_pause(pi, fc, &new_caps); relink = 1; } #if MAC_VERSION == 1 @@ -1673,13 +1786,14 @@ setprop(struct port_info *pi, const char *name, const void *val) rc = begin_synchronized_op(pi, 1, 1); if (rc != 0) return (rc); - rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc); + rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc, + new_caps); end_synchronized_op(pi, 1); if (rc != 0) { cxgb_printf(pi->dip, CE_WARN, - "start_link failed:%d", rc); - /* Restore link_config */ - bcopy(&lc_old, lc, sizeof (struct link_config)); + "%s link config failed: %d", + __func__, rc); + return (rc); } } else if (rx_mode != 0) { rc = begin_synchronized_op(pi, 1, 1); @@ -1690,12 +1804,15 @@ setprop(struct port_info *pi, const char *name, const void *val) end_synchronized_op(pi, 1); if (rc != 0) { cxgb_printf(pi->dip, CE_WARN, - "set_rxmode failed: %d", rc); + "set_rxmode failed: %d", rc); + return (rc); } } - return (rc); } + if (relink != 0) + lc->admin_caps = new_caps; + return (0); } diff --git a/usr/src/uts/common/io/cxgbe/t4nex/t4_nexus.c b/usr/src/uts/common/io/cxgbe/t4nex/t4_nexus.c index 809005878c..894950c2e7 100644 --- a/usr/src/uts/common/io/cxgbe/t4nex/t4_nexus.c +++ b/usr/src/uts/common/io/cxgbe/t4nex/t4_nexus.c @@ -342,6 +342,8 @@ t4_devo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) cv_init(&sc->cv, NULL, CV_DRIVER, NULL); mutex_init(&sc->sfl_lock, NULL, MUTEX_DRIVER, NULL); TAILQ_INIT(&sc->sfl); + mutex_init(&sc->mbox_lock, NULL, MUTEX_DRIVER, NULL); + STAILQ_INIT(&sc->mbox_list); mutex_enter(&t4_adapter_list_lock); SLIST_INSERT_HEAD(&t4_adapter_list, sc, link); @@ -974,9 +976,10 @@ t4_devo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) pci_config_teardown(&sc->pci_regh); mutex_enter(&t4_adapter_list_lock); - SLIST_REMOVE_HEAD(&t4_adapter_list, link); + SLIST_REMOVE(&t4_adapter_list, sc, adapter, link); mutex_exit(&t4_adapter_list_lock); + mutex_destroy(&sc->mbox_lock); mutex_destroy(&sc->lock); cv_destroy(&sc->cv); mutex_destroy(&sc->sfl_lock); @@ -2334,6 +2337,26 @@ done: return (rc); } +static char * +print_port_speed(const struct port_info *pi) +{ + if (!pi) + return "-"; + + if (is_100G_port(pi)) + return "100G"; + else if (is_50G_port(pi)) + return "50G"; + else if (is_40G_port(pi)) + return "40G"; + else if (is_25G_port(pi)) + return "25G"; + else if (is_10G_port(pi)) + return "10G"; + else + return "1G"; +} + #define KS_UINIT(x) kstat_named_init(&kstatp->x, #x, KSTAT_DATA_ULONG) #define KS_CINIT(x) kstat_named_init(&kstatp->x, #x, KSTAT_DATA_CHAR) #define KS_U_SET(x, y) kstatp->x.value.ul = (y) @@ -2735,12 +2758,12 @@ t4_os_find_pci_capability(struct adapter *sc, int cap) } void -t4_os_portmod_changed(const struct adapter *sc, int idx) +t4_os_portmod_changed(struct adapter *sc, int idx) { static const char *mod_str[] = { NULL, "LR", "SR", "ER", "TWINAX", "active TWINAX", "LRM" }; - const struct port_info *pi = sc->port[idx]; + struct port_info *pi = sc->port[idx]; if (pi->mod_type == FW_PORT_MOD_TYPE_NONE) cxgb_printf(pi->dip, CE_NOTE, "transceiver unplugged."); @@ -2756,6 +2779,10 @@ t4_os_portmod_changed(const struct adapter *sc, int idx) else cxgb_printf(pi->dip, CE_NOTE, "transceiver (type %d) inserted.", pi->mod_type); + + if ((isset(&sc->open_device_map, pi->port_id) != 0) && + pi->link_cfg.new_module) + pi->link_cfg.redo_l1cfg = true; } /* ARGSUSED */ -- cgit v1.2.3