xref: /dragonfly/sys/dev/netif/mii_layer/atphy.c (revision 7ff0fc30)
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 /*
29  * Driver for the Attansic/Atheros F1 10/100/1000 PHY.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/socket.h>
38 
39 #include <net/if.h>
40 #include <net/if_media.h>
41 
42 #include <dev/netif/mii_layer/mii.h>
43 #include <dev/netif/mii_layer/miivar.h>
44 #include <dev/netif/mii_layer/atphyreg.h>
45 
46 #include "miibus_if.h"
47 #include "miidevs.h"
48 
49 static int	atphy_probe(device_t);
50 static int	atphy_attach(device_t);
51 static int	atphy_service(struct mii_softc *, struct mii_data *, int);
52 static void	atphy_status(struct mii_softc *);
53 static void	atphy_reset(struct mii_softc *);
54 static uint16_t	atphy_anar(struct ifmedia_entry *);
55 static int	atphy_setmedia(struct mii_softc *sc, int media);
56 
57 static device_method_t atphy_methods[] = {
58 	/* Device interface. */
59 	DEVMETHOD(device_probe,		atphy_probe),
60 	DEVMETHOD(device_attach,	atphy_attach),
61 	DEVMETHOD(device_detach,        ukphy_detach),
62 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
63 	DEVMETHOD_END
64 };
65 
66 static devclass_t atphy_devclass;
67 static driver_t atphy_driver = {
68 	"atphy",
69 	atphy_methods,
70 	sizeof(struct mii_softc)
71 };
72 
73 DRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, NULL, NULL);
74 
75 static const struct mii_phydesc atphys[] = {
76 #if 0
77 	MII_PHYDESC(xxATHEROS,	F1),
78 	MII_PHYDESC(xxATHEROS,	F1_7),
79 	MII_PHYDESC(xxATHEROS,	AR8021),
80 	MII_PHYDESC(xxATHEROS,	F2),
81 #endif
82 	MII_PHYDESC(ATHEROS,	F1),
83 	MII_PHYDESC(ATHEROS,    F1_7),
84 	MII_PHYDESC(ATHEROS,    F2),
85 	MII_PHYDESC_NULL
86 };
87 
88 #if 0
89 static const struct mii_phy_funcs atphy_funcs = {
90 	atphy_service,
91 	atphy_status,
92 	atphy_reset
93 };
94 #endif
95 
96 static int
97 atphy_probe(device_t dev)
98 {
99 	/*return (mii_phy_dev_probe(dev, atphys, BUS_PROBE_DEFAULT));*/
100 	struct mii_attach_args *ma = device_get_ivars(dev);
101 	const struct mii_phydesc *mpd;
102 
103 	mpd = mii_phy_match(ma, atphys);
104 	if (mpd != NULL) {
105 		device_set_desc(dev, mpd->mpd_name);
106 		return 0;
107 	}
108 	return ENXIO;
109 }
110 
111 static int
112 atphy_attach(device_t dev)
113 {
114 	/*mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &atphy_funcs, 1);*/
115 
116 	struct mii_softc *sc;
117 	struct mii_attach_args *ma;
118 	struct mii_data *mii;
119 
120 	sc = device_get_softc(dev);
121 	ma = device_get_ivars(dev);
122 
123 	mii_softc_init(sc, ma);
124 	sc->mii_dev = device_get_parent(dev);
125 	mii = device_get_softc(sc->mii_dev);
126 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
127 
128 	sc->mii_inst = mii->mii_instance;
129 	sc->mii_service = atphy_service;
130 	sc->mii_pdata = mii;
131 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
132 
133 	mii->mii_instance++;
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 
139 	device_printf(dev, " ");
140 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
141 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
142 		kprintf("no media present");
143 	else
144 		mii_phy_add_media(sc);
145 	kprintf("\n");
146 
147 	atphy_reset(sc);
148 
149 	MIIBUS_MEDIAINIT(sc->mii_dev);
150 	return (0);
151 }
152 
153 static int
154 atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
155 {
156 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
157 	uint16_t anar, bmcr, bmsr;
158 
159 	switch (cmd) {
160 	case MII_POLLSTAT:
161 		break;
162 
163 	case MII_MEDIACHG:
164 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
165 		    IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
166 			atphy_setmedia(sc, ife->ifm_media);
167 			break;
168 		}
169 
170 		bmcr = 0;
171 		switch (IFM_SUBTYPE(ife->ifm_media)) {
172 		case IFM_100_TX:
173 			bmcr = BMCR_S100;
174 			break;
175 		case IFM_10_T:
176 			bmcr = BMCR_S10;
177 			break;
178 		case IFM_NONE:
179 			bmcr = PHY_READ(sc, MII_BMCR);
180 			/*
181 			 * XXX
182 			 * Due to an unknown reason powering down PHY resulted
183 			 * in unexpected results such as inaccessibility of
184 			 * hardware of freshly rebooted system. Disable
185 			 * powering down PHY until I got more information for
186 			 * Attansic/Atheros PHY hardwares.
187 			 */
188 			PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
189 			goto done;
190 		default:
191 			return (EINVAL);
192 		}
193 
194 		anar = atphy_anar(ife);
195 		if ((ife->ifm_media & IFM_FDX) != 0) {
196 			bmcr |= BMCR_FDX;
197 #if defined(__FreeBSD__)
198 			if ((ife->ifm_media & IFM_FLOW) != 0 ||
199 			    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
200 				anar |= ANAR_PAUSE_TOWARDS;
201 #else
202 			if ((ife->ifm_media & IFM_FLOW) != 0)
203 				anar |= ANAR_PAUSE_TOWARDS;
204 #endif
205 		}
206 
207 		if ((sc->mii_extcapabilities & (EXTSR_1000TFDX |
208 		    EXTSR_1000THDX)) != 0)
209 			PHY_WRITE(sc, MII_100T2CR, 0);
210 		PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
211 
212 		/*
213 		 * Reset the PHY so all changes take effect.
214 		 */
215 		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN |
216 		    BMCR_STARTNEG);
217 done:
218 		break;
219 
220 	case MII_TICK:
221 		/*
222 		 * Only used for autonegotiation.
223 		 */
224 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
225 			sc->mii_ticks = 0;
226 			break;
227 		}
228 
229 		/*
230 		 * Check for link.
231 		 * Read the status register twice; BMSR_LINK is latch-low.
232 		 */
233 		bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
234 		if (bmsr & BMSR_LINK) {
235 			sc->mii_ticks = 0;
236 			break;
237 		}
238 
239 		/* Announce link loss right after it happens. */
240 		if (sc->mii_ticks++ == 0)
241 			break;
242 		if (sc->mii_ticks <= sc->mii_anegticks)
243 			return (0);
244 
245 		sc->mii_ticks = 0;
246 		atphy_setmedia(sc, ife->ifm_media);
247 		break;
248 	}
249 
250 	/* Update the media status. */
251 	atphy_status(sc);
252 
253 	/* Callback if something changed. */
254 	mii_phy_update(sc, cmd);
255 	return (0);
256 }
257 
258 static void
259 atphy_status(struct mii_softc *sc)
260 {
261 	struct mii_data *mii = sc->mii_pdata;
262 	uint32_t bmsr, bmcr, ssr;
263 
264 	mii->mii_media_status = IFM_AVALID;
265 	mii->mii_media_active = IFM_ETHER;
266 
267 	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
268 	if ((bmsr & BMSR_LINK) != 0)
269 		mii->mii_media_status |= IFM_ACTIVE;
270 
271 	bmcr = PHY_READ(sc, MII_BMCR);
272 	if ((bmcr & BMCR_ISO) != 0) {
273 		mii->mii_media_active |= IFM_NONE;
274 		mii->mii_media_status = 0;
275 		return;
276 	}
277 
278 	if ((bmcr & BMCR_LOOP) != 0)
279 		mii->mii_media_active |= IFM_LOOP;
280 
281 	ssr = PHY_READ(sc, ATPHY_SSR);
282 	if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
283 		/* Erg, still trying, I guess... */
284 		mii->mii_media_active |= IFM_NONE;
285 		return;
286 	}
287 
288 	switch (ssr & ATPHY_SSR_SPEED_MASK) {
289 	case ATPHY_SSR_1000MBS:
290 		mii->mii_media_active |= IFM_1000_T;
291 		/*
292 		 * atphy(4) has a valid link so reset mii_ticks.
293 		 * Resetting mii_ticks is needed in order to
294 		 * detect link loss after auto-negotiation.
295 		 */
296 		sc->mii_ticks = 0;
297 		break;
298 	case ATPHY_SSR_100MBS:
299 		mii->mii_media_active |= IFM_100_TX;
300 		sc->mii_ticks = 0;
301 		break;
302 	case ATPHY_SSR_10MBS:
303 		mii->mii_media_active |= IFM_10_T;
304 		sc->mii_ticks = 0;
305 		break;
306 	default:
307 		mii->mii_media_active |= IFM_NONE;
308 		return;
309 	}
310 
311 	if ((ssr & ATPHY_SSR_DUPLEX) != 0)
312 		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
313 	else
314 		mii->mii_media_active |= IFM_HDX;
315 
316 	if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
317 	    (PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
318 		mii->mii_media_active |= IFM_ETH_MASTER;
319 }
320 
321 static void
322 atphy_reset(struct mii_softc *sc)
323 {
324 	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
325 	uint32_t reg;
326 	int i;
327 
328 	/* Take PHY out of power down mode. */
329 	PHY_WRITE(sc, 29, 0x29);
330 	PHY_WRITE(sc, 30, 0);
331 
332 	reg = PHY_READ(sc, ATPHY_SCR);
333 	/* Enable automatic crossover. */
334 	reg |= ATPHY_SCR_AUTO_X_MODE;
335 	/* Disable power down. */
336 	reg &= ~ATPHY_SCR_MAC_PDOWN;
337 	/* Enable CRS on Tx. */
338 	reg |= ATPHY_SCR_ASSERT_CRS_ON_TX;
339 	/* Auto correction for reversed cable polarity. */
340 	reg |= ATPHY_SCR_POLARITY_REVERSAL;
341 	PHY_WRITE(sc, ATPHY_SCR, reg);
342 
343 	/* Workaround F1 bug to reset phy. */
344 	atphy_setmedia(sc, ife == NULL ? IFM_AUTO : ife->ifm_media);
345 
346 	for (i = 0; i < 1000; i++) {
347 		DELAY(1);
348 		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
349 			break;
350 	}
351 }
352 
353 static uint16_t
354 atphy_anar(struct ifmedia_entry *ife)
355 {
356 	uint16_t anar;
357 
358 	anar = 0;
359 	switch (IFM_SUBTYPE(ife->ifm_media)) {
360 	case IFM_AUTO:
361 		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
362 		return (anar);
363 	case IFM_1000_T:
364 		return (anar);
365 	case IFM_100_TX:
366 		anar |= ANAR_TX;
367 		break;
368 	case IFM_10_T:
369 		anar |= ANAR_10;
370 		break;
371 	default:
372 		return (0);
373 	}
374 
375 	if ((ife->ifm_media & IFM_FDX) != 0) {
376 		if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX)
377 			anar |= ANAR_TX_FD;
378 		else
379 			anar |= ANAR_10_FD;
380 	}
381 
382 	return (anar);
383 }
384 
385 static int
386 atphy_setmedia(struct mii_softc *sc, int media)
387 {
388 	uint16_t anar;
389 
390 	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
391 #if defined(__FreeBSD__)
392 	if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) &&
393 	    ((media & IFM_FLOW) != 0 ||
394 	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
395 		anar |= ANAR_PAUSE_TOWARDS;
396 #else
397 	if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) &&
398 	    ((media & IFM_FLOW) != 0))
399 		anar |= ANAR_PAUSE_TOWARDS;
400 #endif
401 	PHY_WRITE(sc, MII_ANAR, anar);
402 	if ((sc->mii_extcapabilities &
403 	     (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
404 		PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX |
405 		    GTCR_ADV_1000THDX);
406 	else if (sc->mii_model == MII_MODEL_ATHEROS_F1) {
407 		/*
408 		 * AR8132 has 10/100 PHY and the PHY uses the same
409 		 * model number of F1 gigabit PHY.  The PHY has no
410 		 * ability to establish gigabit link so explicitly
411 		 * disable 1000baseT configuration for the PHY.
412 		 * Otherwise, there is a case that atphy(4) could
413 		 * not establish a link against gigabit link partner
414 		 * unless the link partner supports down-shifting.
415 		 */
416 		PHY_WRITE(sc, MII_100T2CR, 0);
417 	}
418 	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
419 
420 	return (EJUSTRETURN);
421 }
422