mii_physubr.c revision 95718
1/*	$NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the NetBSD
22 *	Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 *    contributors may be used to endorse or promote products derived
25 *    from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/*
41 * Subroutines common to all PHYs.
42 */
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/kernel.h>
47#include <sys/socket.h>
48#include <sys/errno.h>
49#include <sys/module.h>
50#include <sys/bus.h>
51
52
53#include <net/if.h>
54#include <net/if_media.h>
55
56#include <dev/mii/mii.h>
57#include <dev/mii/miivar.h>
58
59#include "miibus_if.h"
60
61#if !defined(lint)
62static const char rcsid[] =
63  "$FreeBSD: head/sys/dev/mii/mii_physubr.c 95718 2002-04-29 11:57:30Z phk $";
64#endif
65
66/*
67 * Media to register setting conversion table.  Order matters.
68 */
69const struct mii_media mii_media_table[MII_NMEDIA] = {
70	/* None */
71	{ BMCR_ISO,		ANAR_CSMA,
72	  0, },
73
74	/* 10baseT */
75	{ BMCR_S10,		ANAR_CSMA|ANAR_10,
76	  0, },
77
78	/* 10baseT-FDX */
79	{ BMCR_S10|BMCR_FDX,	ANAR_CSMA|ANAR_10_FD,
80	  0, },
81
82	/* 100baseT4 */
83	{ BMCR_S100,		ANAR_CSMA|ANAR_T4,
84	  0, },
85
86	/* 100baseTX */
87	{ BMCR_S100,		ANAR_CSMA|ANAR_TX,
88	  0, },
89
90	/* 100baseTX-FDX */
91	{ BMCR_S100|BMCR_FDX,	ANAR_CSMA|ANAR_TX_FD,
92	  0, },
93
94	/* 1000baseX */
95	{ BMCR_S1000,		ANAR_CSMA,
96	  0, },
97
98	/* 1000baseX-FDX */
99	{ BMCR_S1000|BMCR_FDX,	ANAR_CSMA,
100	  0, },
101
102	/* 1000baseT */
103	{ BMCR_S1000,		ANAR_CSMA,
104	  GTCR_ADV_1000THDX },
105
106	/* 1000baseT-FDX */
107	{ BMCR_S1000,		ANAR_CSMA,
108	  GTCR_ADV_1000TFDX },
109};
110
111void	mii_phy_auto_timeout(void *);
112
113void
114mii_phy_setmedia(struct mii_softc *sc)
115{
116	struct mii_data *mii = sc->mii_pdata;
117	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
118	int bmcr, anar, gtcr;
119
120	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
121		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
122			(void) mii_phy_auto(sc, 1);
123		return;
124	}
125
126	/*
127	 * Table index is stored in the media entry.
128	 */
129
130	KASSERT(ife->ifm_data >=0 && ife->ifm_data < MII_NMEDIA,
131	    ("invalid ife->ifm_data (0x%x) in mii_phy_setmedia",
132	    ife->ifm_data));
133
134	anar = mii_media_table[ife->ifm_data].mm_anar;
135	bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
136	gtcr = mii_media_table[ife->ifm_data].mm_gtcr;
137
138	if (mii->mii_media.ifm_media & IFM_ETH_MASTER) {
139		switch (IFM_SUBTYPE(ife->ifm_media)) {
140		case IFM_1000_T:
141			gtcr |= GTCR_MAN_MS|GTCR_ADV_MS;
142			break;
143
144		default:
145			panic("mii_phy_setmedia: MASTER on wrong media");
146		}
147	}
148
149	if (ife->ifm_media & IFM_LOOP)
150		bmcr |= BMCR_LOOP;
151
152	PHY_WRITE(sc, MII_ANAR, anar);
153	PHY_WRITE(sc, MII_BMCR, bmcr);
154	if (sc->mii_flags & MIIF_HAVE_GTCR)
155		PHY_WRITE(sc, MII_100T2CR, gtcr);
156}
157
158int
159mii_phy_auto(struct mii_softc *sc, int waitfor)
160{
161	int bmsr, i;
162
163	if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
164		/*
165		 * Check for 1000BASE-X.  Autonegotiation is a bit
166		 * different on such devices.
167		 */
168		if (sc->mii_flags & MIIF_IS_1000X) {
169			uint16_t anar = 0;
170
171			if (sc->mii_extcapabilities & EXTSR_1000XFDX)
172				anar |= ANAR_X_FD;
173			if (sc->mii_extcapabilities & EXTSR_1000XHDX)
174				anar |= ANAR_X_HD;
175
176			if (sc->mii_flags & MIIF_DOPAUSE) {
177				/* XXX Asymmetric vs. symmetric? */
178				anar |= ANLPAR_X_PAUSE_TOWARDS;
179			}
180
181			PHY_WRITE(sc, MII_ANAR, anar);
182		} else {
183			uint16_t anar;
184
185			anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
186			    ANAR_CSMA;
187			if (sc->mii_flags & MIIF_DOPAUSE)
188				anar |= ANAR_FC;
189			PHY_WRITE(sc, MII_ANAR, anar);
190			if (sc->mii_flags & MIIF_HAVE_GTCR) {
191				uint16_t gtcr = 0;
192
193				if (sc->mii_extcapabilities & EXTSR_1000TFDX)
194					gtcr |= GTCR_ADV_1000TFDX;
195				if (sc->mii_extcapabilities & EXTSR_1000THDX)
196					gtcr |= GTCR_ADV_1000THDX;
197
198				PHY_WRITE(sc, MII_100T2CR, gtcr);
199			}
200		}
201		PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
202	}
203
204	if (waitfor) {
205		/* Wait 500ms for it to complete. */
206		for (i = 0; i < 500; i++) {
207			if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP)
208				return (0);
209			DELAY(1000);
210		}
211
212		/*
213		 * Don't need to worry about clearing MIIF_DOINGAUTO.
214		 * If that's set, a timeout is pending, and it will
215		 * clear the flag.
216		 */
217		return (EIO);
218	}
219
220	/*
221	 * Just let it finish asynchronously.  This is for the benefit of
222	 * the tick handler driving autonegotiation.  Don't want 500ms
223	 * delays all the time while the system is running!
224	 */
225	if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
226		sc->mii_flags |= MIIF_DOINGAUTO;
227		sc->mii_auto_ch = timeout(mii_phy_auto_timeout, sc, hz >> 1);
228	}
229	return (EJUSTRETURN);
230}
231
232void
233mii_phy_auto_stop(sc)
234	struct mii_softc *sc;
235{
236	if (sc->mii_flags & MIIF_DOINGAUTO) {
237		sc->mii_flags &= ~MIIF_DOINGAUTO;
238		untimeout(mii_phy_auto_timeout, sc, sc->mii_auto_ch);
239	}
240}
241
242void
243mii_phy_auto_timeout(void *arg)
244{
245	struct mii_softc *sc = arg;
246	int s, bmsr;
247
248#if 0
249	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
250		return;
251#endif
252
253	s = splnet();
254	sc->mii_flags &= ~MIIF_DOINGAUTO;
255	bmsr = PHY_READ(sc, MII_BMSR);
256
257	/* Update the media status. */
258	(void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT);
259	splx(s);
260}
261
262int
263mii_phy_tick(struct mii_softc *sc)
264{
265	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
266	struct ifnet *ifp = sc->mii_pdata->mii_ifp;
267	int reg;
268
269	/* Just bail now if the interface is down. */
270	if ((ifp->if_flags & IFF_UP) == 0)
271		return (EJUSTRETURN);
272
273	/*
274	 * If we're not doing autonegotiation, we don't need to do
275	 * any extra work here.  However, we need to check the link
276	 * status so we can generate an announcement if the status
277	 * changes.
278	 */
279	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
280		return (0);
281
282	/* Read the status register twice; BMSR_LINK is latch-low. */
283	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
284	if (reg & BMSR_LINK) {
285		/*
286		 * See above.
287		 */
288		return (0);
289	}
290
291	/*
292	 * Only retry autonegotiation every N seconds.
293	 */
294	if (sc->mii_anegticks == 0)
295		sc->mii_anegticks = 5;
296	if (++sc->mii_ticks != sc->mii_anegticks)
297		return (EJUSTRETURN);
298
299	sc->mii_ticks = 0;
300	mii_phy_reset(sc);
301	if (mii_phy_auto(sc, 0) == EJUSTRETURN)
302		return (EJUSTRETURN);
303
304	/*
305	 * Might need to generate a status message if autonegotiation
306	 * failed.
307	 */
308	return (0);
309}
310
311void
312mii_phy_reset(struct mii_softc *sc)
313{
314	int reg, i;
315
316	if (sc->mii_flags & MIIF_NOISOLATE)
317		reg = BMCR_RESET;
318	else
319		reg = BMCR_RESET | BMCR_ISO;
320	PHY_WRITE(sc, MII_BMCR, reg);
321
322	/* Wait 100ms for it to complete. */
323	for (i = 0; i < 100; i++) {
324		reg = PHY_READ(sc, MII_BMCR);
325		if ((reg & BMCR_RESET) == 0)
326			break;
327		DELAY(1000);
328	}
329
330	if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
331		PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
332}
333
334void
335mii_phy_update(sc, cmd)
336	struct mii_softc *sc;
337	int cmd;
338{
339	struct mii_data *mii = sc->mii_pdata;
340
341	if (sc->mii_media_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
342		MIIBUS_STATCHG(sc->mii_dev);
343		sc->mii_media_active = mii->mii_media_active;
344	}
345	if (sc->mii_media_status != mii->mii_media_status) {
346		MIIBUS_LINKCHG(sc->mii_dev);
347		sc->mii_media_status = mii->mii_media_status;
348	}
349}
350
351/*
352 * Given an ifmedia word, return the corresponding ANAR value.
353 */
354int
355mii_anar(media)
356	int media;
357{
358	int rv;
359
360	switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) {
361	case IFM_ETHER|IFM_10_T:
362		rv = ANAR_10|ANAR_CSMA;
363		break;
364	case IFM_ETHER|IFM_10_T|IFM_FDX:
365		rv = ANAR_10_FD|ANAR_CSMA;
366		break;
367	case IFM_ETHER|IFM_100_TX:
368		rv = ANAR_TX|ANAR_CSMA;
369		break;
370	case IFM_ETHER|IFM_100_TX|IFM_FDX:
371		rv = ANAR_TX_FD|ANAR_CSMA;
372		break;
373	case IFM_ETHER|IFM_100_T4:
374		rv = ANAR_T4|ANAR_CSMA;
375		break;
376	default:
377		rv = 0;
378		break;
379	}
380
381	return (rv);
382}
383
384/*
385 * Given a BMCR value, return the corresponding ifmedia word.
386 */
387int
388mii_media_from_bmcr(bmcr)
389	int bmcr;
390{
391	int rv = IFM_ETHER;
392
393	if (bmcr & BMCR_S100)
394		rv |= IFM_100_TX;
395	else
396		rv |= IFM_10_T;
397	if (bmcr & BMCR_FDX)
398		rv |= IFM_FDX;
399
400	return (rv);
401}
402
403/*
404 * Initialize generic PHY media based on BMSR, called when a PHY is
405 * attached.  We expect to be set up to print a comma-separated list
406 * of media names.  Does not print a newline.
407 */
408void
409mii_add_media(struct mii_softc *sc)
410{
411	const char *sep = "";
412	struct mii_data *mii;
413
414	mii = device_get_softc(sc->mii_dev);
415	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) {
416		printf("no media present");
417		return;
418	}
419
420#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
421#define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
422
423	if (sc->mii_capabilities & BMSR_10THDX) {
424		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), 0);
425		PRINT("10baseT");
426	}
427	if (sc->mii_capabilities & BMSR_10TFDX) {
428		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
429		    BMCR_FDX);
430		PRINT("10baseT-FDX");
431	}
432	if (sc->mii_capabilities & BMSR_100TXHDX) {
433		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
434		    BMCR_S100);
435		PRINT("100baseTX");
436	}
437	if (sc->mii_capabilities & BMSR_100TXFDX) {
438		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
439		    BMCR_S100|BMCR_FDX);
440		PRINT("100baseTX-FDX");
441	}
442	if (sc->mii_capabilities & BMSR_100T4) {
443		/*
444		 * XXX How do you enable 100baseT4?  I assume we set
445		 * XXX BMCR_S100 and then assume the PHYs will take
446		 * XXX watever action is necessary to switch themselves
447		 * XXX into T4 mode.
448		 */
449		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
450		    BMCR_S100);
451		PRINT("100baseT4");
452	}
453	if (sc->mii_capabilities & BMSR_ANEG) {
454		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
455		    BMCR_AUTOEN);
456		PRINT("auto");
457	}
458
459
460
461#undef ADD
462#undef PRINT
463}
464
465/*
466 * Initialize generic PHY media based on BMSR, called when a PHY is
467 * attached.  We expect to be set up to print a comma-separated list
468 * of media names.  Does not print a newline.
469 */
470void
471mii_phy_add_media(struct mii_softc *sc)
472{
473	struct mii_data *mii = sc->mii_pdata;
474	const char *sep = "";
475
476#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
477#define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
478
479	if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
480		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
481		    MII_MEDIA_NONE);
482
483	/*
484	 * There are different interpretations for the bits in
485	 * HomePNA PHYs.  And there is really only one media type
486	 * that is supported.
487	 */
488	if (sc->mii_flags & MIIF_IS_HPNA) {
489		if (sc->mii_capabilities & BMSR_10THDX) {
490			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0,
491					 sc->mii_inst),
492			    MII_MEDIA_10_T);
493			PRINT("HomePNA1");
494		}
495		return;
496	}
497
498	if (sc->mii_capabilities & BMSR_10THDX) {
499		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
500		    MII_MEDIA_10_T);
501		PRINT("10baseT");
502	}
503	if (sc->mii_capabilities & BMSR_10TFDX) {
504		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
505		    MII_MEDIA_10_T_FDX);
506		PRINT("10baseT-FDX");
507	}
508	if (sc->mii_capabilities & BMSR_100TXHDX) {
509		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
510		    MII_MEDIA_100_TX);
511		PRINT("100baseTX");
512	}
513	if (sc->mii_capabilities & BMSR_100TXFDX) {
514		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
515		    MII_MEDIA_100_TX_FDX);
516		PRINT("100baseTX-FDX");
517	}
518	if (sc->mii_capabilities & BMSR_100T4) {
519		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
520		    MII_MEDIA_100_T4);
521		PRINT("100baseT4");
522	}
523
524	if (sc->mii_extcapabilities & EXTSR_MEDIAMASK) {
525		/*
526		 * XXX Right now only handle 1000SX and 1000TX.  Need
527		 * XXX to handle 1000LX and 1000CX some how.
528		 *
529		 * Note since it can take 5 seconds to auto-negotiate
530		 * a gigabit link, we make anegticks 10 seconds for
531		 * all the gigabit media types.
532		 */
533		if (sc->mii_extcapabilities & EXTSR_1000XHDX) {
534			sc->mii_anegticks = 10;
535			sc->mii_flags |= MIIF_IS_1000X;
536			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0,
537			    sc->mii_inst), MII_MEDIA_1000_X);
538			PRINT("1000baseSX");
539		}
540		if (sc->mii_extcapabilities & EXTSR_1000XFDX) {
541			sc->mii_anegticks = 10;
542			sc->mii_flags |= MIIF_IS_1000X;
543			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX,
544			    sc->mii_inst), MII_MEDIA_1000_X_FDX);
545			PRINT("1000baseSX-FDX");
546		}
547
548		/*
549		 * 1000baseT media needs to be able to manipulate
550		 * master/slave mode.  We set IFM_ETH_MASTER in
551		 * the "don't care mask" and filter it out when
552		 * the media is set.
553		 *
554		 * All 1000baseT PHYs have a 1000baseT control register.
555		 */
556		if (sc->mii_extcapabilities & EXTSR_1000THDX) {
557			sc->mii_anegticks = 10;
558			sc->mii_flags |= MIIF_HAVE_GTCR;
559			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
560			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
561			    sc->mii_inst), MII_MEDIA_1000_T);
562			PRINT("1000baseT");
563		}
564		if (sc->mii_extcapabilities & EXTSR_1000TFDX) {
565			sc->mii_anegticks = 10;
566			sc->mii_flags |= MIIF_HAVE_GTCR;
567			mii->mii_media.ifm_mask |= IFM_ETH_MASTER;
568			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX,
569			    sc->mii_inst), MII_MEDIA_1000_T_FDX);
570			PRINT("1000baseT-FDX");
571		}
572	}
573
574	if (sc->mii_capabilities & BMSR_ANEG) {
575		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
576		    MII_NMEDIA);	/* intentionally invalid index */
577		PRINT("auto");
578	}
579#undef ADD
580#undef PRINT
581}
582
583