Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mcdi_phy.c
Go to the documentation of this file.
1 /****************************************************************************
2  * Driver for Solarflare Solarstorm network controllers and boards
3  * Copyright 2009-2010 Solarflare Communications Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published
7  * by the Free Software Foundation, incorporated herein by reference.
8  */
9 
10 /*
11  * Driver for PHY related operations via MCDI.
12  */
13 
14 #include <linux/slab.h>
15 #include "efx.h"
16 #include "phy.h"
17 #include "mcdi.h"
18 #include "mcdi_pcol.h"
19 #include "nic.h"
20 #include "selftest.h"
21 
29  u8 name[20];
32  u8 revision[20];
34 };
35 
36 static int
37 efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_data *cfg)
38 {
40  size_t outlen;
41  int rc;
42 
45 
46  rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0,
47  outbuf, sizeof(outbuf), &outlen);
48  if (rc)
49  goto fail;
50 
51  if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) {
52  rc = -EIO;
53  goto fail;
54  }
55 
56  cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS);
57  cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE);
58  cfg->supported_cap =
59  MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP);
60  cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL);
61  cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT);
62  cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK);
63  memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME),
64  sizeof(cfg->name));
65  cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE);
66  cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK);
67  memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION),
68  sizeof(cfg->revision));
69 
70  return 0;
71 
72 fail:
73  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
74  return rc;
75 }
76 
77 static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities,
78  u32 flags, u32 loopback_mode,
79  u32 loopback_speed)
80 {
82  int rc;
83 
85 
86  MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities);
87  MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags);
88  MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode);
89  MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed);
90 
91  rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf),
92  NULL, 0, NULL);
93  if (rc)
94  goto fail;
95 
96  return 0;
97 
98 fail:
99  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
100  return rc;
101 }
102 
103 static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes)
104 {
106  size_t outlen;
107  int rc;
108 
110  outbuf, sizeof(outbuf), &outlen);
111  if (rc)
112  goto fail;
113 
114  if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) {
115  rc = -EIO;
116  goto fail;
117  }
118 
119  *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_OUT_SUGGESTED);
120 
121  return 0;
122 
123 fail:
124  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
125  return rc;
126 }
127 
128 int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus,
129  unsigned int prtad, unsigned int devad, u16 addr,
130  u16 *value_out, u32 *status_out)
131 {
134  size_t outlen;
135  int rc;
136 
137  MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus);
138  MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad);
139  MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad);
140  MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr);
141 
142  rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf),
143  outbuf, sizeof(outbuf), &outlen);
144  if (rc)
145  goto fail;
146 
147  *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE);
148  *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS);
149  return 0;
150 
151 fail:
152  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
153  return rc;
154 }
155 
156 int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus,
157  unsigned int prtad, unsigned int devad, u16 addr,
158  u16 value, u32 *status_out)
159 {
162  size_t outlen;
163  int rc;
164 
165  MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus);
166  MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad);
167  MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad);
168  MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr);
169  MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value);
170 
171  rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf),
172  outbuf, sizeof(outbuf), &outlen);
173  if (rc)
174  goto fail;
175 
176  *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS);
177  return 0;
178 
179 fail:
180  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
181  return rc;
182 }
183 
184 static u32 mcdi_to_ethtool_cap(u32 media, u32 cap)
185 {
186  u32 result = 0;
187 
188  switch (media) {
189  case MC_CMD_MEDIA_KX4:
190  result |= SUPPORTED_Backplane;
191  if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
192  result |= SUPPORTED_1000baseKX_Full;
193  if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
194  result |= SUPPORTED_10000baseKX4_Full;
195  break;
196 
197  case MC_CMD_MEDIA_XFP:
199  result |= SUPPORTED_FIBRE;
200  break;
201 
202  case MC_CMD_MEDIA_BASE_T:
203  result |= SUPPORTED_TP;
204  if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
205  result |= SUPPORTED_10baseT_Half;
206  if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
207  result |= SUPPORTED_10baseT_Full;
208  if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
209  result |= SUPPORTED_100baseT_Half;
210  if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
211  result |= SUPPORTED_100baseT_Full;
212  if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
213  result |= SUPPORTED_1000baseT_Half;
214  if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
215  result |= SUPPORTED_1000baseT_Full;
216  if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
217  result |= SUPPORTED_10000baseT_Full;
218  break;
219  }
220 
221  if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
222  result |= SUPPORTED_Pause;
223  if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
224  result |= SUPPORTED_Asym_Pause;
225  if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
226  result |= SUPPORTED_Autoneg;
227 
228  return result;
229 }
230 
231 static u32 ethtool_to_mcdi_cap(u32 cap)
232 {
233  u32 result = 0;
234 
235  if (cap & SUPPORTED_10baseT_Half)
236  result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN);
237  if (cap & SUPPORTED_10baseT_Full)
238  result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN);
239  if (cap & SUPPORTED_100baseT_Half)
240  result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN);
241  if (cap & SUPPORTED_100baseT_Full)
242  result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN);
243  if (cap & SUPPORTED_1000baseT_Half)
244  result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN);
246  result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN);
248  result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN);
249  if (cap & SUPPORTED_Pause)
250  result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN);
251  if (cap & SUPPORTED_Asym_Pause)
252  result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN);
253  if (cap & SUPPORTED_Autoneg)
254  result |= (1 << MC_CMD_PHY_CAP_AN_LBN);
255 
256  return result;
257 }
258 
259 static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx)
260 {
261  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
262  enum efx_phy_mode mode, supported;
263  u32 flags;
264 
265  /* TODO: Advertise the capabilities supported by this PHY */
266  supported = 0;
267  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_TXDIS_LBN))
268  supported |= PHY_MODE_TX_DISABLED;
269  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_LBN))
270  supported |= PHY_MODE_LOW_POWER;
271  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_POWEROFF_LBN))
272  supported |= PHY_MODE_OFF;
273 
274  mode = efx->phy_mode & supported;
275 
276  flags = 0;
277  if (mode & PHY_MODE_TX_DISABLED)
278  flags |= (1 << MC_CMD_SET_LINK_IN_TXDIS_LBN);
279  if (mode & PHY_MODE_LOW_POWER)
280  flags |= (1 << MC_CMD_SET_LINK_IN_LOWPOWER_LBN);
281  if (mode & PHY_MODE_OFF)
282  flags |= (1 << MC_CMD_SET_LINK_IN_POWEROFF_LBN);
283 
284  return flags;
285 }
286 
287 static u32 mcdi_to_ethtool_media(u32 media)
288 {
289  switch (media) {
290  case MC_CMD_MEDIA_XAUI:
291  case MC_CMD_MEDIA_CX4:
292  case MC_CMD_MEDIA_KX4:
293  return PORT_OTHER;
294 
295  case MC_CMD_MEDIA_XFP:
297  return PORT_FIBRE;
298 
299  case MC_CMD_MEDIA_BASE_T:
300  return PORT_TP;
301 
302  default:
303  return PORT_OTHER;
304  }
305 }
306 
307 static int efx_mcdi_phy_probe(struct efx_nic *efx)
308 {
309  struct efx_mcdi_phy_data *phy_data;
310  u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
311  u32 caps;
312  int rc;
313 
314  /* Initialise and populate phy_data */
315  phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
316  if (phy_data == NULL)
317  return -ENOMEM;
318 
319  rc = efx_mcdi_get_phy_cfg(efx, phy_data);
320  if (rc != 0)
321  goto fail;
322 
323  /* Read initial link advertisement */
325  rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
326  outbuf, sizeof(outbuf), NULL);
327  if (rc)
328  goto fail;
329 
330  /* Fill out nic state */
331  efx->phy_data = phy_data;
332  efx->phy_type = phy_data->type;
333 
334  efx->mdio_bus = phy_data->channel;
335  efx->mdio.prtad = phy_data->port;
336  efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22);
337  efx->mdio.mode_support = 0;
338  if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22))
339  efx->mdio.mode_support |= MDIO_SUPPORTS_C22;
340  if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22))
341  efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
342 
343  caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP);
344  if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN))
345  efx->link_advertising =
346  mcdi_to_ethtool_cap(phy_data->media, caps);
347  else
348  phy_data->forced_cap = caps;
349 
350  /* Assert that we can map efx -> mcdi loopback modes */
378 
379  rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes);
380  if (rc != 0)
381  goto fail;
382  /* The MC indicates that LOOPBACK_NONE is a valid loopback mode,
383  * but by convention we don't */
384  efx->loopback_modes &= ~(1 << LOOPBACK_NONE);
385 
386  /* Set the initial link mode */
388  efx, &efx->link_state,
389  MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
390  MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
391  MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
392 
393  /* Default to Autonegotiated flow control if the PHY supports it */
394  efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
395  if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
396  efx->wanted_fc |= EFX_FC_AUTO;
398 
399  return 0;
400 
401 fail:
402  kfree(phy_data);
403  return rc;
404 }
405 
407 {
408  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
409  u32 caps = (efx->link_advertising ?
410  ethtool_to_mcdi_cap(efx->link_advertising) :
411  phy_cfg->forced_cap);
412 
413  return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
414  efx->loopback_mode, 0);
415 }
416 
418  struct efx_link_state *link_state,
419  u32 speed, u32 flags, u32 fcntl)
420 {
421  switch (fcntl) {
422  case MC_CMD_FCNTL_AUTO:
423  WARN_ON(1); /* This is not a link mode */
424  link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX;
425  break;
426  case MC_CMD_FCNTL_BIDIR:
427  link_state->fc = EFX_FC_TX | EFX_FC_RX;
428  break;
430  link_state->fc = EFX_FC_RX;
431  break;
432  default:
433  WARN_ON(1);
434  case MC_CMD_FCNTL_OFF:
435  link_state->fc = 0;
436  break;
437  }
438 
439  link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN));
440  link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN));
441  link_state->speed = speed;
442 }
443 
444 /* Verify that the forced flow control settings (!EFX_FC_AUTO) are
445  * supported by the link partner. Warn the user if this isn't the case
446  */
447 void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa)
448 {
449  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
450  u32 rmtadv;
451 
452  /* The link partner capabilities are only relevant if the
453  * link supports flow control autonegotiation */
454  if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
455  return;
456 
457  /* If flow control autoneg is supported and enabled, then fine */
458  if (efx->wanted_fc & EFX_FC_AUTO)
459  return;
460 
461  rmtadv = 0;
462  if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
463  rmtadv |= ADVERTISED_Pause;
464  if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
465  rmtadv |= ADVERTISED_Asym_Pause;
466 
467  if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause)
468  netif_err(efx, link, efx->net_dev,
469  "warning: link partner doesn't support pause frames");
470 }
471 
472 static bool efx_mcdi_phy_poll(struct efx_nic *efx)
473 {
474  struct efx_link_state old_state = efx->link_state;
475  u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
476  int rc;
477 
478  WARN_ON(!mutex_is_locked(&efx->mac_lock));
479 
481 
482  rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
483  outbuf, sizeof(outbuf), NULL);
484  if (rc) {
485  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
486  __func__, rc);
487  efx->link_state.up = false;
488  } else {
490  efx, &efx->link_state,
491  MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
492  MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
493  MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
494  }
495 
496  return !efx_link_state_equal(&efx->link_state, &old_state);
497 }
498 
499 static void efx_mcdi_phy_remove(struct efx_nic *efx)
500 {
501  struct efx_mcdi_phy_data *phy_data = efx->phy_data;
502 
503  efx->phy_data = NULL;
504  kfree(phy_data);
505 }
506 
507 static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
508 {
509  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
510  u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
511  int rc;
512 
513  ecmd->supported =
514  mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap);
515  ecmd->advertising = efx->link_advertising;
516  ethtool_cmd_speed_set(ecmd, efx->link_state.speed);
517  ecmd->duplex = efx->link_state.fd;
518  ecmd->port = mcdi_to_ethtool_media(phy_cfg->media);
519  ecmd->phy_address = phy_cfg->port;
520  ecmd->transceiver = XCVR_INTERNAL;
521  ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg);
522  ecmd->mdio_support = (efx->mdio.mode_support &
524 
526  rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
527  outbuf, sizeof(outbuf), NULL);
528  if (rc) {
529  netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
530  __func__, rc);
531  return;
532  }
533  ecmd->lp_advertising =
534  mcdi_to_ethtool_cap(phy_cfg->media,
535  MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP));
536 }
537 
538 static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
539 {
540  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
541  u32 caps;
542  int rc;
543 
544  if (ecmd->autoneg) {
545  caps = (ethtool_to_mcdi_cap(ecmd->advertising) |
546  1 << MC_CMD_PHY_CAP_AN_LBN);
547  } else if (ecmd->duplex) {
548  switch (ethtool_cmd_speed(ecmd)) {
549  case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break;
550  case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break;
551  case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break;
552  case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break;
553  default: return -EINVAL;
554  }
555  } else {
556  switch (ethtool_cmd_speed(ecmd)) {
557  case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break;
558  case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break;
559  case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break;
560  default: return -EINVAL;
561  }
562  }
563 
564  rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
565  efx->loopback_mode, 0);
566  if (rc)
567  return rc;
568 
569  if (ecmd->autoneg) {
571  efx, ecmd->advertising | ADVERTISED_Autoneg);
572  phy_cfg->forced_cap = 0;
573  } else {
574  efx_link_set_advertising(efx, 0);
575  phy_cfg->forced_cap = caps;
576  }
577  return 0;
578 }
579 
580 static int efx_mcdi_phy_test_alive(struct efx_nic *efx)
581 {
583  size_t outlen;
584  int rc;
585 
587 
588  rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, 0,
589  outbuf, sizeof(outbuf), &outlen);
590  if (rc)
591  return rc;
592 
593  if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN)
594  return -EIO;
595  if (MCDI_DWORD(outbuf, GET_PHY_STATE_OUT_STATE) != MC_CMD_PHY_STATE_OK)
596  return -EINVAL;
597 
598  return 0;
599 }
600 
601 static const char *const mcdi_sft9001_cable_diag_names[] = {
602  "cable.pairA.length",
603  "cable.pairB.length",
604  "cable.pairC.length",
605  "cable.pairD.length",
606  "cable.pairA.status",
607  "cable.pairB.status",
608  "cable.pairC.status",
609  "cable.pairD.status",
610 };
611 
612 static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode,
613  int *results)
614 {
615  unsigned int retry, i, count = 0;
616  size_t outlen;
617  u32 status;
618  u8 *buf, *ptr;
619  int rc;
620 
621  buf = kzalloc(0x100, GFP_KERNEL);
622  if (buf == NULL)
623  return -ENOMEM;
624 
626  MCDI_SET_DWORD(buf, START_BIST_IN_TYPE, bist_mode);
628  NULL, 0, NULL);
629  if (rc)
630  goto out;
631 
632  /* Wait up to 10s for BIST to finish */
633  for (retry = 0; retry < 100; ++retry) {
635  rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0,
636  buf, 0x100, &outlen);
637  if (rc)
638  goto out;
639 
640  status = MCDI_DWORD(buf, POLL_BIST_OUT_RESULT);
641  if (status != MC_CMD_POLL_BIST_RUNNING)
642  goto finished;
643 
644  msleep(100);
645  }
646 
647  rc = -ETIMEDOUT;
648  goto out;
649 
650 finished:
651  results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1;
652 
653  /* SFT9001 specific cable diagnostics output */
654  if (efx->phy_type == PHY_TYPE_SFT9001B &&
655  (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT ||
656  bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) {
657  ptr = MCDI_PTR(buf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A);
658  if (status == MC_CMD_POLL_BIST_PASSED &&
660  for (i = 0; i < 8; i++) {
661  results[count + i] =
662  EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i],
663  EFX_DWORD_0);
664  }
665  }
666  count += 8;
667  }
668  rc = count;
669 
670 out:
671  kfree(buf);
672 
673  return rc;
674 }
675 
676 static int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results,
677  unsigned flags)
678 {
679  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
680  u32 mode;
681  int rc;
682 
683  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
684  rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results);
685  if (rc < 0)
686  return rc;
687 
688  results += rc;
689  }
690 
691  /* If we support both LONG and SHORT, then run each in response to
692  * break or not. Otherwise, run the one we support */
693  mode = 0;
694  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN)) {
695  if ((flags & ETH_TEST_FL_OFFLINE) &&
696  (phy_cfg->flags &
699  else
701  } else if (phy_cfg->flags &
704 
705  if (mode != 0) {
706  rc = efx_mcdi_bist(efx, mode, results);
707  if (rc < 0)
708  return rc;
709  results += rc;
710  }
711 
712  return 0;
713 }
714 
715 static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
716  unsigned int index)
717 {
718  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
719 
720  if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
721  if (index == 0)
722  return "bist";
723  --index;
724  }
725 
726  if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN) |
728  if (index == 0)
729  return "cable";
730  --index;
731 
732  if (efx->phy_type == PHY_TYPE_SFT9001B) {
733  if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names))
734  return mcdi_sft9001_cable_diag_names[index];
735  index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names);
736  }
737  }
738 
739  return NULL;
740 }
741 
742 #define SFP_PAGE_SIZE 128
743 #define SFP_NUM_PAGES 2
744 static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
745  struct ethtool_eeprom *ee, u8 *data)
746 {
749  size_t outlen;
750  int rc;
751  unsigned int payload_len;
752  unsigned int space_remaining = ee->len;
753  unsigned int page;
754  unsigned int page_off;
755  unsigned int to_copy;
756  u8 *user_data = data;
757 
759 
760  page_off = ee->offset % SFP_PAGE_SIZE;
761  page = ee->offset / SFP_PAGE_SIZE;
762 
763  while (space_remaining && (page < SFP_NUM_PAGES)) {
764  MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
765 
767  inbuf, sizeof(inbuf),
768  outbuf, sizeof(outbuf),
769  &outlen);
770  if (rc)
771  return rc;
772 
774  SFP_PAGE_SIZE))
775  return -EIO;
776 
777  payload_len = MCDI_DWORD(outbuf,
778  GET_PHY_MEDIA_INFO_OUT_DATALEN);
779  if (payload_len != SFP_PAGE_SIZE)
780  return -EIO;
781 
782  /* Copy as much as we can into data */
783  payload_len -= page_off;
784  to_copy = (space_remaining < payload_len) ?
785  space_remaining : payload_len;
786 
787  memcpy(user_data,
788  outbuf + page_off +
790  to_copy);
791 
792  space_remaining -= to_copy;
793  user_data += to_copy;
794  page_off = 0;
795  page++;
796  }
797 
798  return 0;
799 }
800 
801 static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
802  struct ethtool_modinfo *modinfo)
803 {
804  struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
805 
806  switch (phy_cfg->media) {
808  modinfo->type = ETH_MODULE_SFF_8079;
810  return 0;
811  default:
812  return -EOPNOTSUPP;
813  }
814 }
815 
817  .probe = efx_mcdi_phy_probe,
818  .init = efx_port_dummy_op_int,
819  .reconfigure = efx_mcdi_phy_reconfigure,
820  .poll = efx_mcdi_phy_poll,
821  .fini = efx_port_dummy_op_void,
822  .remove = efx_mcdi_phy_remove,
823  .get_settings = efx_mcdi_phy_get_settings,
824  .set_settings = efx_mcdi_phy_set_settings,
825  .test_alive = efx_mcdi_phy_test_alive,
826  .run_tests = efx_mcdi_phy_run_tests,
827  .test_name = efx_mcdi_phy_test_name,
828  .get_module_eeprom = efx_mcdi_phy_get_module_eeprom,
829  .get_module_info = efx_mcdi_phy_get_module_info,
830 };