xref: /freebsd-12.1/sys/dev/mii/mlphy.c (revision df57947f)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1997, 1998, 1999
5  *	Bill Paul <[email protected]>.  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
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 /*
40  * driver for Micro Linear 6692 PHYs
41  *
42  * The Micro Linear 6692 is a strange beast, and dealing with it using
43  * this code framework is tricky. The 6692 is actually a 100Mbps-only
44  * device, which means that a second PHY is required to support 10Mbps
45  * modes. However, even though the 6692 does not support 10Mbps modes,
46  * it can still advertise them when performing autonegotiation. If a
47  * 10Mbps mode is negotiated, we must program the registers of the
48  * companion PHY accordingly in addition to programming the registers
49  * of the 6692.
50  *
51  * This device also does not have vendor/device ID registers.
52  */
53 
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/kernel.h>
57 #include <sys/socket.h>
58 #include <sys/module.h>
59 #include <sys/bus.h>
60 #include <sys/malloc.h>
61 
62 #include <net/if.h>
63 #include <net/if_media.h>
64 
65 #include <dev/mii/mii.h>
66 #include <dev/mii/miivar.h>
67 
68 #include "miibus_if.h"
69 
70 #define ML_STATE_AUTO_SELF	1
71 #define ML_STATE_AUTO_OTHER	2
72 
73 struct mlphy_softc	{
74 	struct mii_softc	ml_mii;
75 	device_t		ml_dev;
76 	int			ml_state;
77 	int			ml_linked;
78 };
79 
80 static int mlphy_probe(device_t);
81 static int mlphy_attach(device_t);
82 
83 static device_method_t mlphy_methods[] = {
84 	/* device interface */
85 	DEVMETHOD(device_probe,		mlphy_probe),
86 	DEVMETHOD(device_attach,	mlphy_attach),
87 	DEVMETHOD(device_detach,	mii_phy_detach),
88 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
89 	DEVMETHOD_END
90 };
91 
92 static devclass_t mlphy_devclass;
93 
94 static driver_t mlphy_driver = {
95 	"mlphy",
96 	mlphy_methods,
97 	sizeof(struct mlphy_softc)
98 };
99 
100 DRIVER_MODULE(mlphy, miibus, mlphy_driver, mlphy_devclass, 0, 0);
101 
102 static struct mii_softc *mlphy_find_other(struct mlphy_softc *);
103 static int	mlphy_service(struct mii_softc *, struct mii_data *, int);
104 static void	mlphy_reset(struct mii_softc *);
105 static void	mlphy_status(struct mii_softc *);
106 
107 static const struct mii_phy_funcs mlphy_funcs = {
108 	mlphy_service,
109 	mlphy_status,
110 	mlphy_reset
111 };
112 
113 static int
mlphy_probe(dev)114 mlphy_probe(dev)
115 	device_t		dev;
116 {
117 	struct mii_attach_args	*ma;
118 
119 	ma = device_get_ivars(dev);
120 
121 	/*
122 	 * Micro Linear PHY reports oui == 0 model == 0
123 	 */
124 	if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
125 	    MII_MODEL(ma->mii_id2) != 0)
126 		return (ENXIO);
127 
128 	/*
129 	 * Make sure the parent is a `tl'. So far, I have only
130 	 * encountered the 6692 on an Olicom card with a ThunderLAN
131 	 * controller chip.
132 	 */
133 	if (!mii_dev_mac_match(dev, "tl"))
134 		return (ENXIO);
135 
136 	device_set_desc(dev, "Micro Linear 6692 media interface");
137 
138 	return (BUS_PROBE_DEFAULT);
139 }
140 
141 static int
mlphy_attach(dev)142 mlphy_attach(dev)
143 	device_t		dev;
144 {
145 	struct mlphy_softc *msc;
146 	struct mii_softc *sc;
147 
148 	msc = device_get_softc(dev);
149 	sc = &msc->ml_mii;
150 	msc->ml_dev = dev;
151 	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mlphy_funcs, 0);
152 
153 	PHY_RESET(sc);
154 
155 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & sc->mii_capmask;
156 	/* Let the companion PHY (if any) only handle the media we don't. */
157 	sc->mii_capmask = ~sc->mii_capabilities;
158 	device_printf(dev, " ");
159 	mii_phy_add_media(sc);
160 	printf("\n");
161 
162 	MIIBUS_MEDIAINIT(sc->mii_dev);
163 	return (0);
164 }
165 
166 static struct mii_softc *
mlphy_find_other(struct mlphy_softc * msc)167 mlphy_find_other(struct mlphy_softc *msc)
168 {
169 	device_t		*devlist;
170 	struct mii_softc *retval;
171 	int i, devs;
172 
173 	retval = NULL;
174 	if (device_get_children(msc->ml_mii.mii_dev, &devlist, &devs) != 0)
175 		return (NULL);
176 	for (i = 0; i < devs; i++) {
177 		if (devlist[i] != msc->ml_dev) {
178 			retval = device_get_softc(devlist[i]);
179 			break;
180 		}
181 	}
182 	free(devlist, M_TEMP);
183 	return (retval);
184 }
185 
186 static int
mlphy_service(xsc,mii,cmd)187 mlphy_service(xsc, mii, cmd)
188 	struct mii_softc *xsc;
189 	struct mii_data *mii;
190 	int cmd;
191 {
192 	struct ifmedia_entry	*ife = mii->mii_media.ifm_cur;
193 	struct mii_softc	*other = NULL;
194 	struct mlphy_softc	*msc = (struct mlphy_softc *)xsc;
195 	struct mii_softc	*sc = (struct mii_softc *)&msc->ml_mii;
196 	int			other_inst, reg;
197 
198 	/*
199 	 * See if there's another PHY on this bus with us.
200 	 * If so, we may need it for 10Mbps modes.
201 	 */
202 	other = mlphy_find_other(msc);
203 
204 	switch (cmd) {
205 	case MII_POLLSTAT:
206 		break;
207 
208 	case MII_MEDIACHG:
209 		switch (IFM_SUBTYPE(ife->ifm_media)) {
210 		case IFM_AUTO:
211 			/*
212 			 * For autonegotiation, reset and isolate the
213 			 * companion PHY (if any) and then do NWAY
214 			 * autonegotiation ourselves.
215 			 */
216 			msc->ml_state = ML_STATE_AUTO_SELF;
217 			if (other != NULL) {
218 				PHY_RESET(other);
219 				PHY_WRITE(other, MII_BMCR, BMCR_ISO);
220 			}
221 			(void)mii_phy_auto(sc);
222 			msc->ml_linked = 0;
223 			return (0);
224 		case IFM_10_T:
225 		case IFM_100_TX:
226 			/*
227 			 * For 10baseT and 100baseTX modes, reset and isolate
228 			 * the companion PHY (if any), then program ourselves
229 			 * accordingly.
230 			 */
231 			if (other != NULL) {
232 				PHY_RESET(other);
233 				PHY_WRITE(other, MII_BMCR, BMCR_ISO);
234 			}
235 			mii_phy_setmedia(sc);
236 			msc->ml_state = 0;
237 			break;
238 		default:
239 			return (EINVAL);
240 
241 		}
242 		break;
243 
244 	case MII_TICK:
245 		/*
246 		 * Only used for autonegotiation.
247 		 */
248 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
249 			break;
250 
251 		/*
252 		 * Check to see if we have link.  If we do, we don't
253 		 * need to restart the autonegotiation process.  Read
254 		 * the BMSR twice in case it's latched.
255 		 * If we're in a 10Mbps mode, check the link of the
256 		 * 10Mbps PHY. Sometimes the Micro Linear PHY's
257 		 * linkstat bit will clear while the linkstat bit of
258 		 * the companion PHY will remain set.
259 		 */
260 		if (msc->ml_state == ML_STATE_AUTO_OTHER) {
261 			reg = PHY_READ(other, MII_BMSR) |
262 			    PHY_READ(other, MII_BMSR);
263 		} else {
264 			reg = PHY_READ(sc, MII_BMSR) |
265 			    PHY_READ(sc, MII_BMSR);
266 		}
267 
268 		if (reg & BMSR_LINK) {
269 			if (!msc->ml_linked) {
270 				msc->ml_linked = 1;
271 				PHY_STATUS(sc);
272 			}
273 			break;
274 		}
275 
276 		/*
277 		 * Only retry autonegotiation every 5 seconds.
278 		 */
279 		if (++sc->mii_ticks <= MII_ANEGTICKS)
280 			break;
281 
282 		sc->mii_ticks = 0;
283 		msc->ml_linked = 0;
284 		mii->mii_media_active = IFM_NONE;
285 		PHY_RESET(sc);
286 		msc->ml_state = ML_STATE_AUTO_SELF;
287 		if (other != NULL) {
288 			PHY_RESET(other);
289 			PHY_WRITE(other, MII_BMCR, BMCR_ISO);
290 		}
291 		mii_phy_auto(sc);
292 		return (0);
293 	}
294 
295 	/* Update the media status. */
296 
297 	if (msc->ml_state == ML_STATE_AUTO_OTHER) {
298 		other_inst = other->mii_inst;
299 		other->mii_inst = sc->mii_inst;
300 		if (IFM_INST(ife->ifm_media) == other->mii_inst)
301 			(void)PHY_SERVICE(other, mii, MII_POLLSTAT);
302 		other->mii_inst = other_inst;
303 		sc->mii_media_active = other->mii_media_active;
304 		sc->mii_media_status = other->mii_media_status;
305 	} else
306 		ukphy_status(sc);
307 
308 	/* Callback if something changed. */
309 	mii_phy_update(sc, cmd);
310 	return (0);
311 }
312 
313 /*
314  * The Micro Linear PHY comes out of reset with the 'autoneg
315  * enable' bit set, which we don't want.
316  */
317 static void
mlphy_reset(sc)318 mlphy_reset(sc)
319 	struct mii_softc	*sc;
320 {
321 	int			reg;
322 
323 	mii_phy_reset(sc);
324 	reg = PHY_READ(sc, MII_BMCR);
325 	reg &= ~BMCR_AUTOEN;
326 	PHY_WRITE(sc, MII_BMCR, reg);
327 }
328 
329 /*
330  * If we negotiate a 10Mbps mode, we need to check for an alternate
331  * PHY and make sure it's enabled and set correctly.
332  */
333 static void
mlphy_status(sc)334 mlphy_status(sc)
335 	struct mii_softc	*sc;
336 {
337 	struct mlphy_softc	*msc = (struct mlphy_softc *)sc;
338 	struct mii_data		*mii = msc->ml_mii.mii_pdata;
339 	struct mii_softc	*other = NULL;
340 
341 	/* See if there's another PHY on the bus with us. */
342 	other = mlphy_find_other(msc);
343 	if (other == NULL)
344 		return;
345 
346 	ukphy_status(sc);
347 
348 	if (IFM_SUBTYPE(mii->mii_media_active) != IFM_10_T) {
349 		msc->ml_state = ML_STATE_AUTO_SELF;
350 		PHY_RESET(other);
351 		PHY_WRITE(other, MII_BMCR, BMCR_ISO);
352 	}
353 
354 	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) {
355 		msc->ml_state = ML_STATE_AUTO_OTHER;
356 		PHY_RESET(&msc->ml_mii);
357 		PHY_WRITE(&msc->ml_mii, MII_BMCR, BMCR_ISO);
358 		PHY_RESET(other);
359 		mii_phy_auto(other);
360 	}
361 }
362