cxgbe(4): Link related changes.

- Switch to using 32b port/link capabilities in the driver.  The 32b
  format is used internally by firmwares > 1.16.45.0 and the driver will
  now interact with the firmware in its native format, whether it's 16b
  or 32b.  Note that the 16b format doesn't have room for 50G, 200G, or
  400G speeds.

- Add a bit in the pause_settings knobs to allow negotiated PAUSE
  settings to override manual settings.

- Ensure that manual link settings persist across an administrative
  down/up as well as transceiver unplug/replug.

- Remove unused is_*G_port() functions.

Approved by:	re@ (gjb@)
MFC after:	1 month
Sponsored by:	Chelsio Communications
This commit is contained in:
np 2018-09-25 05:52:42 +00:00
parent 0b4a4336f2
commit eb5896f52d
7 changed files with 700 additions and 372 deletions

View File

@ -31,7 +31,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd Aug 9, 2018
.Dd Sep 24, 2018
.Dt CXGBE 4
.Os
.Sh NAME
@ -279,19 +279,21 @@ This usually results in the port emitting PAUSE frames.
1 instructs the hardware to drop frames destined for congested queues.
.It Va hw.cxgbe.pause_settings
PAUSE frame settings.
Bit 0 is rx_pause, bit 1 is tx_pause.
Bit 0 is rx_pause, bit 1 is tx_pause, bit 2 is pause_autoneg.
rx_pause = 1 instructs the hardware to heed incoming PAUSE frames, 0 instructs
it to ignore them.
tx_pause = 1 allows the hardware to emit PAUSE frames when its receive FIFO
reaches a high threshold, 0 prohibits the hardware from emitting PAUSE frames.
The default is 3 (both rx_pause and tx_pause = 1).
pause_autoneg = 1 overrides the rx_pause and tx_pause bits and instructs the
hardware to negotiate PAUSE settings with the link peer.
The default is 7 (all three = 1).
This tunable establishes the default PAUSE settings for all ports.
Settings can be displayed and controlled on a per-port basis via the
dev.<port>.X.pause_settings sysctl.
.It Va hw.cxgbe.fec
FEC (Forward Error Correction) settings.
0 diables FEC.
Bit 0 enables RS FEC, bit 1 enables BASE-R RS, bit 3 is reserved.
Bit 0 enables RS FEC, bit 1 enables BASE-R FEC (aka Firecode FEC).
The default is -1 which lets the driver pick a value.
This tunable establishes the default FEC settings for all ports.
Settings can be displayed and controlled on a per-port basis via the

View File

