1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2007-2016 Solarflare Communications Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *    this list of conditions and the following disclaimer in the documentation
14 *    and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include "efx.h"
37#include "efx_impl.h"
38
39#if EFSYS_OPT_SIENA
40static const efx_phy_ops_t	__efx_phy_siena_ops = {
41	siena_phy_power,		/* epo_power */
42	NULL,				/* epo_reset */
43	siena_phy_reconfigure,		/* epo_reconfigure */
44	siena_phy_verify,		/* epo_verify */
45	siena_phy_oui_get,		/* epo_oui_get */
46	NULL,				/* epo_link_state_get */
47#if EFSYS_OPT_PHY_STATS
48	siena_phy_stats_update,		/* epo_stats_update */
49#endif	/* EFSYS_OPT_PHY_STATS */
50#if EFSYS_OPT_BIST
51	NULL,				/* epo_bist_enable_offline */
52	siena_phy_bist_start,		/* epo_bist_start */
53	siena_phy_bist_poll,		/* epo_bist_poll */
54	siena_phy_bist_stop,		/* epo_bist_stop */
55#endif	/* EFSYS_OPT_BIST */
56};
57#endif	/* EFSYS_OPT_SIENA */
58
59#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
60static const efx_phy_ops_t	__efx_phy_ef10_ops = {
61	ef10_phy_power,			/* epo_power */
62	NULL,				/* epo_reset */
63	ef10_phy_reconfigure,		/* epo_reconfigure */
64	ef10_phy_verify,		/* epo_verify */
65	ef10_phy_oui_get,		/* epo_oui_get */
66	ef10_phy_link_state_get,	/* epo_link_state_get */
67#if EFSYS_OPT_PHY_STATS
68	ef10_phy_stats_update,		/* epo_stats_update */
69#endif	/* EFSYS_OPT_PHY_STATS */
70#if EFSYS_OPT_BIST
71	ef10_bist_enable_offline,	/* epo_bist_enable_offline */
72	ef10_bist_start,		/* epo_bist_start */
73	ef10_bist_poll,			/* epo_bist_poll */
74	ef10_bist_stop,			/* epo_bist_stop */
75#endif	/* EFSYS_OPT_BIST */
76};
77#endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
78
79	__checkReturn	efx_rc_t
80efx_phy_probe(
81	__in		efx_nic_t *enp)
82{
83	efx_port_t *epp = &(enp->en_port);
84	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
85	const efx_phy_ops_t *epop;
86	efx_rc_t rc;
87
88	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
89
90	epp->ep_port = encp->enc_port;
91	epp->ep_phy_type = encp->enc_phy_type;
92
93	/* Hook in operations structure */
94	switch (enp->en_family) {
95#if EFSYS_OPT_SIENA
96	case EFX_FAMILY_SIENA:
97		epop = &__efx_phy_siena_ops;
98		break;
99#endif	/* EFSYS_OPT_SIENA */
100
101#if EFSYS_OPT_HUNTINGTON
102	case EFX_FAMILY_HUNTINGTON:
103		epop = &__efx_phy_ef10_ops;
104		break;
105#endif	/* EFSYS_OPT_HUNTINGTON */
106
107#if EFSYS_OPT_MEDFORD
108	case EFX_FAMILY_MEDFORD:
109		epop = &__efx_phy_ef10_ops;
110		break;
111#endif	/* EFSYS_OPT_MEDFORD */
112
113#if EFSYS_OPT_MEDFORD2
114	case EFX_FAMILY_MEDFORD2:
115		epop = &__efx_phy_ef10_ops;
116		break;
117#endif	/* EFSYS_OPT_MEDFORD2 */
118
119	default:
120		rc = ENOTSUP;
121		goto fail1;
122	}
123
124	epp->ep_epop = epop;
125
126	return (0);
127
128fail1:
129	EFSYS_PROBE1(fail1, efx_rc_t, rc);
130
131	epp->ep_port = 0;
132	epp->ep_phy_type = 0;
133
134	return (rc);
135}
136
137	__checkReturn	efx_rc_t
138efx_phy_verify(
139	__in		efx_nic_t *enp)
140{
141	efx_port_t *epp = &(enp->en_port);
142	const efx_phy_ops_t *epop = epp->ep_epop;
143
144	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
145	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
146
147	return (epop->epo_verify(enp));
148}
149
150#if EFSYS_OPT_PHY_LED_CONTROL
151
152	__checkReturn	efx_rc_t
153efx_phy_led_set(
154	__in		efx_nic_t *enp,
155	__in		efx_phy_led_mode_t mode)
156{
157	efx_nic_cfg_t *encp = (&enp->en_nic_cfg);
158	efx_port_t *epp = &(enp->en_port);
159	const efx_phy_ops_t *epop = epp->ep_epop;
160	uint32_t mask;
161	efx_rc_t rc;
162
163	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
164	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
165
166	if (epp->ep_phy_led_mode == mode)
167		goto done;
168
169	mask = (1 << EFX_PHY_LED_DEFAULT);
170	mask |= encp->enc_led_mask;
171
172	if (!((1 << mode) & mask)) {
173		rc = ENOTSUP;
174		goto fail1;
175	}
176
177	EFSYS_ASSERT3U(mode, <, EFX_PHY_LED_NMODES);
178	epp->ep_phy_led_mode = mode;
179
180	if ((rc = epop->epo_reconfigure(enp)) != 0)
181		goto fail2;
182
183done:
184	return (0);
185
186fail2:
187	EFSYS_PROBE(fail2);
188fail1:
189	EFSYS_PROBE1(fail1, efx_rc_t, rc);
190
191	return (rc);
192}
193#endif	/* EFSYS_OPT_PHY_LED_CONTROL */
194
195			void
196efx_phy_adv_cap_get(
197	__in		efx_nic_t *enp,
198	__in		uint32_t flag,
199	__out		uint32_t *maskp)
200{
201	efx_port_t *epp = &(enp->en_port);
202
203	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
204	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
205
206	switch (flag) {
207	case EFX_PHY_CAP_CURRENT:
208		*maskp = epp->ep_adv_cap_mask;
209		break;
210	case EFX_PHY_CAP_DEFAULT:
211		*maskp = epp->ep_default_adv_cap_mask;
212		break;
213	case EFX_PHY_CAP_PERM:
214		*maskp = epp->ep_phy_cap_mask;
215		break;
216	default:
217		EFSYS_ASSERT(B_FALSE);
218		*maskp = 0;
219		break;
220	}
221}
222
223	__checkReturn	efx_rc_t
224efx_phy_adv_cap_set(
225	__in		efx_nic_t *enp,
226	__in		uint32_t mask)
227{
228	efx_port_t *epp = &(enp->en_port);
229	const efx_phy_ops_t *epop = epp->ep_epop;
230	uint32_t old_mask;
231	efx_rc_t rc;
232
233	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
234	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
235
236	if ((mask & ~epp->ep_phy_cap_mask) != 0) {
237		rc = ENOTSUP;
238		goto fail1;
239	}
240
241	if (epp->ep_adv_cap_mask == mask)
242		goto done;
243
244	old_mask = epp->ep_adv_cap_mask;
245	epp->ep_adv_cap_mask = mask;
246
247	if ((rc = epop->epo_reconfigure(enp)) != 0)
248		goto fail2;
249
250done:
251	return (0);
252
253fail2:
254	EFSYS_PROBE(fail2);
255
256	epp->ep_adv_cap_mask = old_mask;
257	/* Reconfigure for robustness */
258	if (epop->epo_reconfigure(enp) != 0) {
259		/*
260		 * We may have an inconsistent view of our advertised speed
261		 * capabilities.
262		 */
263		EFSYS_ASSERT(0);
264	}
265
266fail1:
267	EFSYS_PROBE1(fail1, efx_rc_t, rc);
268
269	return (rc);
270}
271
272	void
273efx_phy_lp_cap_get(
274	__in		efx_nic_t *enp,
275	__out		uint32_t *maskp)
276{
277	efx_port_t *epp = &(enp->en_port);
278
279	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
280	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
281
282	*maskp = epp->ep_lp_cap_mask;
283}
284
285	__checkReturn	efx_rc_t
286efx_phy_oui_get(
287	__in		efx_nic_t *enp,
288	__out		uint32_t *ouip)
289{
290	efx_port_t *epp = &(enp->en_port);
291	const efx_phy_ops_t *epop = epp->ep_epop;
292
293	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
294	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
295
296	return (epop->epo_oui_get(enp, ouip));
297}
298
299			void
300efx_phy_media_type_get(
301	__in		efx_nic_t *enp,
302	__out		efx_phy_media_type_t *typep)
303{
304	efx_port_t *epp = &(enp->en_port);
305
306	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
307	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
308
309	if (epp->ep_module_type != EFX_PHY_MEDIA_INVALID)
310		*typep = epp->ep_module_type;
311	else
312		*typep = epp->ep_fixed_port_type;
313}
314
315	__checkReturn		efx_rc_t
316efx_phy_module_get_info(
317	__in			efx_nic_t *enp,
318	__in			uint8_t dev_addr,
319	__in			size_t offset,
320	__in			size_t len,
321	__out_bcount(len)	uint8_t *data)
322{
323	efx_rc_t rc;
324
325	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
326	EFSYS_ASSERT(data != NULL);
327
328	if ((offset > EFX_PHY_MEDIA_INFO_MAX_OFFSET) ||
329	    ((offset + len) > EFX_PHY_MEDIA_INFO_MAX_OFFSET)) {
330		rc = EINVAL;
331		goto fail1;
332	}
333
334	if ((rc = efx_mcdi_phy_module_get_info(enp, dev_addr,
335	    offset, len, data)) != 0)
336		goto fail2;
337
338	return (0);
339
340fail2:
341	EFSYS_PROBE(fail2);
342fail1:
343	EFSYS_PROBE1(fail1, efx_rc_t, rc);
344
345	return (rc);
346}
347
348	__checkReturn		efx_rc_t
349efx_phy_fec_type_get(
350	__in		efx_nic_t *enp,
351	__out		efx_phy_fec_type_t *typep)
352{
353	efx_rc_t rc;
354	efx_phy_link_state_t epls;
355
356	if ((rc = efx_phy_link_state_get(enp, &epls)) != 0)
357		goto fail1;
358
359	*typep = epls.epls_fec;
360
361	return (0);
362
363fail1:
364	EFSYS_PROBE1(fail1, efx_rc_t, rc);
365
366	return (rc);
367}
368
369	__checkReturn		efx_rc_t
370efx_phy_link_state_get(
371	__in		efx_nic_t *enp,
372	__out		efx_phy_link_state_t *eplsp)
373{
374	efx_port_t *epp = &(enp->en_port);
375	const efx_phy_ops_t *epop = epp->ep_epop;
376	efx_rc_t rc;
377
378	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
379	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
380
381	if (epop->epo_link_state_get == NULL) {
382		rc = ENOTSUP;
383		goto fail1;
384	}
385
386	if ((rc = epop->epo_link_state_get(enp, eplsp)) != 0)
387		goto fail2;
388
389	return (0);
390
391fail2:
392	EFSYS_PROBE(fail2);
393fail1:
394	EFSYS_PROBE1(fail1, efx_rc_t, rc);
395
396	return (rc);
397}
398
399#if EFSYS_OPT_PHY_STATS
400
401#if EFSYS_OPT_NAMES
402
403/* START MKCONFIG GENERATED PhyStatNamesBlock af9ffa24da3bc100 */
404static const char * const __efx_phy_stat_name[] = {
405	"oui",
406	"pma_pmd_link_up",
407	"pma_pmd_rx_fault",
408	"pma_pmd_tx_fault",
409	"pma_pmd_rev_a",
410	"pma_pmd_rev_b",
411	"pma_pmd_rev_c",
412	"pma_pmd_rev_d",
413	"pcs_link_up",
414	"pcs_rx_fault",
415	"pcs_tx_fault",
416	"pcs_ber",
417	"pcs_block_errors",
418	"phy_xs_link_up",
419	"phy_xs_rx_fault",
420	"phy_xs_tx_fault",
421	"phy_xs_align",
422	"phy_xs_sync_a",
423	"phy_xs_sync_b",
424	"phy_xs_sync_c",
425	"phy_xs_sync_d",
426	"an_link_up",
427	"an_master",
428	"an_local_rx_ok",
429	"an_remote_rx_ok",
430	"cl22ext_link_up",
431	"snr_a",
432	"snr_b",
433	"snr_c",
434	"snr_d",
435	"pma_pmd_signal_a",
436	"pma_pmd_signal_b",
437	"pma_pmd_signal_c",
438	"pma_pmd_signal_d",
439	"an_complete",
440	"pma_pmd_rev_major",
441	"pma_pmd_rev_minor",
442	"pma_pmd_rev_micro",
443	"pcs_fw_version_0",
444	"pcs_fw_version_1",
445	"pcs_fw_version_2",
446	"pcs_fw_version_3",
447	"pcs_fw_build_yy",
448	"pcs_fw_build_mm",
449	"pcs_fw_build_dd",
450	"pcs_op_mode",
451};
452
453/* END MKCONFIG GENERATED PhyStatNamesBlock */
454
455					const char *
456efx_phy_stat_name(
457	__in				efx_nic_t *enp,
458	__in				efx_phy_stat_t type)
459{
460	_NOTE(ARGUNUSED(enp))
461	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
462	EFSYS_ASSERT3U(type, <, EFX_PHY_NSTATS);
463
464	return (__efx_phy_stat_name[type]);
465}
466
467#endif	/* EFSYS_OPT_NAMES */
468
469	__checkReturn			efx_rc_t
470efx_phy_stats_update(
471	__in				efx_nic_t *enp,
472	__in				efsys_mem_t *esmp,
473	__inout_ecount(EFX_PHY_NSTATS)	uint32_t *stat)
474{
475	efx_port_t *epp = &(enp->en_port);
476	const efx_phy_ops_t *epop = epp->ep_epop;
477
478	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
479	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
480
481	return (epop->epo_stats_update(enp, esmp, stat));
482}
483
484#endif	/* EFSYS_OPT_PHY_STATS */
485
486#if EFSYS_OPT_BIST
487
488	__checkReturn		efx_rc_t
489efx_bist_enable_offline(
490	__in			efx_nic_t *enp)
491{
492	efx_port_t *epp = &(enp->en_port);
493	const efx_phy_ops_t *epop = epp->ep_epop;
494	efx_rc_t rc;
495
496	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
497
498	if (epop->epo_bist_enable_offline == NULL) {
499		rc = ENOTSUP;
500		goto fail1;
501	}
502
503	if ((rc = epop->epo_bist_enable_offline(enp)) != 0)
504		goto fail2;
505
506	return (0);
507
508fail2:
509	EFSYS_PROBE(fail2);
510fail1:
511	EFSYS_PROBE1(fail1, efx_rc_t, rc);
512
513	return (rc);
514
515}
516
517	__checkReturn		efx_rc_t
518efx_bist_start(
519	__in			efx_nic_t *enp,
520	__in			efx_bist_type_t type)
521{
522	efx_port_t *epp = &(enp->en_port);
523	const efx_phy_ops_t *epop = epp->ep_epop;
524	efx_rc_t rc;
525
526	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
527
528	EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
529	EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
530	EFSYS_ASSERT3U(epp->ep_current_bist, ==, EFX_BIST_TYPE_UNKNOWN);
531
532	if (epop->epo_bist_start == NULL) {
533		rc = ENOTSUP;
534		goto fail1;
535	}
536
537	if ((rc = epop->epo_bist_start(enp, type)) != 0)
538		goto fail2;
539
540	epp->ep_current_bist = type;
541
542	return (0);
543
544fail2:
545	EFSYS_PROBE(fail2);
546fail1:
547	EFSYS_PROBE1(fail1, efx_rc_t, rc);
548
549	return (rc);
550}
551
552	__checkReturn		efx_rc_t
553efx_bist_poll(
554	__in			efx_nic_t *enp,
555	__in			efx_bist_type_t type,
556	__out			efx_bist_result_t *resultp,
557	__out_opt		uint32_t *value_maskp,
558	__out_ecount_opt(count)	unsigned long *valuesp,
559	__in			size_t count)
560{
561	efx_port_t *epp = &(enp->en_port);
562	const efx_phy_ops_t *epop = epp->ep_epop;
563	efx_rc_t rc;
564
565	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
566
567	EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
568	EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
569	EFSYS_ASSERT3U(epp->ep_current_bist, ==, type);
570
571	EFSYS_ASSERT(epop->epo_bist_poll != NULL);
572	if (epop->epo_bist_poll == NULL) {
573		rc = ENOTSUP;
574		goto fail1;
575	}
576
577	if ((rc = epop->epo_bist_poll(enp, type, resultp, value_maskp,
578	    valuesp, count)) != 0)
579		goto fail2;
580
581	return (0);
582
583fail2:
584	EFSYS_PROBE(fail2);
585fail1:
586	EFSYS_PROBE1(fail1, efx_rc_t, rc);
587
588	return (rc);
589}
590
591			void
592efx_bist_stop(
593	__in		efx_nic_t *enp,
594	__in		efx_bist_type_t type)
595{
596	efx_port_t *epp = &(enp->en_port);
597	const efx_phy_ops_t *epop = epp->ep_epop;
598
599	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
600
601	EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
602	EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
603	EFSYS_ASSERT3U(epp->ep_current_bist, ==, type);
604
605	EFSYS_ASSERT(epop->epo_bist_stop != NULL);
606
607	if (epop->epo_bist_stop != NULL)
608		epop->epo_bist_stop(enp, type);
609
610	epp->ep_current_bist = EFX_BIST_TYPE_UNKNOWN;
611}
612
613#endif	/* EFSYS_OPT_BIST */
614			void
615efx_phy_unprobe(
616	__in	efx_nic_t *enp)
617{
618	efx_port_t *epp = &(enp->en_port);
619
620	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
621
622	epp->ep_epop = NULL;
623
624	epp->ep_adv_cap_mask = 0;
625
626	epp->ep_port = 0;
627	epp->ep_phy_type = 0;
628}
629