Commit ae7e6937 authored by Alexander Lobakin's avatar Alexander Lobakin Committed by David S. Miller
Browse files

qed: add support for Forward Error Correction



Add all necessary routines for reading supported FEC modes from NVM and
querying FEC control to the MFW (if the running version supports it).

Signed-off-by: default avatarAlexander Lobakin <alobakin@marvell.com>
Signed-off-by: default avatarIgor Russkikh <irusskikh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 37237b5b
Loading
Loading
Loading
Loading
+37 −17
Original line number Diff line number Diff line
@@ -3968,7 +3968,7 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)

static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
	u32 port_cfg_addr, link_temp, nvm_cfg_addr, device_capabilities;
	u32 port_cfg_addr, link_temp, nvm_cfg_addr, device_capabilities, fc;
	u32 nvm_cfg1_offset, mf_mode, addr, generic_cont0, core_cfg;
	struct qed_mcp_link_capabilities *p_caps;
	struct qed_mcp_link_params *link;
@@ -4081,16 +4081,38 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
	p_hwfn->mcp_info->link_capabilities.default_speed_autoneg =
		link->speed.autoneg;

	link_temp &= NVM_CFG1_PORT_DRV_FLOW_CONTROL_MASK;
	link_temp >>= NVM_CFG1_PORT_DRV_FLOW_CONTROL_OFFSET;
	link->pause.autoneg = !!(link_temp &
				 NVM_CFG1_PORT_DRV_FLOW_CONTROL_AUTONEG);
	link->pause.forced_rx = !!(link_temp &
				   NVM_CFG1_PORT_DRV_FLOW_CONTROL_RX);
	link->pause.forced_tx = !!(link_temp &
				   NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX);
	fc = GET_MFW_FIELD(link_temp, NVM_CFG1_PORT_DRV_FLOW_CONTROL);
	link->pause.autoneg = !!(fc & NVM_CFG1_PORT_DRV_FLOW_CONTROL_AUTONEG);
	link->pause.forced_rx = !!(fc & NVM_CFG1_PORT_DRV_FLOW_CONTROL_RX);
	link->pause.forced_tx = !!(fc & NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX);
	link->loopback_mode = 0;

	if (p_hwfn->mcp_info->capabilities &
	    FW_MB_PARAM_FEATURE_SUPPORT_FEC_CONTROL) {
		switch (GET_MFW_FIELD(link_temp,
				      NVM_CFG1_PORT_FEC_FORCE_MODE)) {
		case NVM_CFG1_PORT_FEC_FORCE_MODE_NONE:
			p_caps->fec_default |= QED_FEC_MODE_NONE;
			break;
		case NVM_CFG1_PORT_FEC_FORCE_MODE_FIRECODE:
			p_caps->fec_default |= QED_FEC_MODE_FIRECODE;
			break;
		case NVM_CFG1_PORT_FEC_FORCE_MODE_RS:
			p_caps->fec_default |= QED_FEC_MODE_RS;
			break;
		case NVM_CFG1_PORT_FEC_FORCE_MODE_AUTO:
			p_caps->fec_default |= QED_FEC_MODE_AUTO;
			break;
		default:
			DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
				   "unknown FEC mode in 0x%08x\n", link_temp);
		}
	} else {
		p_caps->fec_default = QED_FEC_MODE_UNSUPPORTED;
	}

	link->fec = p_caps->fec_default;

	if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) {
		link_temp = qed_rd(p_hwfn, p_ptt, port_cfg_addr +
				   offsetof(struct nvm_cfg1_port, ext_phy));
@@ -4122,14 +4144,12 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
		p_caps->default_eee = QED_MCP_EEE_UNSUPPORTED;
	}

	DP_VERBOSE(p_hwfn,
		   NETIF_MSG_LINK,
		   "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x EEE: %02x [%08x usec]\n",
		   link->speed.forced_speed,
		   link->speed.advertised_speeds,
		   link->speed.autoneg,
		   link->pause.autoneg,
		   p_caps->default_eee, p_caps->eee_lpi_timer);
	DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
		   "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x, EEE: 0x%02x [0x%08x usec], FEC: 0x%02x\n",
		   link->speed.forced_speed, link->speed.advertised_speeds,
		   link->speed.autoneg, link->pause.autoneg,
		   p_caps->default_eee, p_caps->eee_lpi_timer,
		   p_caps->fec_default);

	if (IS_LEAD_HWFN(p_hwfn)) {
		struct qed_dev *cdev = p_hwfn->cdev;
+22 −2
Original line number Diff line number Diff line
@@ -11566,8 +11566,15 @@ struct eth_phy_cfg {
#define EEE_TX_TIMER_USEC_AGGRESSIVE_TIME	0x100
#define EEE_TX_TIMER_USEC_LATENCY_TIME		0x6000

	u32 feature_config_flags;
#define ETH_EEE_MODE_ADV_LPI		(1 << 0)
	u32					deprecated;

	u32					fec_mode;
#define FEC_FORCE_MODE_MASK			0x000000ff
#define FEC_FORCE_MODE_OFFSET			0
#define FEC_FORCE_MODE_NONE			0x00
#define FEC_FORCE_MODE_FIRECODE			0x01
#define FEC_FORCE_MODE_RS			0x02
#define FEC_FORCE_MODE_AUTO			0x07
};

struct port_mf_cfg {
@@ -11934,6 +11941,11 @@ struct public_port {
#define LINK_STATUS_MAC_REMOTE_FAULT			0x02000000
#define LINK_STATUS_UNSUPPORTED_SPD_REQ			0x04000000

#define LINK_STATUS_FEC_MODE_MASK			0x38000000
#define LINK_STATUS_FEC_MODE_NONE			(0 << 27)
#define LINK_STATUS_FEC_MODE_FIRECODE_CL74		(1 << 27)
#define LINK_STATUS_FEC_MODE_RS_CL91			(2 << 27)

	u32 link_status1;
	u32 ext_phy_fw_version;
	u32 drv_phy_cfg_addr;
@@ -12553,6 +12565,7 @@ struct public_drv_mb {
#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_MASK		0x0000FFFF
#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_OFFSET	0
#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE		0x00000002
#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_FEC_CONTROL	0x00000004
#define DRV_MB_PARAM_FEATURE_SUPPORT_FUNC_VLINK		0x00010000

/* DRV_MSG_CODE_DEBUG_DATA_SEND parameters */
@@ -12641,6 +12654,7 @@ struct public_drv_mb {
	/* Get MFW feature support response */
#define FW_MB_PARAM_FEATURE_SUPPORT_SMARTLINQ		0x00000001
#define FW_MB_PARAM_FEATURE_SUPPORT_EEE			0x00000002
#define FW_MB_PARAM_FEATURE_SUPPORT_FEC_CONTROL		0x00000020
#define FW_MB_PARAM_FEATURE_SUPPORT_VLINK		0x00010000

#define FW_MB_PARAM_LOAD_DONE_DID_EFUSE_ERROR		BIT(0)
@@ -13091,6 +13105,12 @@ struct nvm_cfg1_port {
#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_AUTONEG			0x1
#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_RX			0x2
#define NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX			0x4
#define NVM_CFG1_PORT_FEC_FORCE_MODE_MASK			0x000e0000
#define NVM_CFG1_PORT_FEC_FORCE_MODE_OFFSET			17
#define NVM_CFG1_PORT_FEC_FORCE_MODE_NONE			0x0
#define NVM_CFG1_PORT_FEC_FORCE_MODE_FIRECODE			0x1
#define NVM_CFG1_PORT_FEC_FORCE_MODE_RS				0x2
#define NVM_CFG1_PORT_FEC_FORCE_MODE_AUTO			0x7

	u32							phy_cfg;
	u32							mgmt_traffic;
+6 −0
Original line number Diff line number Diff line
@@ -1597,6 +1597,9 @@ static int qed_set_link(struct qed_dev *cdev, struct qed_link_params *params)
		memcpy(&link_params->eee, &params->eee,
		       sizeof(link_params->eee));

	if (params->override_flags & QED_LINK_OVERRIDE_FEC_CONFIG)
		link_params->fec = params->fec;

	rc = qed_mcp_set_link(hwfn, ptt, params->link_up);

	qed_ptt_release(hwfn, ptt);
@@ -1938,6 +1941,9 @@ static void qed_fill_link(struct qed_hwfn *hwfn,
	else
		phylink_clear(if_link->advertised_caps, Autoneg);

	if_link->sup_fec = link_caps.fec_default;
	if_link->active_fec = params.fec;

	/* Fill link advertised capability */
	qed_fill_link_capability(hwfn, ptt, params.speed.advertised_speeds,
				 if_link->advertised_caps);
+39 −8
Original line number Diff line number Diff line
@@ -1446,6 +1446,25 @@ static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn,
	if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE)
		qed_mcp_read_eee_config(p_hwfn, p_ptt, p_link);

	if (p_hwfn->mcp_info->capabilities &
	    FW_MB_PARAM_FEATURE_SUPPORT_FEC_CONTROL) {
		switch (status & LINK_STATUS_FEC_MODE_MASK) {
		case LINK_STATUS_FEC_MODE_NONE:
			p_link->fec_active = QED_FEC_MODE_NONE;
			break;
		case LINK_STATUS_FEC_MODE_FIRECODE_CL74:
			p_link->fec_active = QED_FEC_MODE_FIRECODE;
			break;
		case LINK_STATUS_FEC_MODE_RS_CL91:
			p_link->fec_active = QED_FEC_MODE_RS;
			break;
		default:
			p_link->fec_active = QED_FEC_MODE_AUTO;
		}
	} else {
		p_link->fec_active = QED_FEC_MODE_UNSUPPORTED;
	}

	qed_link_update(p_hwfn, p_ptt);
out:
	spin_unlock_bh(&p_hwfn->mcp_info->link_lock);
@@ -1456,8 +1475,8 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up)
	struct qed_mcp_link_params *params = &p_hwfn->mcp_info->link_input;
	struct qed_mcp_mb_params mb_params;
	struct eth_phy_cfg phy_cfg;
	u32 cmd, fec_bit = 0;
	int rc = 0;
	u32 cmd;

	/* Set the shmem configuration according to params */
	memset(&phy_cfg, 0, sizeof(phy_cfg));
@@ -1489,16 +1508,27 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up)
				   EEE_TX_TIMER_USEC_MASK;
	}

	if (p_hwfn->mcp_info->capabilities &
	    FW_MB_PARAM_FEATURE_SUPPORT_FEC_CONTROL) {
		if (params->fec & QED_FEC_MODE_NONE)
			fec_bit |= FEC_FORCE_MODE_NONE;
		else if (params->fec & QED_FEC_MODE_FIRECODE)
			fec_bit |= FEC_FORCE_MODE_FIRECODE;
		else if (params->fec & QED_FEC_MODE_RS)
			fec_bit |= FEC_FORCE_MODE_RS;
		else if (params->fec & QED_FEC_MODE_AUTO)
			fec_bit |= FEC_FORCE_MODE_AUTO;

		SET_MFW_FIELD(phy_cfg.fec_mode, FEC_FORCE_MODE, fec_bit);
	}

	p_hwfn->b_drv_link_init = b_up;

	if (b_up) {
		DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
			   "Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, features 0x%08x\n",
			   phy_cfg.speed,
			   phy_cfg.pause,
			   phy_cfg.adv_speed,
			   phy_cfg.loopback_mode,
			   phy_cfg.feature_config_flags);
			   "Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, FEC 0x%08x\n",
			   phy_cfg.speed, phy_cfg.pause, phy_cfg.adv_speed,
			   phy_cfg.loopback_mode, phy_cfg.fec_mode);
	} else {
		DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
			   "Resetting link\n");
@@ -3805,7 +3835,8 @@ int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
	u32 mcp_resp, mcp_param, features;

	features = DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE |
		   DRV_MB_PARAM_FEATURE_SUPPORT_FUNC_VLINK;
		   DRV_MB_PARAM_FEATURE_SUPPORT_FUNC_VLINK |
		   DRV_MB_PARAM_FEATURE_SUPPORT_PORT_FEC_CONTROL;

	return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_FEATURE_SUPPORT,
			   features, &mcp_resp, &mcp_param);
+4 −0
Original line number Diff line number Diff line
@@ -38,11 +38,13 @@ struct qed_mcp_link_params {
	struct qed_mcp_link_pause_params	pause;
	u32					loopback_mode;
	struct qed_link_eee_params		eee;
	u32					fec;
};

struct qed_mcp_link_capabilities {
	u32					speed_capabilities;
	bool					default_speed_autoneg;
	u32					fec_default;
	enum qed_mcp_eee_mode			default_eee;
	u32					eee_lpi_timer;
	u8					eee_speed_caps;
@@ -88,6 +90,8 @@ struct qed_mcp_link_state {
	bool					eee_active;
	u8					eee_adv_caps;
	u8					eee_lp_adv_caps;

	u32					fec_active;
};

struct qed_mcp_function_info {
Loading