@ -289,7 +289,6 @@ struct port_info {
uint8_t rx_e_chan_map; /* rx TP e-channel bitmap */
struct link_config link_cfg;
struct link_config old_link_cfg;
struct ifmedia media;
struct timeval last_refreshed;
@ -1073,52 +1072,6 @@ t4_os_set_hw_addr(struct port_info *pi, uint8_t hw_addr[])
bcopy(hw_addr, pi->vi[0].hw_addr, ETHER_ADDR_LEN);
}
static inline bool
is_10G_port(const struct port_info *pi)
{
return ((pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G) != 0);
}
static inline bool
is_25G_port(const struct port_info *pi)
{
return ((pi->link_cfg.supported & FW_PORT_CAP_SPEED_25G) != 0);
}
static inline bool
is_40G_port(const struct port_info *pi)
{
return ((pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G) != 0);
}
static inline bool
is_100G_port(const struct port_info *pi)
{
return ((pi->link_cfg.supported & FW_PORT_CAP_SPEED_100G) != 0);
}
static inline int
port_top_speed(const struct port_info *pi)
{
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100G)
return (100);
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G)
return (40);
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_25G)
return (25);
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G)
return (10);
if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_1G)
return (1);
return (0);
}
static inline int
tx_resume_threshold(struct sge_eq *eq)
{

View File

@ -66,9 +66,10 @@ enum {
};
enum {
FEC_NONE = 0,
FEC_RS = 1 << 0,
FEC_BASER_RS = 1 << 1,
FEC_RESERVED = 1 << 2,
FEC_AUTO = 1 << 5, /* M_FW_PORT_CAP32_FEC + 1 */
};
enum t4_bar2_qtype { T4_BAR2_QTYPE_EGRESS, T4_BAR2_QTYPE_INGRESS };
@ -368,6 +369,7 @@ struct adapter_params {
unsigned int ethoffload:1;
unsigned int hash_filter:1;
unsigned int filter2_wr_support:1;
unsigned int port_caps32:1;
unsigned int ofldq_wr_cred;
unsigned int eo_wr_cred;
@ -409,20 +411,21 @@ struct trace_params {
};
struct link_config {
/* OS-specific code owns all the requested_* fields */
unsigned char requested_aneg; /* link aneg user has requested */
unsigned char requested_fc; /* flow control user has requested */
unsigned char requested_fec; /* FEC user has requested */
unsigned int requested_speed; /* speed user has requested (Mbps) */
/* OS-specific code owns all the requested_* fields. */
int8_t requested_aneg; /* link autonegotiation */
int8_t requested_fc; /* flow control */
int8_t requested_fec; /* FEC */
u_int requested_speed; /* speed (Mbps) */
unsigned short supported; /* link capabilities */
unsigned short advertising; /* advertised capabilities */
unsigned short lp_advertising; /* peer advertised capabilities */
unsigned int speed; /* actual link speed (Mbps) */
unsigned char fc; /* actual link flow control */
unsigned char fec; /* actual FEC */
unsigned char link_ok; /* link up? */
unsigned char link_down_rc; /* link down reason */
uint32_t supported; /* link capabilities */
uint32_t advertising; /* advertised capabilities */
uint32_t lp_advertising; /* peer advertised capabilities */
uint32_t fec_hint; /* use this fec */
u_int speed; /* actual link speed (Mbps) */
int8_t fc; /* actual link flow control */
int8_t fec; /* actual FEC */
bool link_ok; /* link up? */
uint8_t link_down_rc; /* link down reason */
};
#include "adapter.h"
@ -874,5 +877,16 @@ int t4vf_prep_adapter(struct adapter *adapter);
int t4_bar2_sge_qregs(struct adapter *adapter, unsigned int qid,
enum t4_bar2_qtype qtype, int user, u64 *pbar2_qoffset,
unsigned int *pbar2_qid);
unsigned int fwcap_to_speed(uint32_t caps);
uint32_t speed_to_fwcap(unsigned int speed);
uint32_t fwcap_top_speed(uint32_t caps);
static inline int
port_top_speed(const struct port_info *pi)
{
/* Mbps -> Gbps */
return (fwcap_to_speed(pi->link_cfg.supported) / 1000);
}
#endif /* __CHELSIO_COMMON_H */

View File

@ -3755,6 +3755,93 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf)
}
}
/**
* 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 uint32_t fwcaps16_to_caps32(uint16_t caps16)
{
uint32_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 uint16_t fwcaps32_to_caps16(uint32_t caps32)
{
uint16_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;
}
static bool
is_bt(struct port_info *pi)
{
return (pi->port_type == FW_PORT_TYPE_BT_SGMII ||
pi->port_type == FW_PORT_TYPE_BT_XFI ||
pi->port_type == FW_PORT_TYPE_BT_XAUI);
}
/**
* t4_link_l1cfg - apply link configuration to MAC/PHY
* @phy: the PHY to setup
@ -3772,53 +3859,45 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
struct link_config *lc)
{
struct fw_port_cmd c;
unsigned int mdi = V_FW_PORT_CAP_MDI(FW_PORT_CAP_MDI_AUTO);
unsigned int mdi = V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO);
unsigned int aneg, fc, fec, speed, rcap;
fc = 0;
if (lc->requested_fc & PAUSE_RX)
fc |= FW_PORT_CAP_FC_RX;
fc |= FW_PORT_CAP32_FC_RX;
if (lc->requested_fc & PAUSE_TX)
fc |= FW_PORT_CAP_FC_TX;
fc |= FW_PORT_CAP32_FC_TX;
if (!(lc->requested_fc & PAUSE_AUTONEG))
fc |= FW_PORT_CAP32_FORCE_PAUSE;
fec = 0;
if (lc->requested_fec & FEC_RS)
fec = FW_PORT_CAP_FEC_RS;
else if (lc->requested_fec & FEC_BASER_RS)
fec = FW_PORT_CAP_FEC_BASER_RS;
if (!(lc->supported & FW_PORT_CAP_ANEG) ||
lc->requested_aneg == AUTONEG_DISABLE) {
aneg = 0;
switch (lc->requested_speed) {
case 100000:
speed = FW_PORT_CAP_SPEED_100G;
break;
case 40000:
speed = FW_PORT_CAP_SPEED_40G;
break;
case 25000:
speed = FW_PORT_CAP_SPEED_25G;
break;
case 10000:
speed = FW_PORT_CAP_SPEED_10G;
break;
case 1000:
speed = FW_PORT_CAP_SPEED_1G;
break;
case 100:
speed = FW_PORT_CAP_SPEED_100M;
break;
default:
return -EINVAL;
break;
}
} else {
aneg = FW_PORT_CAP_ANEG;
speed = lc->supported &
V_FW_PORT_CAP_SPEED(M_FW_PORT_CAP_SPEED);
if (lc->requested_fec == FEC_AUTO)
fec = lc->fec_hint;
else {
if (lc->requested_fec & FEC_RS)
fec |= FW_PORT_CAP32_FEC_RS;
if (lc->requested_fec & FEC_BASER_RS)
fec |= FW_PORT_CAP32_FEC_BASER_RS;
}
if (lc->requested_aneg == AUTONEG_DISABLE)
aneg = 0;
else if (lc->requested_aneg == AUTONEG_ENABLE)
aneg = FW_PORT_CAP32_ANEG;
else
aneg = lc->supported & FW_PORT_CAP32_ANEG;
if (aneg) {
speed = lc->supported & V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
} else if (lc->requested_speed != 0)
speed = speed_to_fwcap(lc->requested_speed);
else
speed = fwcap_top_speed(lc->supported);
/* Force AN on for BT cards. */
if (is_bt(adap->port[port]))
aneg = lc->supported & FW_PORT_CAP32_ANEG;
rcap = aneg | speed | fc | fec;
if ((rcap | lc->supported) != lc->supported) {
#ifdef INVARIANTS
@ -3833,10 +3912,17 @@ int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
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_PORT_ACTION_L1_CFG) |
if (adap->params.port_caps32) {
c.action_to_len16 =
cpu_to_be32(V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG32) |
FW_LEN16(c));
c.u.l1cfg32.rcap32 = cpu_to_be32(rcap);
} else {
c.action_to_len16 =
cpu_to_be32(V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG) |
FW_LEN16(c));
c.u.l1cfg.rcap = cpu_to_be32(rcap);
c.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap));
}
return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL);
}
@ -7735,57 +7821,206 @@ const char *t4_link_down_rc_str(unsigned char link_down_rc)
return reason[link_down_rc];
}
/*
* Return the highest speed set in the port capabilities, in Mb/s.
*/
unsigned int fwcap_to_speed(uint32_t caps)
{
#define TEST_SPEED_RETURN(__caps_speed, __speed) \
do { \
if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \
return __speed; \
} while (0)
TEST_SPEED_RETURN(400G, 400000);
TEST_SPEED_RETURN(200G, 200000);
TEST_SPEED_RETURN(100G, 100000);
TEST_SPEED_RETURN(50G, 50000);
TEST_SPEED_RETURN(40G, 40000);
TEST_SPEED_RETURN(25G, 25000);
TEST_SPEED_RETURN(10G, 10000);
TEST_SPEED_RETURN(1G, 1000);
TEST_SPEED_RETURN(100M, 100);
#undef TEST_SPEED_RETURN
return 0;
}
/*
* Return the port capabilities bit for the given speed, which is in Mb/s.
*/
uint32_t speed_to_fwcap(unsigned int speed)
{
#define TEST_SPEED_RETURN(__caps_speed, __speed) \
do { \
if (speed == __speed) \
return FW_PORT_CAP32_SPEED_##__caps_speed; \
} while (0)
TEST_SPEED_RETURN(400G, 400000);
TEST_SPEED_RETURN(200G, 200000);
TEST_SPEED_RETURN(100G, 100000);
TEST_SPEED_RETURN(50G, 50000);
TEST_SPEED_RETURN(40G, 40000);
TEST_SPEED_RETURN(25G, 25000);
TEST_SPEED_RETURN(10G, 10000);
TEST_SPEED_RETURN(1G, 1000);
TEST_SPEED_RETURN(100M, 100);
#undef TEST_SPEED_RETURN
return 0;
}
/*
* Return the port capabilities bit for the highest speed in the capabilities.
*/
uint32_t fwcap_top_speed(uint32_t caps)
{
#define TEST_SPEED_RETURN(__caps_speed) \
do { \
if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \
return FW_PORT_CAP32_SPEED_##__caps_speed; \
} while (0)
TEST_SPEED_RETURN(400G);
TEST_SPEED_RETURN(200G);
TEST_SPEED_RETURN(100G);
TEST_SPEED_RETURN(50G);
TEST_SPEED_RETURN(40G);
TEST_SPEED_RETURN(25G);
TEST_SPEED_RETURN(10G);
TEST_SPEED_RETURN(1G);
TEST_SPEED_RETURN(100M);
#undef TEST_SPEED_RETURN
return 0;
}
/**
* lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities
* @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value
*
* Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new
* 32-bit Port Capabilities value.
*/
static uint32_t lstatus_to_fwcap(u32 lstatus)
{
uint32_t linkattr = 0;
/*
* Unfortunately the format of the Link Status in the old
* 16-bit Port Information message isn't the same as the
* 16-bit Port Capabilities bitfield used everywhere else ...
*/
if (lstatus & F_FW_PORT_CMD_RXPAUSE)
linkattr |= FW_PORT_CAP32_FC_RX;
if (lstatus & F_FW_PORT_CMD_TXPAUSE)
linkattr |= FW_PORT_CAP32_FC_TX;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M))
linkattr |= FW_PORT_CAP32_SPEED_100M;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G))
linkattr |= FW_PORT_CAP32_SPEED_1G;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G))
linkattr |= FW_PORT_CAP32_SPEED_10G;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_25G))
linkattr |= FW_PORT_CAP32_SPEED_25G;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_40G))
linkattr |= FW_PORT_CAP32_SPEED_40G;
if (lstatus & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100G))
linkattr |= FW_PORT_CAP32_SPEED_100G;
return linkattr;
}
/*
* Updates all fields owned by the common code in port_info and link_config
* based on information provided by the firmware. Does not touch any
* requested_* field.
*/
static void handle_port_info(struct port_info *pi, const struct fw_port_info *p)
static void handle_port_info(struct port_info *pi, const struct fw_port_cmd *p,
enum fw_port_action action, bool *mod_changed, bool *link_changed)
{
struct link_config *lc = &pi->link_cfg;
int speed;
struct link_config old_lc, *lc = &pi->link_cfg;
unsigned char fc, fec;
u32 stat = be32_to_cpu(p->lstatus_to_modtype);
u32 stat, linkattr;
int old_ptype, old_mtype;
pi->port_type = G_FW_PORT_CMD_PTYPE(stat);
pi->mod_type = G_FW_PORT_CMD_MODTYPE(stat);
pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP ?
G_FW_PORT_CMD_MDIOADDR(stat) : -1;
old_ptype = pi->port_type;
old_mtype = pi->mod_type;
old_lc = *lc;
if (action == FW_PORT_ACTION_GET_PORT_INFO) {
stat = be32_to_cpu(p->u.info.lstatus_to_modtype);
lc->supported = be16_to_cpu(p->pcap);
lc->advertising = be16_to_cpu(p->acap);
lc->lp_advertising = be16_to_cpu(p->lpacap);
lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS) != 0;
lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC(stat);
pi->port_type = G_FW_PORT_CMD_PTYPE(stat);
pi->mod_type = G_FW_PORT_CMD_MODTYPE(stat);
pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP ?
G_FW_PORT_CMD_MDIOADDR(stat) : -1;
speed = 0;
if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M))
speed = 100;
else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G))
speed = 1000;
else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G))
speed = 10000;
else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_25G))
speed = 25000;
else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_40G))
speed = 40000;
else if (stat & V_FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100G))
speed = 100000;
lc->speed = speed;
lc->supported = fwcaps16_to_caps32(be16_to_cpu(p->u.info.pcap));
lc->advertising = fwcaps16_to_caps32(be16_to_cpu(p->u.info.acap));
lc->lp_advertising = fwcaps16_to_caps32(be16_to_cpu(p->u.info.lpacap));
lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS) != 0;
lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC(stat);
linkattr = lstatus_to_fwcap(stat);
} else if (action == FW_PORT_ACTION_GET_PORT_INFO32) {
stat = be32_to_cpu(p->u.info32.lstatus32_to_cbllen32);
pi->port_type = G_FW_PORT_CMD_PORTTYPE32(stat);
pi->mod_type = G_FW_PORT_CMD_MODTYPE32(stat);
pi->mdio_addr = stat & F_FW_PORT_CMD_MDIOCAP32 ?
G_FW_PORT_CMD_MDIOADDR32(stat) : -1;
lc->supported = be32_to_cpu(p->u.info32.pcaps32);
lc->advertising = be32_to_cpu(p->u.info32.acaps32);
lc->lp_advertising = be16_to_cpu(p->u.info32.lpacaps32);
lc->link_ok = (stat & F_FW_PORT_CMD_LSTATUS32) != 0;
lc->link_down_rc = G_FW_PORT_CMD_LINKDNRC32(stat);
linkattr = be32_to_cpu(p->u.info32.linkattr32);
} else {
CH_ERR(pi->adapter, "bad port_info action 0x%x\n", action);
return;
}
lc->speed = fwcap_to_speed(linkattr);
fc = 0;
if (stat & F_FW_PORT_CMD_RXPAUSE)
if (linkattr & FW_PORT_CAP32_FC_RX)
fc |= PAUSE_RX;
if (stat & F_FW_PORT_CMD_TXPAUSE)
if (linkattr & FW_PORT_CAP32_FC_TX)
fc |= PAUSE_TX;
lc->fc = fc;
fec = 0;
if (lc->advertising & FW_PORT_CAP_FEC_RS)
fec = FEC_RS;
else if (lc->advertising & FW_PORT_CAP_FEC_BASER_RS)
fec = FEC_BASER_RS;
fec = FEC_NONE;
if (linkattr & FW_PORT_CAP32_FEC_RS)
fec |= FEC_RS;
if (linkattr & FW_PORT_CAP32_FEC_BASER_RS)
fec |= FEC_BASER_RS;
lc->fec = fec;
if (mod_changed != NULL)
*mod_changed = false;
if (link_changed != NULL)
*link_changed = false;
if (old_ptype != pi->port_type || old_mtype != pi->mod_type ||
old_lc.supported != lc->supported) {
if (pi->mod_type != FW_PORT_MOD_TYPE_NONE) {
lc->fec_hint = lc->advertising &
V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC);
}
if (mod_changed != NULL)
*mod_changed = true;
}
if (old_lc.link_ok != lc->link_ok || old_lc.speed != lc->speed ||
old_lc.fec != lc->fec || old_lc.fc != lc->fc) {
if (link_changed != NULL)
*link_changed = true;
}
}
/**
@ -7798,22 +8033,24 @@ static void handle_port_info(struct port_info *pi, const struct fw_port_info *p)
*/
int t4_update_port_info(struct port_info *pi)
{
struct fw_port_cmd port_cmd;
struct adapter *sc = pi->adapter;
struct fw_port_cmd cmd;
enum fw_port_action action;
int ret;
memset(&port_cmd, 0, sizeof port_cmd);
port_cmd.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) |
F_FW_CMD_REQUEST | F_FW_CMD_READ |
V_FW_PORT_CMD_PORTID(pi->tx_chan));
port_cmd.action_to_len16 = cpu_to_be32(
V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_GET_PORT_INFO) |
FW_LEN16(port_cmd));
ret = t4_wr_mbox_ns(pi->adapter, pi->adapter->mbox,
&port_cmd, sizeof(port_cmd), &port_cmd);
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_READ |
V_FW_PORT_CMD_PORTID(pi->tx_chan));
action = sc->params.port_caps32 ? FW_PORT_ACTION_GET_PORT_INFO32 :
FW_PORT_ACTION_GET_PORT_INFO;
cmd.action_to_len16 = cpu_to_be32(V_FW_PORT_CMD_ACTION(action) |
FW_LEN16(cmd));
ret = t4_wr_mbox_ns(sc, sc->mbox, &cmd, sizeof(cmd), &cmd);
if (ret)
return ret;
handle_port_info(pi, &port_cmd.u.info);
handle_port_info(pi, &cmd, action, NULL, NULL);
return 0;
}
@ -7828,15 +8065,18 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl)
{
u8 opcode = *(const u8 *)rpl;
const struct fw_port_cmd *p = (const void *)rpl;
unsigned int action =
G_FW_PORT_CMD_ACTION(be32_to_cpu(p->action_to_len16));
enum fw_port_action action =
G_FW_PORT_CMD_ACTION(be32_to_cpu(p->action_to_len16));
bool mod_changed, link_changed;
if (opcode == FW_PORT_CMD && action == FW_PORT_ACTION_GET_PORT_INFO) {
if (opcode == FW_PORT_CMD &&
(action == FW_PORT_ACTION_GET_PORT_INFO ||
action == FW_PORT_ACTION_GET_PORT_INFO32)) {
/* link/module state change message */
int i, old_ptype, old_mtype;
int i;
int chan = G_FW_PORT_CMD_PORTID(be32_to_cpu(p->op_to_portid));
struct port_info *pi = NULL;
struct link_config *lc, *old_lc;
struct link_config *lc;
for_each_port(adap, i) {
pi = adap2pinfo(adap, i);
@ -7846,23 +8086,15 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl)
lc = &pi->link_cfg;
PORT_LOCK(pi);
old_lc = &pi->old_link_cfg;
old_ptype = pi->port_type;
old_mtype = pi->mod_type;
handle_port_info(pi, &p->u.info);
handle_port_info(pi, p, action, &mod_changed, &link_changed);
PORT_UNLOCK(pi);
if (old_ptype != pi->port_type || old_mtype != pi->mod_type) {
if (mod_changed)
t4_os_portmod_changed(pi);
}
PORT_LOCK(pi);
if (old_lc->link_ok != lc->link_ok ||
old_lc->speed != lc->speed ||
old_lc->fec != lc->fec ||
old_lc->fc != lc->fc) {
if (link_changed) {
PORT_LOCK(pi);
t4_os_link_changed(pi);
*old_lc = *lc;
PORT_UNLOCK(pi);
}
PORT_UNLOCK(pi);
} else {
CH_WARN_RATELIMIT(adap, "Unknown firmware reply %d\n", opcode);
return -EINVAL;
@ -8595,6 +8827,11 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf, int port_id)
} while ((adap->params.portvec & (1 << j)) == 0);
}
p->tx_chan = j;
p->mps_bg_map = t4_get_mps_bg_map(adap, j);
p->rx_e_chan_map = t4_get_rx_e_chan_map(adap, j);
p->lport = j;
if (!(adap->flags & IS_VF) ||
adap->params.vfres.r_caps & FW_CMD_CAP_PORT) {
t4_update_port_info(p);
@ -8609,10 +8846,6 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf, int port_id)
p->vi[0].smt_idx = (ret & 0x7f) << 1;
else
p->vi[0].smt_idx = (ret & 0x7f);
p->tx_chan = j;
p->mps_bg_map = t4_get_mps_bg_map(adap, j);
p->rx_e_chan_map = t4_get_rx_e_chan_map(adap, j);
p->lport = j;
p->vi[0].rss_size = rss_size;
t4_os_set_hw_addr(p, addr);

View File

@ -108,6 +108,7 @@ typedef boolean_t bool;
#define DUPLEX_HALF 0
#define DUPLEX_FULL 1
#define AUTONEG_AUTO (-1)
#define AUTONEG_DISABLE 0
#define AUTONEG_ENABLE 1

View File

@ -390,18 +390,20 @@ static char t4_cfg_file[32] = DEFAULT_CF;
TUNABLE_STR("hw.cxgbe.config_file", t4_cfg_file, sizeof(t4_cfg_file));
/*
* PAUSE settings (bit 0, 1 = rx_pause, tx_pause respectively).
* PAUSE settings (bit 0, 1, 2 = rx_pause, tx_pause, pause_autoneg respectively).
* rx_pause = 1 to heed incoming PAUSE frames, 0 to ignore them.
* tx_pause = 1 to emit PAUSE frames when the rx FIFO reaches its high water
* mark or when signalled to do so, 0 to never emit PAUSE.
* pause_autoneg = 1 means PAUSE will be negotiated if possible and the
* negotiated settings will override rx_pause/tx_pause.
* Otherwise rx_pause/tx_pause are applied forcibly.
*/
static int t4_pause_settings = PAUSE_TX | PAUSE_RX;
static int t4_pause_settings = PAUSE_RX | PAUSE_TX | PAUSE_AUTONEG;
TUNABLE_INT("hw.cxgbe.pause_settings", &t4_pause_settings);
/*
* Forward Error Correction settings (bit 0, 1, 2 = FEC_RS, FEC_BASER_RS,
* FEC_RESERVED respectively).
* -1 to run with the firmware default.
* Forward Error Correction settings (bit 0, 1 = RS, BASER respectively).
* -1 to run with the firmware default. Same as FEC_AUTO (bit 5)
* 0 to disable FEC.
*/
static int t4_fec = -1;
@ -526,9 +528,11 @@ static int get_params__pre_init(struct adapter *);
static int get_params__post_init(struct adapter *);
static int set_params__post_init(struct adapter *);
static void t4_set_desc(struct adapter *);
static void build_medialist(struct port_info *, struct ifmedia *);
static void init_l1cfg(struct port_info *);
static int apply_l1cfg(struct port_info *);
static bool fixed_ifmedia(struct port_info *);
static void build_medialist(struct port_info *);
static void init_link_config(struct port_info *);
static int fixup_link_config(struct port_info *);
static int apply_link_config(struct port_info *);
static int cxgbe_init_synchronized(struct vi_info *);
static int cxgbe_uninit_synchronized(struct vi_info *);
static void quiesce_txq(struct adapter *, struct sge_txq *);
@ -1018,6 +1022,14 @@ t4_attach(device_t dev)
ifmedia_init(&pi->media, IFM_IMASK, cxgbe_media_change,
cxgbe_media_status);
PORT_LOCK(pi);
init_link_config(pi);
fixup_link_config(pi);
build_medialist(pi);
if (fixed_ifmedia(pi))
pi->flags |= FIXED_IFMEDIA;
PORT_UNLOCK(pi);
pi->dev = device_add_child(dev, sc->names->ifnet_name, -1);
if (pi->dev == NULL) {
device_printf(dev,
@ -1885,7 +1897,7 @@ cxgbe_transmit(struct ifnet *ifp, struct mbuf *m)
M_ASSERTPKTHDR(m);
MPASS(m->m_nextpkt == NULL); /* not quite ready for this yet */
if (__predict_false(pi->link_cfg.link_ok == 0)) {
if (__predict_false(pi->link_cfg.link_ok == false)) {
m_freem(m);
return (ENETDOWN);
}
@ -2063,8 +2075,8 @@ cxgbe_get_counter(struct ifnet *ifp, ift_counter c)
}
/*
* The kernel picks a media from the list we had provided so we do not have to
* validate the request.
* The kernel picks a media from the list we had provided but we still validate
* the requeste.
*/
int
cxgbe_media_change(struct ifnet *ifp)
@ -2081,8 +2093,14 @@ cxgbe_media_change(struct ifnet *ifp)
return (rc);
PORT_LOCK(pi);
if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
MPASS(lc->supported & FW_PORT_CAP_ANEG);
/* ifconfig .. media autoselect */
if (!(lc->supported & FW_PORT_CAP32_ANEG)) {
rc = ENOTSUP; /* AN not supported by transceiver */
goto done;
}
lc->requested_aneg = AUTONEG_ENABLE;
lc->requested_speed = 0;
lc->requested_fc |= PAUSE_AUTONEG;
} else {
lc->requested_aneg = AUTONEG_DISABLE;
lc->requested_speed =
@ -2093,47 +2111,25 @@ cxgbe_media_change(struct ifnet *ifp)
if (IFM_OPTIONS(ifm->ifm_media) & IFM_ETH_TXPAUSE)
lc->requested_fc |= PAUSE_TX;
}
if (pi->up_vis > 0)
rc = apply_l1cfg(pi);
if (pi->up_vis > 0) {
fixup_link_config(pi);
rc = apply_link_config(pi);
}
done:
PORT_UNLOCK(pi);
end_synchronized_op(sc, 0);
return (rc);
}
/*
* Mbps to FW_PORT_CAP_SPEED_* bit.
*/
static uint16_t
speed_to_fwspeed(int speed)
{
switch (speed) {
case 100000:
return (FW_PORT_CAP_SPEED_100G);
case 40000:
return (FW_PORT_CAP_SPEED_40G);
case 25000:
return (FW_PORT_CAP_SPEED_25G);
case 10000:
return (FW_PORT_CAP_SPEED_10G);
case 1000:
return (FW_PORT_CAP_SPEED_1G);
case 100:
return (FW_PORT_CAP_SPEED_100M);
}
return (0);
}
/*
* Base media word (without ETHER, pause, link active, etc.) for the port at the
* given speed.
*/
static int
port_mword(struct port_info *pi, uint16_t speed)
port_mword(struct port_info *pi, uint32_t speed)
{
MPASS(speed & M_FW_PORT_CAP_SPEED);
MPASS(speed & M_FW_PORT_CAP32_SPEED);
MPASS(powerof2(speed));
switch(pi->port_type) {
@ -2142,24 +2138,24 @@ port_mword(struct port_info *pi, uint16_t speed)
case FW_PORT_TYPE_BT_XAUI:
/* BaseT */
switch (speed) {
case FW_PORT_CAP_SPEED_100M:
case FW_PORT_CAP32_SPEED_100M:
return (IFM_100_T);
case FW_PORT_CAP_SPEED_1G:
case FW_PORT_CAP32_SPEED_1G:
return (IFM_1000_T);
case FW_PORT_CAP_SPEED_10G:
case FW_PORT_CAP32_SPEED_10G:
return (IFM_10G_T);
}
break;
case FW_PORT_TYPE_KX4:
if (speed == FW_PORT_CAP_SPEED_10G)
if (speed == FW_PORT_CAP32_SPEED_10G)
return (IFM_10G_KX4);
break;
case FW_PORT_TYPE_CX4:
if (speed == FW_PORT_CAP_SPEED_10G)
if (speed == FW_PORT_CAP32_SPEED_10G)
return (IFM_10G_CX4);
break;
case FW_PORT_TYPE_KX:
if (speed == FW_PORT_CAP_SPEED_1G)
if (speed == FW_PORT_CAP32_SPEED_1G)
return (IFM_1000_KX);
break;
case FW_PORT_TYPE_KR:
@ -2170,15 +2166,17 @@ port_mword(struct port_info *pi, uint16_t speed)
case FW_PORT_TYPE_KR_SFP28:
case FW_PORT_TYPE_KR_XLAUI:
switch (speed) {
case FW_PORT_CAP_SPEED_1G:
case FW_PORT_CAP32_SPEED_1G:
return (IFM_1000_KX);
case FW_PORT_CAP_SPEED_10G:
case FW_PORT_CAP32_SPEED_10G:
return (IFM_10G_KR);
case FW_PORT_CAP_SPEED_25G:
case FW_PORT_CAP32_SPEED_25G:
return (IFM_25G_KR);
case FW_PORT_CAP_SPEED_40G:
case FW_PORT_CAP32_SPEED_40G:
return (IFM_40G_KR4);
case FW_PORT_CAP_SPEED_100G:
case FW_PORT_CAP32_SPEED_50G:
return (IFM_50G_KR2);
case FW_PORT_CAP32_SPEED_100G:
return (IFM_100G_KR4);
}
break;
@ -2196,53 +2194,59 @@ port_mword(struct port_info *pi, uint16_t speed)
switch (pi->mod_type) {
case FW_PORT_MOD_TYPE_LR:
switch (speed) {
case FW_PORT_CAP_SPEED_1G:
case FW_PORT_CAP32_SPEED_1G:
return (IFM_1000_LX);
case FW_PORT_CAP_SPEED_10G:
case FW_PORT_CAP32_SPEED_10G:
return (IFM_10G_LR);
case FW_PORT_CAP_SPEED_25G:
case FW_PORT_CAP32_SPEED_25G:
return (IFM_25G_LR);
case FW_PORT_CAP_SPEED_40G:
case FW_PORT_CAP32_SPEED_40G:
return (IFM_40G_LR4);
case FW_PORT_CAP_SPEED_100G:
case FW_PORT_CAP32_SPEED_50G:
return (IFM_50G_LR2);
case FW_PORT_CAP32_SPEED_100G:
return (IFM_100G_LR4);
}
break;
case FW_PORT_MOD_TYPE_SR:
switch (speed) {
case FW_PORT_CAP_SPEED_1G:
case FW_PORT_CAP32_SPEED_1G:
return (IFM_1000_SX);
case FW_PORT_CAP_SPEED_10G:
case FW_PORT_CAP32_SPEED_10G:
return (IFM_10G_SR);
case FW_PORT_CAP_SPEED_25G:
case FW_PORT_CAP32_SPEED_25G:
return (IFM_25G_SR);
case FW_PORT_CAP_SPEED_40G:
case FW_PORT_CAP32_SPEED_40G:
return (IFM_40G_SR4);
case FW_PORT_CAP_SPEED_100G:
case FW_PORT_CAP32_SPEED_50G:
return (IFM_50G_SR2);
case FW_PORT_CAP32_SPEED_100G:
return (IFM_100G_SR4);
}
break;
case FW_PORT_MOD_TYPE_ER:
if (speed == FW_PORT_CAP_SPEED_10G)
if (speed == FW_PORT_CAP32_SPEED_10G)
return (IFM_10G_ER);
break;
case FW_PORT_MOD_TYPE_TWINAX_PASSIVE:
case FW_PORT_MOD_TYPE_TWINAX_ACTIVE:
switch (speed) {
case FW_PORT_CAP_SPEED_1G:
case FW_PORT_CAP32_SPEED_1G:
return (IFM_1000_CX);
case FW_PORT_CAP_SPEED_10G:
case FW_PORT_CAP32_SPEED_10G:
return (IFM_10G_TWINAX);
case FW_PORT_CAP_SPEED_25G:
case FW_PORT_CAP32_SPEED_25G:
return (IFM_25G_CR);
case FW_PORT_CAP_SPEED_40G:
case FW_PORT_CAP32_SPEED_40G:
return (IFM_40G_CR4);
case FW_PORT_CAP_SPEED_100G:
case FW_PORT_CAP32_SPEED_50G:
return (IFM_50G_CR2);
case FW_PORT_CAP32_SPEED_100G:
return (IFM_100G_CR4);
}
break;
case FW_PORT_MOD_TYPE_LRM:
if (speed == FW_PORT_CAP_SPEED_10G)
if (speed == FW_PORT_CAP32_SPEED_10G)
return (IFM_10G_LRM);
break;
case FW_PORT_MOD_TYPE_NA:
@ -2284,12 +2288,12 @@ cxgbe_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
* function. Just PORT_LOCK would have been enough otherwise.
*/
t4_update_port_info(pi);
build_medialist(pi, &pi->media);
build_medialist(pi);
}
/* ifm_status */
ifmr->ifm_status = IFM_AVALID;
if (lc->link_ok == 0)
if (lc->link_ok == false)
goto done;
ifmr->ifm_status |= IFM_ACTIVE;
@ -2300,7 +2304,7 @@ cxgbe_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
ifmr->ifm_active |= IFM_ETH_RXPAUSE;
if (lc->fc & PAUSE_TX)
ifmr->ifm_active |= IFM_ETH_TXPAUSE;
ifmr->ifm_active |= port_mword(pi, speed_to_fwspeed(lc->speed));
ifmr->ifm_active |= port_mword(pi, speed_to_fwcap(lc->speed));
done:
PORT_UNLOCK(pi);
end_synchronized_op(sc, 0);
@ -4135,6 +4139,12 @@ set_params__post_init(struct adapter *sc)
val = 1;
(void)t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &param, &val);
/* Enable 32b port caps if the firmware supports it. */
param = FW_PARAM_PFVF(PORT_CAPS32);
val = 1;
if (t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &param, &val) == 0)
sc->params.port_caps32 = 1;
#ifdef TCP_OFFLOAD
/*
* Override the TOE timers with user provided tunables. This is not the
@ -4217,22 +4227,30 @@ ifmedia_add4(struct ifmedia *ifm, int m)
ifmedia_add(ifm, m | IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE, 0, NULL);
}
/*
* This is the selected media, which is not quite the same as the active media.
* The media line in ifconfig is "media: Ethernet selected (active)" if selected
* and active are not the same, and "media: Ethernet selected" otherwise.
*/
static void
set_current_media(struct port_info *pi, struct ifmedia *ifm)
set_current_media(struct port_info *pi)
{
struct link_config *lc;
struct ifmedia *ifm;
int mword;
u_int speed;
PORT_LOCK_ASSERT_OWNED(pi);
/* Leave current media alone if it's already set to IFM_NONE. */
ifm = &pi->media;
if (ifm->ifm_cur != NULL &&
IFM_SUBTYPE(ifm->ifm_cur->ifm_media) == IFM_NONE)
return;
lc = &pi->link_cfg;
if (lc->requested_aneg == AUTONEG_ENABLE &&
lc->supported & FW_PORT_CAP_ANEG) {
if (lc->requested_aneg != AUTONEG_DISABLE &&
lc->supported & FW_PORT_CAP32_ANEG) {
ifmedia_set(ifm, IFM_ETHER | IFM_AUTO);
return;
}
@ -4241,16 +4259,42 @@ set_current_media(struct port_info *pi, struct ifmedia *ifm)
mword |= IFM_ETH_TXPAUSE;
if (lc->requested_fc & PAUSE_RX)
mword |= IFM_ETH_RXPAUSE;
mword |= port_mword(pi, speed_to_fwspeed(lc->requested_speed));
if (lc->requested_speed == 0)
speed = port_top_speed(pi) * 1000; /* Gbps -> Mbps */
else
speed = lc->requested_speed;
mword |= port_mword(pi, speed_to_fwcap(speed));
ifmedia_set(ifm, mword);
}
static void
build_medialist(struct port_info *pi, struct ifmedia *ifm)
/*
* Returns true if the ifmedia list for the port cannot change.
*/
static bool
fixed_ifmedia(struct port_info *pi)
{
uint16_t ss, speed;
return (pi->port_type == FW_PORT_TYPE_BT_SGMII ||
pi->port_type == FW_PORT_TYPE_BT_XFI ||
pi->port_type == FW_PORT_TYPE_BT_XAUI ||
pi->port_type == FW_PORT_TYPE_KX4 ||
pi->port_type == FW_PORT_TYPE_KX ||
pi->port_type == FW_PORT_TYPE_KR ||
pi->port_type == FW_PORT_TYPE_BP_AP ||
pi->port_type == FW_PORT_TYPE_BP4_AP ||
pi->port_type == FW_PORT_TYPE_BP40_BA ||
pi->port_type == FW_PORT_TYPE_KR4_100G ||
pi->port_type == FW_PORT_TYPE_KR_SFP28 ||
pi->port_type == FW_PORT_TYPE_KR_XLAUI);
}
static void
build_medialist(struct port_info *pi)
{
uint32_t ss, speed;
int unknown, mword, bit;
struct link_config *lc;
struct ifmedia *ifm;
PORT_LOCK_ASSERT_OWNED(pi);
@ -4258,18 +4302,12 @@ build_medialist(struct port_info *pi, struct ifmedia *ifm)
return;
/*
* First setup all the requested_ fields so that they comply with what's
* supported by the port + transceiver. Note that this clobbers any
* user preferences set via sysctl_pause_settings or sysctl_autoneg.
*/
init_l1cfg(pi);
/*
* Now (re)build the ifmedia list.
* Rebuild the ifmedia list.
*/
ifm = &pi->media;
ifmedia_removeall(ifm);
lc = &pi->link_cfg;
ss = G_FW_PORT_CAP_SPEED(lc->supported); /* Supported Speeds */
ss = G_FW_PORT_CAP32_SPEED(lc->supported); /* Supported Speeds */
if (__predict_false(ss == 0)) { /* not supposed to happen. */
MPASS(ss != 0);
no_media:
@ -4280,9 +4318,9 @@ build_medialist(struct port_info *pi, struct ifmedia *ifm)
}
unknown = 0;
for (bit = 0; bit < fls(ss); bit++) {
for (bit = S_FW_PORT_CAP32_SPEED; bit < fls(ss); bit++) {
speed = 1 << bit;
MPASS(speed & M_FW_PORT_CAP_SPEED);
MPASS(speed & M_FW_PORT_CAP32_SPEED);
if (ss & speed) {
mword = port_mword(pi, speed);
if (mword == IFM_NONE) {
@ -4295,86 +4333,134 @@ build_medialist(struct port_info *pi, struct ifmedia *ifm)
}
if (unknown > 0) /* Add one unknown for all unknown media types. */
ifmedia_add4(ifm, IFM_ETHER | IFM_FDX | IFM_UNKNOWN);
if (lc->supported & FW_PORT_CAP_ANEG)
if (lc->supported & FW_PORT_CAP32_ANEG)
ifmedia_add(ifm, IFM_ETHER | IFM_AUTO, 0, NULL);
set_current_media(pi, ifm);
set_current_media(pi);
}
/*
* Update all the requested_* fields in the link config to something valid (and
* reasonable).
* Initialize the requested fields in the link config based on driver tunables.
*/
static void
init_l1cfg(struct port_info *pi)
init_link_config(struct port_info *pi)
{
struct link_config *lc = &pi->link_cfg;
PORT_LOCK_ASSERT_OWNED(pi);
/* Gbps -> Mbps */
lc->requested_speed = port_top_speed(pi) * 1000;
lc->requested_speed = 0;
if (t4_autoneg != 0 && lc->supported & FW_PORT_CAP_ANEG) {
lc->requested_aneg = AUTONEG_ENABLE;
} else {
if (t4_autoneg == 0)
lc->requested_aneg = AUTONEG_DISABLE;
}
else if (t4_autoneg == 1)
lc->requested_aneg = AUTONEG_ENABLE;
else
lc->requested_aneg = AUTONEG_AUTO;
lc->requested_fc = t4_pause_settings & (PAUSE_TX | PAUSE_RX);
lc->requested_fc = t4_pause_settings & (PAUSE_TX | PAUSE_RX |
PAUSE_AUTONEG);
if (t4_fec != -1) {
if (t4_fec & FEC_RS && lc->supported & FW_PORT_CAP_FEC_RS) {
lc->requested_fec = FEC_RS;
} else if (t4_fec & FEC_BASER_RS &&
lc->supported & FW_PORT_CAP_FEC_BASER_RS) {
lc->requested_fec = FEC_BASER_RS;
} else {
lc->requested_fec = 0;
}
} else {
/* Use the suggested value provided by the firmware in acaps */
if (lc->advertising & FW_PORT_CAP_FEC_RS &&
lc->supported & FW_PORT_CAP_FEC_RS) {
lc->requested_fec = FEC_RS;
} else if (lc->advertising & FW_PORT_CAP_FEC_BASER_RS &&
lc->supported & FW_PORT_CAP_FEC_BASER_RS) {
lc->requested_fec = FEC_BASER_RS;
} else {
lc->requested_fec = 0;
}
if (t4_fec == -1 || t4_fec & FEC_AUTO)
lc->requested_fec = FEC_AUTO;
else {
lc->requested_fec = FEC_NONE;
if (t4_fec & FEC_RS)
lc->requested_fec |= FEC_RS;
if (t4_fec & FEC_BASER_RS)
lc->requested_fec |= FEC_BASER_RS;
}
}
/*
* Apply the settings in requested_* to the hardware. The parameters are
* expected to be sane.
* Makes sure that all requested settings comply with what's supported by the
* port. Returns the number of settings that were invalid and had to be fixed.
*/
static int
apply_l1cfg(struct port_info *pi)
fixup_link_config(struct port_info *pi)
{
int n = 0;
struct link_config *lc = &pi->link_cfg;
uint32_t fwspeed;
PORT_LOCK_ASSERT_OWNED(pi);
/* Speed (when not autonegotiating) */
if (lc->requested_speed != 0) {
fwspeed = speed_to_fwcap(lc->requested_speed);
if ((fwspeed & lc->supported) == 0) {
n++;
lc->requested_speed = 0;
}
}
/* Link autonegotiation */
MPASS(lc->requested_aneg == AUTONEG_ENABLE ||
lc->requested_aneg == AUTONEG_DISABLE ||
lc->requested_aneg == AUTONEG_AUTO);
if (lc->requested_aneg == AUTONEG_ENABLE &&
!(lc->supported & FW_PORT_CAP32_ANEG)) {
n++;
lc->requested_aneg = AUTONEG_AUTO;
}
/* Flow control */
MPASS((lc->requested_fc & ~(PAUSE_TX | PAUSE_RX | PAUSE_AUTONEG)) == 0);
if (lc->requested_fc & PAUSE_TX &&
!(lc->supported & FW_PORT_CAP32_FC_TX)) {
n++;
lc->requested_fc &= ~PAUSE_TX;
}
if (lc->requested_fc & PAUSE_RX &&
!(lc->supported & FW_PORT_CAP32_FC_RX)) {
n++;
lc->requested_fc &= ~PAUSE_RX;
}
if (!(lc->requested_fc & PAUSE_AUTONEG) &&
!(lc->supported & FW_PORT_CAP32_FORCE_PAUSE)) {
n++;
lc->requested_fc |= PAUSE_AUTONEG;
}
/* FEC */
if ((lc->requested_fec & FEC_RS &&
!(lc->supported & FW_PORT_CAP32_FEC_RS)) ||
(lc->requested_fec & FEC_BASER_RS &&
!(lc->supported & FW_PORT_CAP32_FEC_BASER_RS))) {
n++;
lc->requested_fec = FEC_AUTO;
}
return (n);
}
/*
* Apply the requested L1 settings, which are expected to be valid, to the
* hardware.
*/
static int
apply_link_config(struct port_info *pi)
{
struct adapter *sc = pi->adapter;
struct link_config *lc = &pi->link_cfg;
int rc;
#ifdef INVARIANTS
uint16_t fwspeed;
#ifdef INVARIANTS
ASSERT_SYNCHRONIZED_OP(sc);
PORT_LOCK_ASSERT_OWNED(pi);
if (lc->requested_aneg == AUTONEG_ENABLE)
MPASS(lc->supported & FW_PORT_CAP_ANEG);
MPASS(lc->supported & FW_PORT_CAP32_ANEG);
if (!(lc->requested_fc & PAUSE_AUTONEG))
MPASS(lc->supported & FW_PORT_CAP32_FORCE_PAUSE);
if (lc->requested_fc & PAUSE_TX)
MPASS(lc->supported & FW_PORT_CAP_FC_TX);
MPASS(lc->supported & FW_PORT_CAP32_FC_TX);
if (lc->requested_fc & PAUSE_RX)
MPASS(lc->supported & FW_PORT_CAP_FC_RX);
if (lc->requested_fec == FEC_RS)
MPASS(lc->supported & FW_PORT_CAP_FEC_RS);
if (lc->requested_fec == FEC_BASER_RS)
MPASS(lc->supported & FW_PORT_CAP_FEC_BASER_RS);
fwspeed = speed_to_fwspeed(lc->requested_speed);
MPASS(fwspeed != 0);
MPASS(lc->supported & fwspeed);
MPASS(lc->supported & FW_PORT_CAP32_FC_RX);
if (lc->requested_fec & FEC_RS)
MPASS(lc->supported & FW_PORT_CAP32_FEC_RS);
if (lc->requested_fec & FEC_BASER_RS)
MPASS(lc->supported & FW_PORT_CAP32_FEC_BASER_RS);
#endif
rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc);
if (rc != 0) {
@ -4382,8 +4468,17 @@ apply_l1cfg(struct port_info *pi)
if (!(sc->flags & IS_VF) || rc != FW_EPERM)
device_printf(pi->dev, "l1cfg failed: %d\n", rc);
} else {
lc->fc = lc->requested_fc;
lc->fec = lc->requested_fec;
/*
* An L1_CFG will almost always result in a link-change event if
* the link is up, and the driver will refresh the actual
* fec/fc/etc. when the notification is processed. If the link
* is down then the actual settings are meaningless.
*
* This takes care of the case where a change in the L1 settings
* may not result in a notification.
*/
if (lc->link_ok && !(lc->requested_fc & PAUSE_AUTONEG))
lc->fc = lc->requested_fc & (PAUSE_TX | PAUSE_RX);
}
return (rc);
}
@ -4637,9 +4732,18 @@ cxgbe_init_synchronized(struct vi_info *vi)
if (rc)
goto done; /* error message displayed already */
PORT_LOCK(pi);
if (pi->up_vis == 0) {
t4_update_port_info(pi);
fixup_link_config(pi);
build_medialist(pi);
apply_link_config(pi);
}
rc = -t4_enable_vi(sc, sc->mbox, vi->viid, true, true);
if (rc != 0) {
if_printf(ifp, "enable_vi failed: %d\n", rc);
PORT_UNLOCK(pi);
goto done;
}
@ -4666,12 +4770,7 @@ cxgbe_init_synchronized(struct vi_info *vi)
}
/* all ok */
PORT_LOCK(pi);
if (pi->up_vis++ == 0) {
t4_update_port_info(pi);
build_medialist(pi, &pi->media);
apply_l1cfg(pi);
}
pi->up_vis++;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
if (pi->nvi > 1 || sc->flags & IS_VF)
@ -4746,11 +4845,10 @@ cxgbe_uninit_synchronized(struct vi_info *vi)
return (0);
}
pi->link_cfg.link_ok = 0;
pi->link_cfg.link_ok = false;
pi->link_cfg.speed = 0;
pi->link_cfg.link_down_rc = 255;
t4_os_link_changed(pi);
pi->old_link_cfg = pi->link_cfg;
PORT_UNLOCK(pi);
return (0);
@ -6514,7 +6612,7 @@ sysctl_pause_settings(SYSCTL_HANDLER_ARGS)
if (req->newptr == NULL) {
struct sbuf *sb;
static char *bits = "\20\1PAUSE_RX\2PAUSE_TX";
static char *bits = "\20\1RX\2TX\3AUTO";
rc = sysctl_wire_old_buffer(req, 0);
if (rc != 0)
@ -6524,14 +6622,21 @@ sysctl_pause_settings(SYSCTL_HANDLER_ARGS)
if (sb == NULL)
return (ENOMEM);
sbuf_printf(sb, "%b", lc->fc & (PAUSE_TX | PAUSE_RX), bits);
if (lc->link_ok) {
sbuf_printf(sb, "%b", (lc->fc & (PAUSE_TX | PAUSE_RX)) |
(lc->requested_fc & PAUSE_AUTONEG), bits);
} else {
sbuf_printf(sb, "%b", lc->requested_fc & (PAUSE_TX |
PAUSE_RX | PAUSE_AUTONEG), bits);
}
rc = sbuf_finish(sb);
sbuf_delete(sb);
} else {
char s[2];
int n;
s[0] = '0' + (lc->requested_fc & (PAUSE_TX | PAUSE_RX));
s[0] = '0' + (lc->requested_fc & (PAUSE_TX | PAUSE_RX |
PAUSE_AUTONEG));
s[1] = 0;
rc = sysctl_handle_string(oidp, s, sizeof(s), req);
@ -6543,7 +6648,7 @@ sysctl_pause_settings(SYSCTL_HANDLER_ARGS)
if (s[0] < '0' || s[0] > '9')
return (EINVAL); /* not a number */
n = s[0] - '0';
if (n & ~(PAUSE_TX | PAUSE_RX))
if (n & ~(PAUSE_TX | PAUSE_RX | PAUSE_AUTONEG))
return (EINVAL); /* some other bit is set too */
rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK,
@ -6551,15 +6656,11 @@ sysctl_pause_settings(SYSCTL_HANDLER_ARGS)
if (rc)
return (rc);
PORT_LOCK(pi);
if ((lc->requested_fc & (PAUSE_TX | PAUSE_RX)) != n) {
lc->requested_fc &= ~(PAUSE_TX | PAUSE_RX);
lc->requested_fc |= n;
rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc);
if (rc == 0) {
lc->fc = lc->requested_fc;
set_current_media(pi, &pi->media);
}
}
lc->requested_fc = n;
fixup_link_config(pi);
if (pi->up_vis > 0)
rc = apply_link_config(pi);
set_current_media(pi);
PORT_UNLOCK(pi);
end_synchronized_op(sc, 0);
}
@ -6574,10 +6675,11 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
struct adapter *sc = pi->adapter;
struct link_config *lc = &pi->link_cfg;
int rc;
int8_t old;
if (req->newptr == NULL) {
struct sbuf *sb;
static char *bits = "\20\1RS\2BASER_RS\3RESERVED";
static char *bits = "\20\1RS\2BASE-R\3RSVD1\4RSVD2\5RSVD3\6AUTO";
rc = sysctl_wire_old_buffer(req, 0);
if (rc != 0)
@ -6587,43 +6689,68 @@ sysctl_fec(SYSCTL_HANDLER_ARGS)
if (sb == NULL)
return (ENOMEM);
sbuf_printf(sb, "%b", lc->fec & M_FW_PORT_CAP_FEC, bits);
/*
* Display the requested_fec when the link is down -- the actual
* FEC makes sense only when the link is up.
*/
if (lc->link_ok) {
sbuf_printf(sb, "%b", (lc->fec & M_FW_PORT_CAP32_FEC) |
(lc->requested_fec & FEC_AUTO), bits);
} else {
sbuf_printf(sb, "%b", lc->requested_fec, bits);
}
rc = sbuf_finish(sb);
sbuf_delete(sb);
} else {
char s[2];
char s[3];
int n;
s[0] = '0' + (lc->requested_fec & M_FW_PORT_CAP_FEC);
s[1] = 0;
snprintf(s, sizeof(s), "%d",
lc->requested_fec == FEC_AUTO ? -1 :
lc->requested_fec & M_FW_PORT_CAP32_FEC);
rc = sysctl_handle_string(oidp, s, sizeof(s), req);
if (rc != 0)
return(rc);
if (s[1] != 0)
return (EINVAL);
if (s[0] < '0' || s[0] > '9')
return (EINVAL); /* not a number */
n = s[0] - '0';
if (n & ~M_FW_PORT_CAP_FEC)
return (EINVAL); /* some other bit is set too */
if (!powerof2(n))
return (EINVAL); /* one bit can be set at most */
n = strtol(&s[0], NULL, 0);
if (n < 0 || n & FEC_AUTO)
n = FEC_AUTO;
else {
if (n & ~M_FW_PORT_CAP32_FEC)
return (EINVAL);/* some other bit is set too */
if (!powerof2(n))
return (EINVAL);/* one bit can be set at most */
}
rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK,
"t4fec");
if (rc)
return (rc);
PORT_LOCK(pi);
if ((lc->requested_fec & M_FW_PORT_CAP_FEC) != n) {
lc->requested_fec = n &
G_FW_PORT_CAP_FEC(lc->supported);
rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc);
if (rc == 0) {
lc->fec = lc->requested_fec;
old = lc->requested_fec;
if (n == FEC_AUTO)
lc->requested_fec = FEC_AUTO;
else if (n == 0)
lc->requested_fec = FEC_NONE;
else {
if ((lc->supported | V_FW_PORT_CAP32_FEC(n)) !=
lc->supported) {
rc = ENOTSUP;
goto done;
}
lc->requested_fec = n;
}
fixup_link_config(pi);
if (pi->up_vis > 0) {
rc = apply_link_config(pi);
if (rc != 0) {
lc->requested_fec = old;
if (rc == FW_EPROTO)
rc = ENOTSUP;
}
}
done:
PORT_UNLOCK(pi);
end_synchronized_op(sc, 0);
}
@ -6637,10 +6764,10 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS)
struct port_info *pi = arg1;
struct adapter *sc = pi->adapter;
struct link_config *lc = &pi->link_cfg;
int rc, val, old;
int rc, val;
if (lc->supported & FW_PORT_CAP_ANEG)
val = lc->requested_aneg == AUTONEG_ENABLE ? 1 : 0;
if (lc->supported & FW_PORT_CAP32_ANEG)
val = lc->requested_aneg == AUTONEG_DISABLE ? 0 : 1;
else
val = -1;
rc = sysctl_handle_int(oidp, &val, 0, req);
@ -6651,28 +6778,22 @@ sysctl_autoneg(SYSCTL_HANDLER_ARGS)
else if (val == 1)
val = AUTONEG_ENABLE;
else
return (EINVAL);
val = AUTONEG_AUTO;
rc = begin_synchronized_op(sc, &pi->vi[0], SLEEP_OK | INTR_OK,
"t4aneg");
if (rc)
return (rc);
PORT_LOCK(pi);
if ((lc->supported & FW_PORT_CAP_ANEG) == 0) {
if (val == AUTONEG_ENABLE && !(lc->supported & FW_PORT_CAP32_ANEG)) {
rc = ENOTSUP;
goto done;
}
if (lc->requested_aneg == val) {
rc = 0; /* no change, do nothing. */
goto done;
}
old = lc->requested_aneg;
lc->requested_aneg = val;
rc = -t4_link_l1cfg(sc, sc->mbox, pi->tx_chan, lc);
if (rc != 0)
lc->requested_aneg = old;
else
set_current_media(pi, &pi->media);
fixup_link_config(pi);
if (pi->up_vis > 0)
rc = apply_link_config(pi);
set_current_media(pi);
done:
PORT_UNLOCK(pi);
end_synchronized_op(sc, 0);
@ -9409,13 +9530,17 @@ t4_os_portmod_changed(struct port_info *pi)
NULL, "LR", "SR", "ER", "TWINAX", "active TWINAX", "LRM"
};
MPASS((pi->flags & FIXED_IFMEDIA) == 0);
KASSERT((pi->flags & FIXED_IFMEDIA) == 0,
("%s: port_type %u", __func__, pi->port_type));
vi = &pi->vi[0];
if (begin_synchronized_op(sc, vi, HOLD_LOCK, "t4mod") == 0) {
PORT_LOCK(pi);
build_medialist(pi, &pi->media);
apply_l1cfg(pi);
build_medialist(pi);
if (pi->mod_type != FW_PORT_MOD_TYPE_NONE) {
fixup_link_config(pi);
apply_link_config(pi);
}
PORT_UNLOCK(pi);
end_synchronized_op(sc, LOCK_HELD);
}

View File

@ -634,7 +634,7 @@ write_tx_wr(void *dst, struct toepcb *toep, unsigned int immdlen,
if (txalign > 0) {
struct tcpcb *tp = intotcpcb(toep->inp);
if (plen < 2 * tp->t_maxseg || is_10G_port(toep->vi->pi))
if (plen < 2 * tp->t_maxseg)
txwr->lsodisable_to_flags |=
htobe32(F_FW_OFLD_TX_DATA_WR_LSODISABLE);
else