Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/****************************************************************************
  3 * Driver for Solarflare network controllers and boards
  4 * Copyright 2009-2013 Solarflare Communications Inc.
  5 */
  6
  7/*
  8 * Driver for PHY related operations via MCDI.
  9 */
 10
 11#include <linux/slab.h>
 12#include "efx.h"
 13#include "mcdi_port.h"
 14#include "mcdi.h"
 15#include "mcdi_pcol.h"
 16#include "nic.h"
 17#include "selftest.h"
 18#include "mcdi_port_common.h"
 19
 20static int efx_mcdi_mdio_read(struct net_device *net_dev,
 21			      int prtad, int devad, u16 addr)
 22{
 23	struct efx_nic *efx = efx_netdev_priv(net_dev);
 24	MCDI_DECLARE_BUF(inbuf, MC_CMD_MDIO_READ_IN_LEN);
 25	MCDI_DECLARE_BUF(outbuf, MC_CMD_MDIO_READ_OUT_LEN);
 26	size_t outlen;
 27	int rc;
 28
 29	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, efx->mdio_bus);
 30	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad);
 31	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad);
 32	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr);
 33
 34	rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf),
 35			  outbuf, sizeof(outbuf), &outlen);
 36	if (rc)
 37		return rc;
 38
 39	if (MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS) !=
 40	    MC_CMD_MDIO_STATUS_GOOD)
 41		return -EIO;
 42
 43	return (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE);
 44}
 45
 46static int efx_mcdi_mdio_write(struct net_device *net_dev,
 47			       int prtad, int devad, u16 addr, u16 value)
 48{
 49	struct efx_nic *efx = efx_netdev_priv(net_dev);
 50	MCDI_DECLARE_BUF(inbuf, MC_CMD_MDIO_WRITE_IN_LEN);
 51	MCDI_DECLARE_BUF(outbuf, MC_CMD_MDIO_WRITE_OUT_LEN);
 52	size_t outlen;
 53	int rc;
 54
 55	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, efx->mdio_bus);
 56	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad);
 57	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad);
 58	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr);
 59	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value);
 60
 61	rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf),
 62			  outbuf, sizeof(outbuf), &outlen);
 63	if (rc)
 64		return rc;
 65
 66	if (MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS) !=
 67	    MC_CMD_MDIO_STATUS_GOOD)
 68		return -EIO;
 69
 70	return 0;
 71}
 72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 73u32 efx_mcdi_phy_get_caps(struct efx_nic *efx)
 74{
 75	struct efx_mcdi_phy_data *phy_data = efx->phy_data;
 76
 77	return phy_data->supported_cap;
 78}
 79
 80bool efx_mcdi_mac_check_fault(struct efx_nic *efx)
 81{
 82	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
 83	size_t outlength;
 84	int rc;
 85
 86	BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
 87
 88	rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
 89			  outbuf, sizeof(outbuf), &outlength);
 90	if (rc)
 91		return true;
 92
 93	return MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT) != 0;
 94}
 95
 96int efx_mcdi_port_probe(struct efx_nic *efx)
 97{
 98	int rc;
 99
 
 
 
100	/* Set up MDIO structure for PHY */
101	efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
102	efx->mdio.mdio_read = efx_mcdi_mdio_read;
103	efx->mdio.mdio_write = efx_mcdi_mdio_write;
104
105	/* Fill out MDIO structure, loopback modes, and initial link state */
106	rc = efx_mcdi_phy_probe(efx);
107	if (rc != 0)
108		return rc;
109
110	return efx_mcdi_mac_init_stats(efx);
111}
112
113void efx_mcdi_port_remove(struct efx_nic *efx)
114{
115	efx_mcdi_phy_remove(efx);
116	efx_mcdi_mac_fini_stats(efx);
117}
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/****************************************************************************
  3 * Driver for Solarflare network controllers and boards
  4 * Copyright 2009-2013 Solarflare Communications Inc.
  5 */
  6
  7/*
  8 * Driver for PHY related operations via MCDI.
  9 */
 10
 11#include <linux/slab.h>
 12#include "efx.h"
 13#include "mcdi_port.h"
 14#include "mcdi.h"
 15#include "mcdi_pcol.h"
 16#include "nic.h"
 17#include "selftest.h"
 18#include "mcdi_port_common.h"
 19
 20static int efx_mcdi_mdio_read(struct net_device *net_dev,
 21			      int prtad, int devad, u16 addr)
 22{
 23	struct efx_nic *efx = netdev_priv(net_dev);
 24	MCDI_DECLARE_BUF(inbuf, MC_CMD_MDIO_READ_IN_LEN);
 25	MCDI_DECLARE_BUF(outbuf, MC_CMD_MDIO_READ_OUT_LEN);
 26	size_t outlen;
 27	int rc;
 28
 29	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, efx->mdio_bus);
 30	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad);
 31	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad);
 32	MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr);
 33
 34	rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf),
 35			  outbuf, sizeof(outbuf), &outlen);
 36	if (rc)
 37		return rc;
 38
 39	if (MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS) !=
 40	    MC_CMD_MDIO_STATUS_GOOD)
 41		return -EIO;
 42
 43	return (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE);
 44}
 45
 46static int efx_mcdi_mdio_write(struct net_device *net_dev,
 47			       int prtad, int devad, u16 addr, u16 value)
 48{
 49	struct efx_nic *efx = netdev_priv(net_dev);
 50	MCDI_DECLARE_BUF(inbuf, MC_CMD_MDIO_WRITE_IN_LEN);
 51	MCDI_DECLARE_BUF(outbuf, MC_CMD_MDIO_WRITE_OUT_LEN);
 52	size_t outlen;
 53	int rc;
 54
 55	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, efx->mdio_bus);
 56	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad);
 57	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad);
 58	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr);
 59	MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value);
 60
 61	rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf),
 62			  outbuf, sizeof(outbuf), &outlen);
 63	if (rc)
 64		return rc;
 65
 66	if (MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS) !=
 67	    MC_CMD_MDIO_STATUS_GOOD)
 68		return -EIO;
 69
 70	return 0;
 71}
 72
 73static int efx_mcdi_phy_probe(struct efx_nic *efx)
 74{
 75	struct efx_mcdi_phy_data *phy_data;
 76	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
 77	u32 caps;
 78	int rc;
 79
 80	/* Initialise and populate phy_data */
 81	phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
 82	if (phy_data == NULL)
 83		return -ENOMEM;
 84
 85	rc = efx_mcdi_get_phy_cfg(efx, phy_data);
 86	if (rc != 0)
 87		goto fail;
 88
 89	/* Read initial link advertisement */
 90	BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
 91	rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
 92			  outbuf, sizeof(outbuf), NULL);
 93	if (rc)
 94		goto fail;
 95
 96	/* Fill out nic state */
 97	efx->phy_data = phy_data;
 98	efx->phy_type = phy_data->type;
 99
