xref: /illumos-gate/usr/src/uts/common/io/bnxe/bnxe_illumos.c (revision a69e76ca280a5227e229566b1e076da8f32193e0)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2017, Joyent, Inc.
14  */
15 
16 /*
17  * illumos specific bnxe related functions.
18  */
19 
20 #include "bnxe.h"
21 
22 /*
23  * Try to figure out which phy we should be using at this time based on the
24  * requested transceiver.
25  */
26 static uint_t
27 bnxe_get_phy_id(um_device_t *um)
28 {
29 	if (um->lm_dev.params.link.num_phys <= 1)
30 		return (ELINK_INT_PHY);
31 
32 	if (um->lm_dev.vars.link.link_up) {
33 		if ((um->lm_dev.vars.link.link_status &
34 		    LINK_STATUS_SERDES_LINK) &&
35 		    (um->lm_dev.params.link.phy[ELINK_EXT_PHY2].supported &
36 		    ELINK_SUPPORTED_FIBRE))
37 			return (ELINK_EXT_PHY2);
38 		return (ELINK_EXT_PHY1);
39 	} else {
40 		switch (elink_phy_selection(&um->lm_dev.params.link)) {
41 		case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
42 		case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
43 		case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
44 			return (ELINK_EXT_PHY1);
45 		case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
46 		case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
47 			return (ELINK_EXT_PHY2);
48 		/*
49 		 * The above hardware types are the only ones currently defined
50 		 * by the specification and common code. If we end up with an
51 		 * unknown value, then we default to what the hardware considers
52 		 * the default, which is PHY1.
53 		 */
54 		default:
55 			return (ELINK_EXT_PHY1);
56 		}
57 	}
58 }
59 
60 static int
61 bnxe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
62 {
63 	uint_t phyid;
64 	um_device_t *um = arg;
65 	struct elink_params *params;
66 	struct elink_phy *phy;
67 	boolean_t present = B_FALSE, usable = B_FALSE;
68 	elink_status_t ret;
69 	uint8_t buf;
70 
71 	if (id != 0 || arg == NULL || infop == NULL)
72 		return (EINVAL);
73 
74 	BNXE_LOCK_ENTER_PHY(um);
75 	phyid = bnxe_get_phy_id(um);
76 	params = &um->lm_dev.params.link;
77 	phy = &params->phy[phyid];
78 	if (phy->media_type == ELINK_ETH_PHY_BASE_T) {
79 		BNXE_LOCK_EXIT_PHY(um);
80 		return (ENOTSUP);
81 	}
82 
83 	/*
84 	 * Right now, the core OS-independent code from QLogic doesn't quite
85 	 * track whether or not the phy is plugged in, though it easily could.
86 	 * As such, the best way to determine whether or not the phy is present
87 	 * is to see if we can read the first byte from page 0xa0. We expect to
88 	 * get an explicit timeout if the device isn't present. We'll propagate
89 	 * EIO on any other error as we're not in a good state to understand
90 	 * what happened.
91 	 */
92 	PHY_HW_LOCK(&um->lm_dev);
93 	ret = elink_read_sfp_module_eeprom(phy, params, 0xa0, 0, sizeof (buf),
94 	    &buf);
95 	PHY_HW_UNLOCK(&um->lm_dev);
96 	if (ret != ELINK_STATUS_OK && ret != ELINK_STATUS_TIMEOUT) {
97 		BNXE_LOCK_EXIT_PHY(um);
98 		return (EIO);
99 	}
100 	if (ret == ELINK_STATUS_OK) {
101 		present = B_TRUE;
102 		if ((phy->flags & ELINK_FLAGS_SFP_NOT_APPROVED) == 0)
103 			usable = B_TRUE;
104 	}
105 	BNXE_LOCK_EXIT_PHY(um);
106 
107 	mac_transceiver_info_set_present(infop, present);
108 	mac_transceiver_info_set_usable(infop, usable);
109 
110 	return (0);
111 }
112 
113 static int
114 bnxe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
115     size_t nbytes, off_t offset, size_t *nread)
116 {
117 	uint_t phyid;
118 	um_device_t *um = arg;
119 	struct elink_phy *phy;
120 	struct elink_params *params;
121 	elink_status_t ret;
122 
123 	if (id != 0 || bp == NULL || nbytes == 0 || nread == NULL ||
124 	    (page != 0xa0 && page != 0xa2) || offset < 0)
125 		return (EINVAL);
126 
127 	/*
128 	 * Sanity check length params.
129 	 */
130 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
131 		return (EINVAL);
132 	}
133 
134 	BNXE_LOCK_ENTER_PHY(um);
135 	phyid = bnxe_get_phy_id(um);
136 	params = &um->lm_dev.params.link;
137 	phy = &um->lm_dev.params.link.phy[phyid];
138 
139 	if (phy->media_type == ELINK_ETH_PHY_BASE_T) {
140 		BNXE_LOCK_EXIT_PHY(um);
141 		return (ENOTSUP);
142 	}
143 
144 
145 	PHY_HW_LOCK(&um->lm_dev);
146 	ret = elink_read_sfp_module_eeprom(phy, params, (uint8_t)page,
147 	    (uint16_t)offset, (uint16_t)nbytes, bp);
148 	PHY_HW_UNLOCK(&um->lm_dev);
149 
150 	BNXE_LOCK_EXIT_PHY(um);
151 
152 	switch (ret) {
153 	case ELINK_STATUS_OK:
154 		*nread = nbytes;
155 		return (0);
156 	case ELINK_OP_NOT_SUPPORTED:
157 		return (ENOTSUP);
158 	default:
159 		return (EIO);
160 	}
161 }
162 
163 boolean_t
164 bnxe_fill_transceiver(um_device_t *um, void *arg)
165 {
166 	uint_t ntran = 1;
167 	mac_capab_transceiver_t *mct = arg;
168 
169 	mct->mct_flags = 0;
170 	/*
171 	 * While there is nominally a dual-phy version of bnxe out there (see
172 	 * ELINK_DUAL_MEDIA and related macros), these haven't been seen in the
173 	 * wild. For now, only assume that we have a single phy.
174 	 */
175 	mct->mct_ntransceivers = 1;
176 	mct->mct_info = bnxe_transceiver_info;
177 	mct->mct_read = bnxe_transceiver_read;
178 
179 	return (B_TRUE);
180 }
181