+static void t4_set_link_autoneg_speed(struct port_info *pi, u32 *new_caps)
+{
+ struct link_config *lc = &pi->link_cfg;
+ u32 caps = *new_caps;
+
+ caps &= ~V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
+ caps |= G_FW_PORT_CAP32_SPEED(lc->acaps);
+
+ *new_caps = caps;
+}
+
+int t4_set_link_speed(struct port_info *pi, u32 speed, u32 *new_caps)
+{
+ u32 fw_speed_cap = t4_speed_to_fwcap(speed);
+ struct link_config *lc = &pi->link_cfg;
+ u32 caps = *new_caps;
+
+ if (!(lc->pcaps & fw_speed_cap))
+ return -EOPNOTSUPP;
+
+ caps &= ~V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
+ caps |= fw_speed_cap;
+
+ *new_caps = caps;
+
+ return 0;
+}
+
+int t4_set_link_pause(struct port_info *pi, u8 autoneg, u8 pause_tx,
+ u8 pause_rx, u32 *new_caps)
+{
+ struct link_config *lc = &pi->link_cfg;
+ u32 caps = *new_caps;
+ u32 max_speed;
+
+ max_speed = t4_fwcap_to_speed(lc->link_caps);
+
+ if (autoneg) {
+ if (!(lc->pcaps & FW_PORT_CAP32_ANEG))
+ return -EINVAL;
+
+ caps |= FW_PORT_CAP32_ANEG;
+ t4_set_link_autoneg_speed(pi, &caps);
+ } else {
+ if (!max_speed)
+ max_speed = t4_fwcap_to_speed(lc->acaps);
+
+ caps &= ~FW_PORT_CAP32_ANEG;
+ t4_set_link_speed(pi, max_speed, &caps);
+ }
+
+ if (lc->pcaps & FW_PORT_CAP32_MDIAUTO)
+ caps |= V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO);
+
+ 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_tx && 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_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_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;
+ }
+
+ *new_caps = caps;
+
+ return 0;
+}
+
+int t4_set_link_fec(struct port_info *pi, u8 fec_rs, u8 fec_baser,
+ u8 fec_none, u32 *new_caps)
+{
+ struct link_config *lc = &pi->link_cfg;
+ u32 max_speed, caps = *new_caps;
+
+ if (!(lc->pcaps & V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC)))
+ return -EOPNOTSUPP;
+
+ /* Link might be down. In that case consider the max
+ * speed advertised
+ */
+ max_speed = t4_fwcap_to_speed(lc->link_caps);
+ if (!max_speed)
+ max_speed = t4_fwcap_to_speed(lc->acaps);
+
+ caps &= ~V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC);
+ if (fec_rs) {
+ switch (max_speed) {
+ case 100000:
+ case 25000:
+ caps |= FW_PORT_CAP32_FEC_RS;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if (fec_baser) {
+ switch (max_speed) {
+ case 50000:
+ case 25000:
+ caps |= FW_PORT_CAP32_FEC_BASER_RS;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if (fec_none)
+ caps |= FW_PORT_CAP32_FEC_NO_FEC;
+
+ if (!(caps & V_FW_PORT_CAP32_FEC(M_FW_PORT_CAP32_FEC))) {
+ /* No explicit encoding is requested.
+ * So, default back to AUTO.
+ */
+ switch (max_speed) {
+ case 100000:
+ caps |= FW_PORT_CAP32_FEC_RS |
+ FW_PORT_CAP32_FEC_NO_FEC;
+ break;
+ case 50000:
+ caps |= FW_PORT_CAP32_FEC_BASER_RS |
+ FW_PORT_CAP32_FEC_NO_FEC;
+ break;
+ case 25000:
+ caps |= FW_PORT_CAP32_FEC_RS |
+ FW_PORT_CAP32_FEC_BASER_RS |
+ FW_PORT_CAP32_FEC_NO_FEC;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ *new_caps = caps;
+
+ return 0;
+}
+