100	efx->mdio_bus = phy_data->channel;
101	efx->mdio.prtad = phy_data->port;
102	efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22);
103	efx->mdio.mode_support = 0;
104	if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22))
105		efx->mdio.mode_support |= MDIO_SUPPORTS_C22;
106	if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22))
107		efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
108
109	caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP);
110	if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN))
111		mcdi_to_ethtool_linkset(phy_data->media, caps,
112					efx->link_advertising);
113	else
114		phy_data->forced_cap = caps;
115
116	/* Assert that we can map efx -> mcdi loopback modes */
117	BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE);
118	BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA);
119	BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC);
120	BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII);
121	BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS);
122	BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI);
123	BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII);
124	BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII);
125	BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR);
126	BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI);
127	BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR);
128	BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR);
129	BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR);
130	BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR);
131	BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY);
132	BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS);
133	BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS);
134	BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD);
135	BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT);
136	BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS);
137	BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS);
138	BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR);
139	BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR);
140	BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS);
141	BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS);
142	BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR);
143	BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS);
144
145	rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes);
146	if (rc != 0)
147		goto fail;
148	/* The MC indicates that LOOPBACK_NONE is a valid loopback mode,
149	 * but by convention we don't */
150	efx->loopback_modes &= ~(1 << LOOPBACK_NONE);
151
152	/* Set the initial link mode */
153	efx_mcdi_phy_decode_link(
154		efx, &efx->link_state,
155		MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
156		MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
157		MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
158
159	/* Record the initial FEC configuration (or nearest approximation
160	 * representable in the ethtool configuration space)
161	 */
162	efx->fec_config = mcdi_fec_caps_to_ethtool(caps,
163						   efx->link_state.speed == 25000 ||
164						   efx->link_state.speed == 50000);
165
166	/* Default to Autonegotiated flow control if the PHY supports it */
167	efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
168	if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
169		efx->wanted_fc |= EFX_FC_AUTO;
170	efx_link_set_wanted_fc(efx, efx->wanted_fc);
171
172	return 0;
173
174fail:
175	kfree(phy_data);
176	return rc;
177}
178
179static void efx_mcdi_phy_remove(struct efx_nic *efx)
180{
181	struct efx_mcdi_phy_data *phy_data = efx->phy_data;
182
183	efx->phy_data = NULL;
184	kfree(phy_data);
185}
186
187static void efx_mcdi_phy_get_link_ksettings(struct efx_nic *efx,
188					    struct ethtool_link_ksettings *cmd)
189{
190	struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
191	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
192	int rc;
193
194	cmd->base.speed = efx->link_state.speed;
195	cmd->base.duplex = efx->link_state.fd;
196	cmd->base.port = mcdi_to_ethtool_media(phy_cfg->media);
197	cmd->base.phy_address = phy_cfg->port;
198	cmd->base.autoneg = !!(efx->link_advertising[0] & ADVERTISED_Autoneg);
199	cmd->base.mdio_support = (efx->mdio.mode_support &
200			      (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22));
201
202	mcdi_to_ethtool_linkset(phy_cfg->media, phy_cfg->supported_cap,
203				cmd->link_modes.supported);
204	memcpy(cmd->link_modes.advertising, efx->link_advertising,
205	       sizeof(__ETHTOOL_DECLARE_LINK_MODE_MASK()));
206
207	BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
208	rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
209			  outbuf, sizeof(outbuf), NULL);
210	if (rc)
211		return;
212	mcdi_to_ethtool_linkset(phy_cfg->media,
213				MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP),
214				cmd->link_modes.lp_advertising);
215}
216
217static int
218efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx,
219				const struct ethtool_link_ksettings *cmd)
220{
221	struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
222	u32 caps;
223	int rc;
224
225	if (cmd->base.autoneg) {
226		caps = (ethtool_linkset_to_mcdi_cap(cmd->link_modes.advertising) |
227			1 << MC_CMD_PHY_CAP_AN_LBN);
228	} else if (cmd->base.duplex) {
229		switch (cmd->base.speed) {
230		case 10:     caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN;     break;
231		case 100:    caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN;    break;
232		case 1000:   caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN;   break;
233		case 10000:  caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN;  break;
234		case 40000:  caps = 1 << MC_CMD_PHY_CAP_40000FDX_LBN;  break;
235		case 100000: caps = 1 << MC_CMD_PHY_CAP_100000FDX_LBN; break;
236		case 25000:  caps = 1 << MC_CMD_PHY_CAP_25000FDX_LBN;  break;
237		case 50000:  caps = 1 << MC_CMD_PHY_CAP_50000FDX_LBN;  break;
238		default:     return -EINVAL;
239		}
240	} else {
241		switch (cmd->base.speed) {
242		case 10:     caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN;     break;
243		case 100:    caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN;    break;
244		case 1000:   caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN;   break;
245		default:     return -EINVAL;
246		}
247	}
248
249	caps |= ethtool_fec_caps_to_mcdi(efx->fec_config);
250
251	rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
252			       efx->loopback_mode, 0);
253	if (rc)
254		return rc;
255
256	if (cmd->base.autoneg) {
257		efx_link_set_advertising(efx, cmd->link_modes.advertising);
258		phy_cfg->forced_cap = 0;
259	} else {
260		efx_link_clear_advertising(efx);
261		phy_cfg->forced_cap = caps;
262	}
263	return 0;
264}
265
266static int efx_mcdi_phy_set_fecparam(struct efx_nic *efx,
267				     const struct ethtool_fecparam *fec)
268{
269	struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
270	u32 caps;
271	int rc;
272
273	/* Work out what efx_mcdi_phy_set_link_ksettings() would produce from
274	 * saved advertising bits
275	 */
276	if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, efx->link_advertising))
277		caps = (ethtool_linkset_to_mcdi_cap(efx->link_advertising) |
278			1 << MC_CMD_PHY_CAP_AN_LBN);
279	else
280		caps = phy_cfg->forced_cap;
281
282	caps |= ethtool_fec_caps_to_mcdi(fec->fec);
283	rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
284			       efx->loopback_mode, 0);
285	if (rc)
286		return rc;
287
288	/* Record the new FEC setting for subsequent set_link calls */
289	efx->fec_config = fec->fec;
290	return 0;
291}
292
293static const char *const mcdi_sft9001_cable_diag_names[] = {
294	"cable.pairA.length",
295	"cable.pairB.length",
296	"cable.pairC.length",
297	"cable.pairD.length",
298	"cable.pairA.status",
299	"cable.pairB.status",
300	"cable.pairC.status",
301	"cable.pairD.status",
302};
303
304static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode,
305			 int *results)
306{
307	unsigned int retry, i, count = 0;
308	size_t outlen;
309	u32 status;
310	MCDI_DECLARE_BUF(inbuf, MC_CMD_START_BIST_IN_LEN);
311	MCDI_DECLARE_BUF(outbuf, MC_CMD_POLL_BIST_OUT_SFT9001_LEN);
312	u8 *ptr;
313	int rc;
314
315	BUILD_BUG_ON(MC_CMD_START_BIST_OUT_LEN != 0);
316	MCDI_SET_DWORD(inbuf, START_BIST_IN_TYPE, bist_mode);
317	rc = efx_mcdi_rpc(efx, MC_CMD_START_BIST,
318			  inbuf, MC_CMD_START_BIST_IN_LEN, NULL, 0, NULL);
319	if (rc)
320		goto out;
321
322	/* Wait up to 10s for BIST to finish */
323	for (retry = 0; retry < 100; ++retry) {
324		BUILD_BUG_ON(MC_CMD_POLL_BIST_IN_LEN != 0);
325		rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0,
326				  outbuf, sizeof(outbuf), &outlen);
327		if (rc)
328			goto out;
329
330		status = MCDI_DWORD(outbuf, POLL_BIST_OUT_RESULT);
331		if (status != MC_CMD_POLL_BIST_RUNNING)
332			goto finished;
333
334		msleep(100);
335	}
336
337	rc = -ETIMEDOUT;
338	goto out;
339
340finished:
341	results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1;
342
343	/* SFT9001 specific cable diagnostics output */
344	if (efx->phy_type == PHY_TYPE_SFT9001B &&
345	    (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT ||
346	     bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) {
347		ptr = MCDI_PTR(outbuf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A);
348		if (status == MC_CMD_POLL_BIST_PASSED &&
349		    outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) {
350			for (i = 0; i < 8; i++) {
351				results[count + i] =
352					EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i],
353							EFX_DWORD_0);
354			}
355		}
356		count += 8;
357	}
358	rc = count;
359
360out:
361	return rc;
362}
363
364static int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results,
365				  unsigned flags)
366{
367	struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
368	u32 mode;
369	int rc;
370
371	if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
372		rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results);
373		if (rc < 0)
374			return rc;
375
376		results += rc;
377	}
378
379	/* If we support both LONG and SHORT, then run each in response to
380	 * break or not. Otherwise, run the one we support */
381	mode = 0;
382	if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN)) {
383		if ((flags & ETH_TEST_FL_OFFLINE) &&
384		    (phy_cfg->flags &
385		     (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN)))
386			mode = MC_CMD_PHY_BIST_CABLE_LONG;
387		else
388			mode = MC_CMD_PHY_BIST_CABLE_SHORT;
389	} else if (phy_cfg->flags &
390		   (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))
391		mode = MC_CMD_PHY_BIST_CABLE_LONG;
392
393	if (mode != 0) {
394		rc = efx_mcdi_bist(efx, mode, results);
395		if (rc < 0)
396			return rc;
397		results += rc;
398	}
399
400	return 0;
401}
402
403static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
404					  unsigned int index)
405{
406	struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
407
408	if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
409		if (index == 0)
410			return "bist";
411		--index;
412	}
413
414	if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN) |
415			      (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))) {
416		if (index == 0)
417			return "cable";
418		--index;
419
420		if (efx->phy_type == PHY_TYPE_SFT9001B) {
421			if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names))
422				return mcdi_sft9001_cable_diag_names[index];
423			index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names);
424		}
425	}
426
427	return NULL;
428}
429
430#define SFP_PAGE_SIZE		128
431#define SFF_DIAG_TYPE_OFFSET	92
432#define SFF_DIAG_ADDR_CHANGE	BIT(2)
433#define SFF_8079_NUM_PAGES	2
434#define SFF_8472_NUM_PAGES	4
435#define SFF_8436_NUM_PAGES	5
436#define SFF_DMT_LEVEL_OFFSET	94
437
438/** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom
439 * @efx:	NIC context
440 * @page:	EEPROM page number
441 * @data:	Destination data pointer
442 * @offset:	Offset in page to copy from in to data
443 * @space:	Space available in data
444 *
445 * Return:
446 *   >=0 - amount of data copied
447 *   <0  - error
448 */
449static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx,
450					       unsigned int page,
451					       u8 *data, ssize_t offset,
452					       ssize_t space)
453{
454	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX);
455	MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN);
456	size_t outlen;
457	unsigned int payload_len;
458	unsigned int to_copy;
459	int rc;
460
461	if (offset > SFP_PAGE_SIZE)
462		return -EINVAL;
463
464	to_copy = min(space, SFP_PAGE_SIZE - offset);
465
466	MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
467	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO,
468				inbuf, sizeof(inbuf),
469				outbuf, sizeof(outbuf),
470				&outlen);
471
472	if (rc)
473		return rc;
474
475	if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
476			SFP_PAGE_SIZE))
477		return -EIO;
478
479	payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN);
480	if (payload_len != SFP_PAGE_SIZE)
481		return -EIO;
482
483	memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
484	       to_copy);
485
486	return to_copy;
487}
488
489static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx,
490					       unsigned int page,
491					       u8 byte)
492{
493	int rc;
494	u8 data;
495
496	rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, &data, byte, 1);
497	if (rc == 1)
498		return data;
499
500	return rc;
501}
502
503static int efx_mcdi_phy_diag_type(struct efx_nic *efx)
504{
505	/* Page zero of the EEPROM includes the diagnostic type at byte 92. */
506	return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
507						   SFF_DIAG_TYPE_OFFSET);
508}
509
510static int efx_mcdi_phy_sff_8472_level(struct efx_nic *efx)
511{
512	/* Page zero of the EEPROM includes the DMT level at byte 94. */
513	return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
514						   SFF_DMT_LEVEL_OFFSET);
515}
516
517static u32 efx_mcdi_phy_module_type(struct efx_nic *efx)
518{
519	struct efx_mcdi_phy_data *phy_data = efx->phy_data;
520
521	if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS)
522		return phy_data->media;
523
524	/* A QSFP+ NIC may actually have an SFP+ module attached.
525	 * The ID is page 0, byte 0.
526	 */
527	switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) {
528	case 0x3:
529		return MC_CMD_MEDIA_SFP_PLUS;
530	case 0xc:
531	case 0xd:
532		return MC_CMD_MEDIA_QSFP_PLUS;
533	default:
534		return 0;
535	}
536}
537
538static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
539					  struct ethtool_eeprom *ee, u8 *data)
540{
541	int rc;
542	ssize_t space_remaining = ee->len;
543	unsigned int page_off;
544	bool ignore_missing;
545	int num_pages;
546	int page;
547
548	switch (efx_mcdi_phy_module_type(efx)) {
549	case MC_CMD_MEDIA_SFP_PLUS:
550		num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ?
551				SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES;
552		page = 0;
553		ignore_missing = false;
554		break;
555	case MC_CMD_MEDIA_QSFP_PLUS:
556		num_pages = SFF_8436_NUM_PAGES;
557		page = -1; /* We obtain the lower page by asking for -1. */
558		ignore_missing = true; /* Ignore missing pages after page 0. */
559		break;
560	default:
561		return -EOPNOTSUPP;
562	}
563
564	page_off = ee->offset % SFP_PAGE_SIZE;
565	page += ee->offset / SFP_PAGE_SIZE;
566
567	while (space_remaining && (page < num_pages)) {
568		rc = efx_mcdi_phy_get_module_eeprom_page(efx, page,
569							 data, page_off,
570							 space_remaining);
571
572		if (rc > 0) {
573			space_remaining -= rc;
574			data += rc;
575			page_off = 0;
576			page++;
577		} else if (rc == 0) {
578			space_remaining = 0;
579		} else if (ignore_missing && (page > 0)) {
580			int intended_size = SFP_PAGE_SIZE - page_off;
581
582			space_remaining -= intended_size;
583			if (space_remaining < 0) {
584				space_remaining = 0;
585			} else {
586				memset(data, 0, intended_size);
587				data += intended_size;
588				page_off = 0;
589				page++;
590				rc = 0;
591			}
592		} else {
593			return rc;
594		}
595	}
596
597	return 0;
598}
599
600static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
601					struct ethtool_modinfo *modinfo)
602{
603	int sff_8472_level;
604	int diag_type;
605
606	switch (efx_mcdi_phy_module_type(efx)) {
607	case MC_CMD_MEDIA_SFP_PLUS:
608		sff_8472_level = efx_mcdi_phy_sff_8472_level(efx);
609
610		/* If we can't read the diagnostics level we have none. */
611		if (sff_8472_level < 0)
612			return -EOPNOTSUPP;
613
614		/* Check if this module requires the (unsupported) address
615		 * change operation.
616		 */
617		diag_type = efx_mcdi_phy_diag_type(efx);
618
619		if ((sff_8472_level == 0) ||
620		    (diag_type & SFF_DIAG_ADDR_CHANGE)) {
621			modinfo->type = ETH_MODULE_SFF_8079;
622			modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
623		} else {
624			modinfo->type = ETH_MODULE_SFF_8472;
625			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
626		}
627		break;
628
629	case MC_CMD_MEDIA_QSFP_PLUS:
630		modinfo->type = ETH_MODULE_SFF_8436;
631		modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
632		break;
633
634	default:
635		return -EOPNOTSUPP;
636	}
637
638	return 0;
639}
640
641static const struct efx_phy_operations efx_mcdi_phy_ops = {
642	.probe		= efx_mcdi_phy_probe,
643	.init		= efx_port_dummy_op_int,
644	.reconfigure	= efx_mcdi_port_reconfigure,
645	.poll		= efx_mcdi_phy_poll,
646	.fini		= efx_port_dummy_op_void,
647	.remove		= efx_mcdi_phy_remove,
648	.get_link_ksettings = efx_mcdi_phy_get_link_ksettings,
649	.set_link_ksettings = efx_mcdi_phy_set_link_ksettings,
650	.get_fecparam	= efx_mcdi_phy_get_fecparam,
651	.set_fecparam	= efx_mcdi_phy_set_fecparam,
652	.test_alive	= efx_mcdi_phy_test_alive,
653	.run_tests	= efx_mcdi_phy_run_tests,
654	.test_name	= efx_mcdi_phy_test_name,
655	.get_module_eeprom = efx_mcdi_phy_get_module_eeprom,
656	.get_module_info = efx_mcdi_phy_get_module_info,
657};
658
659u32 efx_mcdi_phy_get_caps(struct efx_nic *efx)
660{
661	struct efx_mcdi_phy_data *phy_data = efx->phy_data;
662
663	return phy_data->supported_cap;
664}
665
666bool efx_mcdi_mac_check_fault(struct efx_nic *efx)
667{
668	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
669	size_t outlength;
670	int rc;
671
672	BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
673
674	rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
675			  outbuf, sizeof(outbuf), &outlength);
676	if (rc)
677		return true;
678
679	return MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT) != 0;
680}
681
682int efx_mcdi_port_probe(struct efx_nic *efx)
683{
684	int rc;
685
686	/* Hook in PHY operations table */
687	efx->phy_op = &efx_mcdi_phy_ops;
688
689	/* Set up MDIO structure for PHY */
690	efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
691	efx->mdio.mdio_read = efx_mcdi_mdio_read;
692	efx->mdio.mdio_write = efx_mcdi_mdio_write;
693
694	/* Fill out MDIO structure, loopback modes, and initial link state */
695	rc = efx->phy_op->probe(efx);
696	if (rc != 0)
697		return rc;
698
699	return efx_mcdi_mac_init_stats(efx);
700}
701
702void efx_mcdi_port_remove(struct efx_nic *efx)
703{
704	efx->phy_op->remove(efx);
705	efx_mcdi_mac_fini_stats(efx);
706}