1947eaaf6SRobert Mustacchi /*
2947eaaf6SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3947eaaf6SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4947eaaf6SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5947eaaf6SRobert Mustacchi  * 1.0 of the CDDL.
6947eaaf6SRobert Mustacchi  *
7947eaaf6SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8947eaaf6SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9947eaaf6SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10947eaaf6SRobert Mustacchi  */
11947eaaf6SRobert Mustacchi 
12947eaaf6SRobert Mustacchi /*
13947eaaf6SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
14*b01ab2deSRobert Mustacchi  * Copyright 2023 Oxide Computer Company
15947eaaf6SRobert Mustacchi  */
16947eaaf6SRobert Mustacchi 
17947eaaf6SRobert Mustacchi /*
18947eaaf6SRobert Mustacchi  * illumos specific bnxe related functions.
19947eaaf6SRobert Mustacchi  */
20947eaaf6SRobert Mustacchi 
21947eaaf6SRobert Mustacchi #include "bnxe.h"
22947eaaf6SRobert Mustacchi 
23947eaaf6SRobert Mustacchi /*
24947eaaf6SRobert Mustacchi  * Try to figure out which phy we should be using at this time based on the
25947eaaf6SRobert Mustacchi  * requested transceiver.
26947eaaf6SRobert Mustacchi  */
27947eaaf6SRobert Mustacchi static uint_t
bnxe_get_phy_id(um_device_t * um)28947eaaf6SRobert Mustacchi bnxe_get_phy_id(um_device_t *um)
29947eaaf6SRobert Mustacchi {
30947eaaf6SRobert Mustacchi 	if (um->lm_dev.params.link.num_phys <= 1)
31947eaaf6SRobert Mustacchi 		return (ELINK_INT_PHY);
32947eaaf6SRobert Mustacchi 
33947eaaf6SRobert Mustacchi 	if (um->lm_dev.vars.link.link_up) {
34947eaaf6SRobert Mustacchi 		if ((um->lm_dev.vars.link.link_status &
35947eaaf6SRobert Mustacchi 		    LINK_STATUS_SERDES_LINK) &&
36947eaaf6SRobert Mustacchi 		    (um->lm_dev.params.link.phy[ELINK_EXT_PHY2].supported &
37947eaaf6SRobert Mustacchi 		    ELINK_SUPPORTED_FIBRE))
38947eaaf6SRobert Mustacchi 			return (ELINK_EXT_PHY2);
39947eaaf6SRobert Mustacchi 		return (ELINK_EXT_PHY1);
40947eaaf6SRobert Mustacchi 	} else {
41947eaaf6SRobert Mustacchi 		switch (elink_phy_selection(&um->lm_dev.params.link)) {
42947eaaf6SRobert Mustacchi 		case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
43947eaaf6SRobert Mustacchi 		case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
44947eaaf6SRobert Mustacchi 		case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
45947eaaf6SRobert Mustacchi 			return (ELINK_EXT_PHY1);
46947eaaf6SRobert Mustacchi 		case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
47947eaaf6SRobert Mustacchi 		case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
48947eaaf6SRobert Mustacchi 			return (ELINK_EXT_PHY2);
49947eaaf6SRobert Mustacchi 		/*
50947eaaf6SRobert Mustacchi 		 * The above hardware types are the only ones currently defined
51947eaaf6SRobert Mustacchi 		 * by the specification and common code. If we end up with an
52947eaaf6SRobert Mustacchi 		 * unknown value, then we default to what the hardware considers
53947eaaf6SRobert Mustacchi 		 * the default, which is PHY1.
54947eaaf6SRobert Mustacchi 		 */
55947eaaf6SRobert Mustacchi 		default:
56947eaaf6SRobert Mustacchi 			return (ELINK_EXT_PHY1);
57947eaaf6SRobert Mustacchi 		}
58947eaaf6SRobert Mustacchi 	}
59947eaaf6SRobert Mustacchi }
60947eaaf6SRobert Mustacchi 
61*b01ab2deSRobert Mustacchi /*
62*b01ab2deSRobert Mustacchi  * This media map table and structure is shared across the different pluggable
63*b01ab2deSRobert Mustacchi  * modules. The driver doesn't really look carefully at the difference between
64*b01ab2deSRobert Mustacchi  * the various multi-speed modules which is why for 10G based pieces we also
65*b01ab2deSRobert Mustacchi  * have lower speed based checks.
66*b01ab2deSRobert Mustacchi  */
67*b01ab2deSRobert Mustacchi typedef struct {
68*b01ab2deSRobert Mustacchi 	uint32_t bmm_sfp;
69*b01ab2deSRobert Mustacchi 	uint32_t bmm_speed;
70*b01ab2deSRobert Mustacchi 	mac_ether_media_t bmm_media;
71*b01ab2deSRobert Mustacchi } bnxe_media_map_t;
72*b01ab2deSRobert Mustacchi 
73*b01ab2deSRobert Mustacchi static const bnxe_media_map_t bnxe_media_map[] = {
74*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_SR, 10000, ETHER_MEDIA_10GBASE_SR },
75*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_SR, 1000, ETHER_MEDIA_1000BASE_SX },
76*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_1GBASE_SX, 1000, ETHER_MEDIA_1000BASE_SX },
77*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_LR, 10000, ETHER_MEDIA_10GBASE_LR },
78*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_LR, 1000, ETHER_MEDIA_1000BASE_LX },
79*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_1GBASE_LX, 1000, ETHER_MEDIA_1000BASE_LX },
80*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_LRM, 10000, ETHER_MEDIA_10GBASE_LRM },
81*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_10GBASE_ER, 10000, ETHER_MEDIA_10GBASE_ER },
82*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_1GBASE_T, 1000, ETHER_MEDIA_1000BASE_T },
83*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_1GBASE_CX, 1000, ETHER_MEDIA_1000BASE_CX },
84*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_DAC, 10000, ETHER_MEDIA_10GBASE_CR },
85*b01ab2deSRobert Mustacchi 	{ ELINK_ETH_SFP_ACC, 10000, ETHER_MEDIA_10GBASE_ACC },
86*b01ab2deSRobert Mustacchi };
87*b01ab2deSRobert Mustacchi 
88*b01ab2deSRobert Mustacchi mac_ether_media_t
bnxe_phy_to_media(um_device_t * um)89*b01ab2deSRobert Mustacchi bnxe_phy_to_media(um_device_t *um)
90*b01ab2deSRobert Mustacchi {
91*b01ab2deSRobert Mustacchi 	uint_t phyid;
92*b01ab2deSRobert Mustacchi 	struct elink_params *params;
93*b01ab2deSRobert Mustacchi 	struct elink_phy *phy;
94*b01ab2deSRobert Mustacchi 	mac_ether_media_t media = ETHER_MEDIA_UNKNOWN;
95*b01ab2deSRobert Mustacchi 
96*b01ab2deSRobert Mustacchi 	BNXE_LOCK_ENTER_PHY(um);
97*b01ab2deSRobert Mustacchi 	phyid = bnxe_get_phy_id(um);
98*b01ab2deSRobert Mustacchi 	params = &um->lm_dev.params.link;
99*b01ab2deSRobert Mustacchi 	phy = &params->phy[phyid];
100*b01ab2deSRobert Mustacchi 
101*b01ab2deSRobert Mustacchi 	switch (phy->media_type) {
102*b01ab2deSRobert Mustacchi 	/*
103*b01ab2deSRobert Mustacchi 	 * Right now the driver does not ask the XFP i2c entity to determine the
104*b01ab2deSRobert Mustacchi 	 * media information. If we encounter someone with an XFP device then we
105*b01ab2deSRobert Mustacchi 	 * can add logic to the driver to cover proper detection, but otherwise
106*b01ab2deSRobert Mustacchi 	 * it would fit into this same set of modes.
107*b01ab2deSRobert Mustacchi 	 */
108*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFPP_10G_FIBER:
109*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_XFP_FIBER:
110*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_DA_TWINAX:
111*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFP_1G_FIBER:
112*b01ab2deSRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(bnxe_media_map); i++) {
113*b01ab2deSRobert Mustacchi 			const bnxe_media_map_t *map = &bnxe_media_map[i];
114*b01ab2deSRobert Mustacchi 			if (phy->sfp_media == map->bmm_sfp &&
115*b01ab2deSRobert Mustacchi 			    um->props.link_speed == map->bmm_speed) {
116*b01ab2deSRobert Mustacchi 				media = map->bmm_media;
117*b01ab2deSRobert Mustacchi 				break;
118*b01ab2deSRobert Mustacchi 			}
119*b01ab2deSRobert Mustacchi 		}
120*b01ab2deSRobert Mustacchi 		break;
121*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_BASE_T:
122*b01ab2deSRobert Mustacchi 		switch (um->props.link_speed) {
123*b01ab2deSRobert Mustacchi 		case 10:
124*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_10BASE_T;
125*b01ab2deSRobert Mustacchi 			break;
126*b01ab2deSRobert Mustacchi 		case 100:
127*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_100BASE_TX;
128*b01ab2deSRobert Mustacchi 			break;
129*b01ab2deSRobert Mustacchi 		case 1000:
130*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_1000BASE_T;
131*b01ab2deSRobert Mustacchi 			break;
132*b01ab2deSRobert Mustacchi 		case 10000:
133*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_10GBASE_T;
134*b01ab2deSRobert Mustacchi 			break;
135*b01ab2deSRobert Mustacchi 		default:
136*b01ab2deSRobert Mustacchi 			break;
137*b01ab2deSRobert Mustacchi 		}
138*b01ab2deSRobert Mustacchi 		break;
139*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_KR:
140*b01ab2deSRobert Mustacchi 		switch (um->props.link_speed) {
141*b01ab2deSRobert Mustacchi 		case 1000:
142*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_1000BASE_KX;
143*b01ab2deSRobert Mustacchi 			break;
144*b01ab2deSRobert Mustacchi 		case 10000:
145*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_10GBASE_KR;
146*b01ab2deSRobert Mustacchi 			break;
147*b01ab2deSRobert Mustacchi 		default:
148*b01ab2deSRobert Mustacchi 			break;
149*b01ab2deSRobert Mustacchi 		}
150*b01ab2deSRobert Mustacchi 		break;
151*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_CX4:
152*b01ab2deSRobert Mustacchi 		if (um->props.link_speed == 10000) {
153*b01ab2deSRobert Mustacchi 			media = ETHER_MEDIA_10GBASE_CX4;
154*b01ab2deSRobert Mustacchi 		}
155*b01ab2deSRobert Mustacchi 		break;
156*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_NOT_PRESENT:
157*b01ab2deSRobert Mustacchi 		media = ETHER_MEDIA_NONE;
158*b01ab2deSRobert Mustacchi 		break;
159*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_UNSPECIFIED:
160*b01ab2deSRobert Mustacchi 	default:
161*b01ab2deSRobert Mustacchi 		media = ETHER_MEDIA_UNKNOWN;
162*b01ab2deSRobert Mustacchi 		break;
163*b01ab2deSRobert Mustacchi 	}
164*b01ab2deSRobert Mustacchi 
165*b01ab2deSRobert Mustacchi 	BNXE_LOCK_EXIT_PHY(um);
166*b01ab2deSRobert Mustacchi 	return (media);
167*b01ab2deSRobert Mustacchi }
168*b01ab2deSRobert Mustacchi 
169*b01ab2deSRobert Mustacchi 
170947eaaf6SRobert Mustacchi static int
bnxe_transceiver_info(void * arg,uint_t id,mac_transceiver_info_t * infop)171947eaaf6SRobert Mustacchi bnxe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
172947eaaf6SRobert Mustacchi {
173947eaaf6SRobert Mustacchi 	uint_t phyid;
174947eaaf6SRobert Mustacchi 	um_device_t *um = arg;
175947eaaf6SRobert Mustacchi 	struct elink_params *params;
176947eaaf6SRobert Mustacchi 	struct elink_phy *phy;
177947eaaf6SRobert Mustacchi 	boolean_t present = B_FALSE, usable = B_FALSE;
178947eaaf6SRobert Mustacchi 	elink_status_t ret;
179947eaaf6SRobert Mustacchi 	uint8_t buf;
180947eaaf6SRobert Mustacchi 
181947eaaf6SRobert Mustacchi 	if (id != 0 || arg == NULL || infop == NULL)
182947eaaf6SRobert Mustacchi 		return (EINVAL);
183947eaaf6SRobert Mustacchi 
184947eaaf6SRobert Mustacchi 	BNXE_LOCK_ENTER_PHY(um);
185947eaaf6SRobert Mustacchi 	phyid = bnxe_get_phy_id(um);
186947eaaf6SRobert Mustacchi 	params = &um->lm_dev.params.link;
187947eaaf6SRobert Mustacchi 	phy = &params->phy[phyid];
188*b01ab2deSRobert Mustacchi 
189*b01ab2deSRobert Mustacchi 	switch (phy->media_type) {
190*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFPP_10G_FIBER:
191*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_DA_TWINAX:
192*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFP_1G_FIBER:
193*b01ab2deSRobert Mustacchi 		break;
194*b01ab2deSRobert Mustacchi 	default:
195947eaaf6SRobert Mustacchi 		BNXE_LOCK_EXIT_PHY(um);
196947eaaf6SRobert Mustacchi 		return (ENOTSUP);
197947eaaf6SRobert Mustacchi 	}
198947eaaf6SRobert Mustacchi 
199947eaaf6SRobert Mustacchi 	/*
200947eaaf6SRobert Mustacchi 	 * Right now, the core OS-independent code from QLogic doesn't quite
201947eaaf6SRobert Mustacchi 	 * track whether or not the phy is plugged in, though it easily could.
202947eaaf6SRobert Mustacchi 	 * As such, the best way to determine whether or not the phy is present
203947eaaf6SRobert Mustacchi 	 * is to see if we can read the first byte from page 0xa0. We expect to
204947eaaf6SRobert Mustacchi 	 * get an explicit timeout if the device isn't present. We'll propagate
205947eaaf6SRobert Mustacchi 	 * EIO on any other error as we're not in a good state to understand
206947eaaf6SRobert Mustacchi 	 * what happened.
207947eaaf6SRobert Mustacchi 	 */
208947eaaf6SRobert Mustacchi 	PHY_HW_LOCK(&um->lm_dev);
209947eaaf6SRobert Mustacchi 	ret = elink_read_sfp_module_eeprom(phy, params, 0xa0, 0, sizeof (buf),
210947eaaf6SRobert Mustacchi 	    &buf);
211947eaaf6SRobert Mustacchi 	PHY_HW_UNLOCK(&um->lm_dev);
212947eaaf6SRobert Mustacchi 	if (ret != ELINK_STATUS_OK && ret != ELINK_STATUS_TIMEOUT) {
213947eaaf6SRobert Mustacchi 		BNXE_LOCK_EXIT_PHY(um);
214947eaaf6SRobert Mustacchi 		return (EIO);
215947eaaf6SRobert Mustacchi 	}
216947eaaf6SRobert Mustacchi 	if (ret == ELINK_STATUS_OK) {
217947eaaf6SRobert Mustacchi 		present = B_TRUE;
218947eaaf6SRobert Mustacchi 		if ((phy->flags & ELINK_FLAGS_SFP_NOT_APPROVED) == 0)
219947eaaf6SRobert Mustacchi 			usable = B_TRUE;
220947eaaf6SRobert Mustacchi 	}
221947eaaf6SRobert Mustacchi 	BNXE_LOCK_EXIT_PHY(um);
222947eaaf6SRobert Mustacchi 
223947eaaf6SRobert Mustacchi 	mac_transceiver_info_set_present(infop, present);
224947eaaf6SRobert Mustacchi 	mac_transceiver_info_set_usable(infop, usable);
225947eaaf6SRobert Mustacchi 
226947eaaf6SRobert Mustacchi 	return (0);
227947eaaf6SRobert Mustacchi }
228947eaaf6SRobert Mustacchi 
229947eaaf6SRobert Mustacchi static int
bnxe_transceiver_read(void * arg,uint_t id,uint_t page,void * bp,size_t nbytes,off_t offset,size_t * nread)230947eaaf6SRobert Mustacchi bnxe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
231947eaaf6SRobert Mustacchi     size_t nbytes, off_t offset, size_t *nread)
232947eaaf6SRobert Mustacchi {
233947eaaf6SRobert Mustacchi 	uint_t phyid;
234947eaaf6SRobert Mustacchi 	um_device_t *um = arg;
235947eaaf6SRobert Mustacchi 	struct elink_phy *phy;
236947eaaf6SRobert Mustacchi 	struct elink_params *params;
237947eaaf6SRobert Mustacchi 	elink_status_t ret;
238947eaaf6SRobert Mustacchi 
239947eaaf6SRobert Mustacchi 	if (id != 0 || bp == NULL || nbytes == 0 || nread == NULL ||
240947eaaf6SRobert Mustacchi 	    (page != 0xa0 && page != 0xa2) || offset < 0)
241947eaaf6SRobert Mustacchi 		return (EINVAL);
242947eaaf6SRobert Mustacchi 
243947eaaf6SRobert Mustacchi 	/*
244947eaaf6SRobert Mustacchi 	 * Sanity check length params.
245947eaaf6SRobert Mustacchi 	 */
246947eaaf6SRobert Mustacchi 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
247947eaaf6SRobert Mustacchi 		return (EINVAL);
248947eaaf6SRobert Mustacchi 	}
249947eaaf6SRobert Mustacchi 
250947eaaf6SRobert Mustacchi 	BNXE_LOCK_ENTER_PHY(um);
251947eaaf6SRobert Mustacchi 	phyid = bnxe_get_phy_id(um);
252947eaaf6SRobert Mustacchi 	params = &um->lm_dev.params.link;
253947eaaf6SRobert Mustacchi 	phy = &um->lm_dev.params.link.phy[phyid];
254947eaaf6SRobert Mustacchi 
255*b01ab2deSRobert Mustacchi 	switch (phy->media_type) {
256*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFPP_10G_FIBER:
257*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_DA_TWINAX:
258*b01ab2deSRobert Mustacchi 	case ELINK_ETH_PHY_SFP_1G_FIBER:
259*b01ab2deSRobert Mustacchi 		break;
260*b01ab2deSRobert Mustacchi 	default:
261947eaaf6SRobert Mustacchi 		BNXE_LOCK_EXIT_PHY(um);
262947eaaf6SRobert Mustacchi 		return (ENOTSUP);
263947eaaf6SRobert Mustacchi 	}
264947eaaf6SRobert Mustacchi 
265947eaaf6SRobert Mustacchi 	PHY_HW_LOCK(&um->lm_dev);
266947eaaf6SRobert Mustacchi 	ret = elink_read_sfp_module_eeprom(phy, params, (uint8_t)page,
267947eaaf6SRobert Mustacchi 	    (uint16_t)offset, (uint16_t)nbytes, bp);
268947eaaf6SRobert Mustacchi 	PHY_HW_UNLOCK(&um->lm_dev);
269947eaaf6SRobert Mustacchi 
270947eaaf6SRobert Mustacchi 	BNXE_LOCK_EXIT_PHY(um);
271947eaaf6SRobert Mustacchi 
272947eaaf6SRobert Mustacchi 	switch (ret) {
273947eaaf6SRobert Mustacchi 	case ELINK_STATUS_OK:
274947eaaf6SRobert Mustacchi 		*nread = nbytes;
275947eaaf6SRobert Mustacchi 		return (0);
276947eaaf6SRobert Mustacchi 	case ELINK_OP_NOT_SUPPORTED:
277947eaaf6SRobert Mustacchi 		return (ENOTSUP);
278947eaaf6SRobert Mustacchi 	default:
279947eaaf6SRobert Mustacchi 		return (EIO);
280947eaaf6SRobert Mustacchi 	}
281947eaaf6SRobert Mustacchi }
282947eaaf6SRobert Mustacchi 
283947eaaf6SRobert Mustacchi boolean_t
bnxe_fill_transceiver(um_device_t * um,void * arg)284947eaaf6SRobert Mustacchi bnxe_fill_transceiver(um_device_t *um, void *arg)
285947eaaf6SRobert Mustacchi {
286947eaaf6SRobert Mustacchi 	uint_t ntran = 1;
287947eaaf6SRobert Mustacchi 	mac_capab_transceiver_t *mct = arg;
288947eaaf6SRobert Mustacchi 
289947eaaf6SRobert Mustacchi 	mct->mct_flags = 0;
290947eaaf6SRobert Mustacchi 	/*
291947eaaf6SRobert Mustacchi 	 * While there is nominally a dual-phy version of bnxe out there (see
292947eaaf6SRobert Mustacchi 	 * ELINK_DUAL_MEDIA and related macros), these haven't been seen in the
293947eaaf6SRobert Mustacchi 	 * wild. For now, only assume that we have a single phy.
294947eaaf6SRobert Mustacchi 	 */
295947eaaf6SRobert Mustacchi 	mct->mct_ntransceivers = 1;
296947eaaf6SRobert Mustacchi 	mct->mct_info = bnxe_transceiver_info;
297947eaaf6SRobert Mustacchi 	mct->mct_read = bnxe_transceiver_read;
298947eaaf6SRobert Mustacchi 
299947eaaf6SRobert Mustacchi 	return (B_TRUE);
300947eaaf6SRobert Mustacchi }
301