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