The branch main has been updated by np: URL: https://cgit.FreeBSD.org/src/commit/?id=448bcd01dc5edcbbf23975588f131e13cd09778f
commit 448bcd01dc5edcbbf23975588f131e13cd09778f Author: Navdeep Parhar <n...@freebsd.org> AuthorDate: 2021-11-10 19:38:54 +0000 Commit: Navdeep Parhar <n...@freebsd.org> CommitDate: 2021-11-10 23:16:53 +0000 cxgbe(4): internal knob for flexible control over FEC selection. Recent firmwares have support for autonomous FEC selection and a "force" knob to let the driver control this behavior (or not) in a fine grained manner. This change adds a driver knob so that all the different ways of configuring the link FEC can be exercised. Note that this controls the internal driver/firmware interaction for link configuration and is not meant for general use. MFC after: 1 week Sponsored by: Chelsio Communications --- sys/dev/cxgbe/common/common.h | 2 ++ sys/dev/cxgbe/common/t4_hw.c | 30 +++++++++++++++++----- sys/dev/cxgbe/t4_main.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h index 50859e868b9d..bee4f58f693c 100644 --- a/sys/dev/cxgbe/common/common.h +++ b/sys/dev/cxgbe/common/common.h @@ -443,9 +443,11 @@ struct link_config { int8_t requested_aneg; /* link autonegotiation */ int8_t requested_fc; /* flow control */ int8_t requested_fec; /* FEC */ + int8_t force_fec; /* FORCE_FEC in L1_CFG32 command. */ u_int requested_speed; /* speed (Mbps) */ uint32_t requested_caps;/* rcap in last l1cfg issued by the driver. */ + /* These are populated with information from the firmware. */ uint32_t pcaps; /* link capabilities */ uint32_t acaps; /* advertised capabilities */ uint32_t lpacaps; /* peer advertised capabilities */ diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index b9bf5df5ccc6..d6f85a1fcd34 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -3917,9 +3917,19 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, speed = fwcap_top_speed(lc->pcaps); fec = 0; +#ifdef INVARIANTS + if (lc->force_fec != 0) + MPASS(lc->pcaps & FW_PORT_CAP32_FORCE_FEC); +#endif if (fec_supported(speed)) { if (lc->requested_fec == FEC_AUTO) { - if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) { + if (lc->force_fec > 0) { + /* + * Must use FORCE_FEC even though requested FEC + * is AUTO. Set all the FEC bits valid for the + * speed and let the firmware pick one. + */ + fec |= FW_PORT_CAP32_FORCE_FEC; if (speed & FW_PORT_CAP32_SPEED_100G) { fec |= FW_PORT_CAP32_FEC_RS; fec |= FW_PORT_CAP32_FEC_NO_FEC; @@ -3929,20 +3939,26 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, fec |= FW_PORT_CAP32_FEC_NO_FEC; } } else { - /* Set only 1b with old firmwares. */ + /* + * Set only 1b. Old firmwares can't deal with + * multiple bits and new firmwares are free to + * ignore this and try whatever FECs they want + * because we aren't setting FORCE_FEC here. + */ fec |= fec_to_fwcap(lc->fec_hint); } } else { + /* + * User has explicitly requested some FEC(s). Set + * FORCE_FEC unless prohibited from using it. + */ + if (lc->force_fec != 0) + fec |= FW_PORT_CAP32_FORCE_FEC; fec |= fec_to_fwcap(lc->requested_fec & M_FW_PORT_CAP32_FEC); if (lc->requested_fec & FEC_MODULE) fec |= fec_to_fwcap(lc->fec_hint); } - - if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) - fec |= FW_PORT_CAP32_FORCE_FEC; - else if (fec == FW_PORT_CAP32_FEC_NO_FEC) - fec = 0; } /* Force AN on for BT cards. */ diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index b8e4ff7e9c0f..44a9a1343016 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -517,6 +517,21 @@ static int t4_fec = -1; SYSCTL_INT(_hw_cxgbe, OID_AUTO, fec, CTLFLAG_RDTUN, &t4_fec, 0, "Forward Error Correction (bit 0 = RS, bit 1 = BASER_RS)"); +/* + * Controls when the driver sets the FORCE_FEC bit in the L1_CFG32 that it + * issues to the firmware. If the firmware doesn't support FORCE_FEC then the + * driver runs as if this is set to 0. + * -1 to set FORCE_FEC iff requested_fec != AUTO. Multiple FEC bits are okay. + * 0 to never set FORCE_FEC. requested_fec = AUTO means use the hint from the + * transceiver. Multiple FEC bits may not be okay but will be passed on to + * the firmware anyway (may result in l1cfg errors with old firmwares). + * 1 to always set FORCE_FEC. Multiple FEC bits are okay. requested_fec = AUTO + * means set all FEC bits that are valid for the speed. + */ +static int t4_force_fec = -1; +SYSCTL_INT(_hw_cxgbe, OID_AUTO, force_fec, CTLFLAG_RDTUN, &t4_force_fec, 0, + "Controls the use of FORCE_FEC bit in L1 configuration."); + /* * Link autonegotiation. * -1 to run with the firmware default. @@ -775,6 +790,7 @@ static int sysctl_link_fec(SYSCTL_HANDLER_ARGS); static int sysctl_requested_fec(SYSCTL_HANDLER_ARGS); static int sysctl_module_fec(SYSCTL_HANDLER_ARGS); static int sysctl_autoneg(SYSCTL_HANDLER_ARGS); +static int sysctl_force_fec(SYSCTL_HANDLER_ARGS); static int sysctl_handle_t4_reg64(SYSCTL_HANDLER_ARGS); static int sysctl_temperature(SYSCTL_HANDLER_ARGS); static int sysctl_vdd(SYSCTL_HANDLER_ARGS); @@ -5703,6 +5719,7 @@ init_link_config(struct port_info *pi) struct link_config *lc = &pi->link_cfg; PORT_LOCK_ASSERT_OWNED(pi); + MPASS(lc->pcaps != 0); lc->requested_caps = 0; lc->requested_speed = 0; @@ -5728,6 +5745,13 @@ init_link_config(struct port_info *pi) if (lc->requested_fec == 0) lc->requested_fec = FEC_AUTO; } + lc->force_fec = 0; + if (lc->pcaps & FW_PORT_CAP32_FORCE_FEC) { + if (t4_force_fec < 0) + lc->force_fec = -1; + else if (t4_force_fec > 0) + lc->force_fec = 1; + } } /* @@ -7774,6 +7798,9 @@ cxgbe_sysctls(struct port_info *pi) CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, pi, 0, sysctl_autoneg, "I", "autonegotiation (-1 = not supported)"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "force_fec", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, pi, 0, + sysctl_force_fec, "I", "when to use FORCE_FEC bit for link config"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rcaps", CTLFLAG_RD, &pi->link_cfg.requested_caps, 0, "L1 config requested by driver"); @@ -8492,6 +8519,39 @@ done: return (rc); } +static int +sysctl_force_fec(SYSCTL_HANDLER_ARGS) +{ + struct port_info *pi = arg1; + struct adapter *sc = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int rc, val; + + val = lc->force_fec; + MPASS(val >= -1 && val <= 1); + rc = sysctl_handle_int(oidp, &val, 0, req); + if (rc != 0 || req->newptr == NULL) + return (rc); + if (!(lc->pcaps & FW_PORT_CAP32_FORCE_FEC)) + return (ENOTSUP); + if (val < -1 || val > 1) + return (EINVAL); + + rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK, "t4ff"); + if (rc) + return (rc); + PORT_LOCK(pi); + lc->force_fec = val; + if (!hw_off_limits(sc)) { + fixup_link_config(pi); + if (pi->up_vis > 0) + rc = apply_link_config(pi); + } + PORT_UNLOCK(pi); + end_synchronized_op(sc, 0); + return (rc); +} + static int sysctl_handle_t4_reg64(SYSCTL_HANDLER_ARGS) {