xref: /freebsd/sys/dev/mii/jmphy.c (revision aa0a1e58f0189b0fde359a8bda032887e72057fa)
1 /*-
2  * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 /*
32  * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
40 #include <sys/bus.h>
41 
42 #include <net/if.h>
43 #include <net/if_media.h>
44 
45 #include <dev/mii/mii.h>
46 #include <dev/mii/miivar.h>
47 #include "miidevs.h"
48 
49 #include <dev/mii/jmphyreg.h>
50 
51 #include "miibus_if.h"
52 
53 static int	jmphy_probe(device_t);
54 static int	jmphy_attach(device_t);
55 static void	jmphy_reset(struct mii_softc *);
56 static uint16_t	jmphy_anar(struct ifmedia_entry *);
57 static int	jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
58 
59 struct jmphy_softc {
60 	struct mii_softc mii_sc;
61 	int mii_oui;
62 	int mii_model;
63 	int mii_rev;
64 };
65 
66 static device_method_t jmphy_methods[] = {
67 	/* Device interface. */
68 	DEVMETHOD(device_probe,		jmphy_probe),
69 	DEVMETHOD(device_attach,	jmphy_attach),
70 	DEVMETHOD(device_detach,	mii_phy_detach),
71 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
72 	{ NULL, NULL }
73 };
74 
75 static devclass_t jmphy_devclass;
76 static driver_t jmphy_driver = {
77 	"jmphy",
78 	jmphy_methods,
79 	sizeof(struct jmphy_softc)
80 };
81 
82 DRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0);
83 
84 static int	jmphy_service(struct mii_softc *, struct mii_data *, int);
85 static void	jmphy_status(struct mii_softc *);
86 
87 static const struct mii_phydesc jmphys[] = {
88 	MII_PHY_DESC(JMICRON, JMP202),
89 	MII_PHY_DESC(JMICRON, JMP211),
90 	MII_PHY_END
91 };
92 
93 static int
94 jmphy_probe(device_t dev)
95 {
96 
97 	return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
98 }
99 
100 static int
101 jmphy_attach(device_t dev)
102 {
103 	struct jmphy_softc *jsc;
104 	struct mii_softc *sc;
105 	struct mii_attach_args *ma;
106 	struct mii_data *mii;
107 	struct ifnet *ifp;
108 
109 	jsc = device_get_softc(dev);
110 	sc = &jsc->mii_sc;
111 	ma = device_get_ivars(dev);
112 	sc->mii_dev = device_get_parent(dev);
113 	mii = ma->mii_data;
114 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
115 
116 	sc->mii_flags = miibus_get_flags(dev);
117 	sc->mii_inst = mii->mii_instance++;
118 	sc->mii_phy = ma->mii_phyno;
119 	sc->mii_service = jmphy_service;
120 	sc->mii_pdata = mii;
121 
122 	ifp = sc->mii_pdata->mii_ifp;
123 	if (strcmp(ifp->if_dname, "jme") == 0 &&
124 	    (sc->mii_flags & MIIF_MACPRIV0) != 0)
125 		sc->mii_flags |= MIIF_PHYPRIV0;
126 	jsc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
127 	jsc->mii_model = MII_MODEL(ma->mii_id2);
128 	jsc->mii_rev = MII_REV(ma->mii_id2);
129 	if (bootverbose)
130 		device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
131 		    jsc->mii_oui, jsc->mii_model, jsc->mii_rev);
132 
133 	jmphy_reset(sc);
134 
135 	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
136 	if (sc->mii_capabilities & BMSR_EXTSTAT)
137 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
138 	device_printf(dev, " ");
139 	mii_phy_add_media(sc);
140 	printf("\n");
141 
142 	MIIBUS_MEDIAINIT(sc->mii_dev);
143 	return (0);
144 }
145 
146 static int
147 jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
148 {
149 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
150 
151 	switch (cmd) {
152 	case MII_POLLSTAT:
153 		break;
154 
155 	case MII_MEDIACHG:
156 		/*
157 		 * If the interface is not up, don't do anything.
158 		 */
159 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
160 			break;
161 
162 		if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
163 			return (EINVAL);
164 		break;
165 
166 	case MII_TICK:
167 		/*
168 		 * Is the interface even up?
169 		 */
170 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
171 			return (0);
172 
173 		/*
174 		 * Only used for autonegotiation.
175 		 */
176 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
177 			sc->mii_ticks = 0;
178 			break;
179 		}
180 
181 		/* Check for link. */
182 		if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
183 			sc->mii_ticks = 0;
184 			break;
185 		}
186 
187 		/* Announce link loss right after it happens. */
188 		if (sc->mii_ticks++ == 0)
189 			break;
190 		if (sc->mii_ticks <= sc->mii_anegticks)
191 			return (0);
192 
193 		sc->mii_ticks = 0;
194 		(void)jmphy_setmedia(sc, ife);
195 		break;
196 	}
197 
198 	/* Update the media status. */
199 	jmphy_status(sc);
200 
201 	/* Callback if something changed. */
202 	mii_phy_update(sc, cmd);
203 	return (0);
204 }
205 
206 static void
207 jmphy_status(struct mii_softc *sc)
208 {
209 	struct mii_data *mii = sc->mii_pdata;
210 	int bmcr, ssr;
211 
212 	mii->mii_media_status = IFM_AVALID;
213 	mii->mii_media_active = IFM_ETHER;
214 
215 	ssr = PHY_READ(sc, JMPHY_SSR);
216 	if ((ssr & JMPHY_SSR_LINK_UP) != 0)
217 		mii->mii_media_status |= IFM_ACTIVE;
218 
219 	bmcr = PHY_READ(sc, MII_BMCR);
220 	if ((bmcr & BMCR_ISO) != 0) {
221 		mii->mii_media_active |= IFM_NONE;
222 		mii->mii_media_status = 0;
223 		return;
224 	}
225 
226 	if ((bmcr & BMCR_LOOP) != 0)
227 		mii->mii_media_active |= IFM_LOOP;
228 
229 	if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
230 		/* Erg, still trying, I guess... */
231 		mii->mii_media_active |= IFM_NONE;
232 		return;
233 	}
234 
235 	switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
236 	case JMPHY_SSR_SPEED_1000:
237 		mii->mii_media_active |= IFM_1000_T;
238 		/*
239 		 * jmphy(4) got a valid link so reset mii_ticks.
240 		 * Resetting mii_ticks is needed in order to
241 		 * detect link loss after auto-negotiation.
242 		 */
243 		sc->mii_ticks = 0;
244 		break;
245 	case JMPHY_SSR_SPEED_100:
246 		mii->mii_media_active |= IFM_100_TX;
247 		sc->mii_ticks = 0;
248 		break;
249 	case JMPHY_SSR_SPEED_10:
250 		mii->mii_media_active |= IFM_10_T;
251 		sc->mii_ticks = 0;
252 		break;
253 	default:
254 		mii->mii_media_active |= IFM_NONE;
255 		return;
256 	}
257 
258 	if ((ssr & JMPHY_SSR_DUPLEX) != 0)
259 		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
260 	else
261 		mii->mii_media_active |= IFM_HDX;
262 
263 	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
264 		if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
265 			mii->mii_media_active |= IFM_ETH_MASTER;
266 	}
267 }
268 
269 static void
270 jmphy_reset(struct mii_softc *sc)
271 {
272 	struct jmphy_softc *jsc;
273 	uint16_t t2cr, val;
274 	int i;
275 
276 	jsc = (struct jmphy_softc *)sc;
277 
278 	/* Disable sleep mode. */
279 	PHY_WRITE(sc, JMPHY_TMCTL,
280 	    PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
281 	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
282 
283 	for (i = 0; i < 1000; i++) {
284 		DELAY(1);
285 		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
286 			break;
287 	}
288 	/* Perform vendor recommended PHY calibration. */
289 	if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
290 		/* Select PHY test mode 1. */
291 		t2cr = PHY_READ(sc, MII_100T2CR);
292 		t2cr &= ~GTCR_TEST_MASK;
293 		t2cr |= 0x2000;
294 		PHY_WRITE(sc, MII_100T2CR, t2cr);
295 		/* Apply calibration patch. */
296 		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
297 		    JMPHY_EXT_COMM_2);
298 		val = PHY_READ(sc, JMPHY_SPEC_DATA);
299 		val &= ~0x0002;
300 		val |= 0x0010 | 0x0001;
301 		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
302 		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
303 		    JMPHY_EXT_COMM_2);
304 
305 		/* XXX 20ms to complete recalibration. */
306 		DELAY(20 * 1000);
307 
308 		PHY_READ(sc, MII_100T2CR);
309 		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
310 		    JMPHY_EXT_COMM_2);
311 		val = PHY_READ(sc, JMPHY_SPEC_DATA);
312 		val &= ~(0x0001 | 0x0002 | 0x0010);
313 		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
314 		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
315 		    JMPHY_EXT_COMM_2);
316 		/* Disable PHY test mode. */
317 		PHY_READ(sc, MII_100T2CR);
318 		t2cr &= ~GTCR_TEST_MASK;
319 		PHY_WRITE(sc, MII_100T2CR, t2cr);
320 	}
321 }
322 
323 static uint16_t
324 jmphy_anar(struct ifmedia_entry *ife)
325 {
326 	uint16_t anar;
327 
328 	anar = 0;
329 	switch (IFM_SUBTYPE(ife->ifm_media)) {
330 	case IFM_AUTO:
331 		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
332 		break;
333 	case IFM_1000_T:
334 		break;
335 	case IFM_100_TX:
336 		anar |= ANAR_TX | ANAR_TX_FD;
337 		break;
338 	case IFM_10_T:
339 		anar |= ANAR_10 | ANAR_10_FD;
340 		break;
341 	default:
342 		break;
343 	}
344 
345 	return (anar);
346 }
347 
348 static int
349 jmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
350 {
351 	uint16_t anar, bmcr, gig;
352 
353 	gig = 0;
354 	bmcr = PHY_READ(sc, MII_BMCR);
355 	switch (IFM_SUBTYPE(ife->ifm_media)) {
356 	case IFM_AUTO:
357 		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
358 		break;
359 	case IFM_1000_T:
360 		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
361 		break;
362 	case IFM_100_TX:
363 	case IFM_10_T:
364 		break;
365 	case IFM_NONE:
366 		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
367 		return (EJUSTRETURN);
368 	default:
369 		return (EINVAL);
370 	}
371 
372 	if ((ife->ifm_media & IFM_LOOP) != 0)
373 		bmcr |= BMCR_LOOP;
374 
375 	anar = jmphy_anar(ife);
376 	if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
377 	    (ife->ifm_media & IFM_FDX) != 0) &&
378 	    ((ife->ifm_media & IFM_FLOW) != 0 ||
379 	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
380 		anar |= ANAR_PAUSE_TOWARDS;
381 
382 	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
383 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
384 			gig |= GTCR_MAN_MS;
385 			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
386 				gig |= GTCR_ADV_MS;
387 		}
388 		PHY_WRITE(sc, MII_100T2CR, gig);
389 	}
390 	PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
391 	PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
392 
393 	return (EJUSTRETURN);
394 }
395