mii_physubr.c revision 1.8
1/*	$OpenBSD: mii_physubr.c,v 1.8 2001/05/03 12:31:43 aaron Exp $	*/
2/*	$NetBSD: mii_physubr.c,v 1.16 2000/03/15 20:34:43 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#include <net/route.h>
56
57#include <dev/mii/mii.h>
58#include <dev/mii/miivar.h>
59
60/*
61 * Media to register setting conversion table.  Order matters.
62 */
63const struct mii_media mii_media_table[] = {
64	{ BMCR_ISO,		ANAR_CSMA },		/* None */
65	{ 0,			ANAR_CSMA|ANAR_10 },	/* 10baseT */
66	{ BMCR_FDX,		ANAR_CSMA|ANAR_10_FD },	/* 10baseT-FDX */
67	{ BMCR_S100,		ANAR_CSMA|ANAR_T4 },	/* 100baseT4 */
68	{ BMCR_S100,		ANAR_CSMA|ANAR_TX },	/* 100baseTX */
69	{ BMCR_S100|BMCR_FDX,	ANAR_CSMA|ANAR_TX_FD },	/* 100baseTX-FDX */
70};
71
72void	mii_phy_auto_timeout __P((void *));
73
74void
75mii_phy_setmedia(sc)
76	struct mii_softc *sc;
77{
78	struct mii_data *mii = sc->mii_pdata;
79	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
80	int bmcr, anar;
81
82	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
83		if ((PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) == 0)
84			(void) mii_phy_auto(sc, 1);
85		return;
86	}
87
88	/*
89	 * Table index is stored in the media entry.
90	 */
91
92#ifdef DIAGNOSTIC
93	if (ife->ifm_data < 0 || ife->ifm_data >= MII_NMEDIA)
94		panic("mii_phy_setmedia");
95#endif
96
97	anar = mii_media_table[ife->ifm_data].mm_anar;
98	bmcr = mii_media_table[ife->ifm_data].mm_bmcr;
99
100	if (ife->ifm_media & IFM_LOOP)
101		bmcr |= BMCR_LOOP;
102
103	PHY_WRITE(sc, MII_ANAR, anar);
104	PHY_WRITE(sc, MII_BMCR, bmcr);
105}
106
107int
108mii_phy_auto(sc, waitfor)
109	struct mii_softc *sc;
110	int waitfor;
111{
112	int bmsr, i;
113
114	if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
115		PHY_WRITE(sc, MII_ANAR,
116		    BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA);
117		PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
118	}
119
120	if (waitfor) {
121		/* Wait 500ms for it to complete. */
122		for (i = 0; i < 500; i++) {
123			if ((bmsr = PHY_READ(sc, MII_BMSR)) & BMSR_ACOMP)
124				return (0);
125			delay(1000);
126		}
127
128		/*
129		 * Don't need to worry about clearing MIIF_DOINGAUTO.
130		 * If that's set, a timeout is pending, and it will
131		 * clear the flag.
132		 */
133		return (EIO);
134	}
135
136	/*
137	 * Just let it finish asynchronously.  This is for the benefit of
138	 * the tick handler driving autonegotiation.  Don't want 500ms
139	 * delays all the time while the system is running!
140	 */
141	if (sc->mii_flags & MIIF_AUTOTSLEEP) {
142		sc->mii_flags |= MIIF_DOINGAUTO;
143		tsleep(&sc->mii_flags, PZERO, "miiaut", hz >> 1);
144		mii_phy_auto_timeout(sc);
145	} else if ((sc->mii_flags & MIIF_DOINGAUTO) == 0) {
146		sc->mii_flags |= MIIF_DOINGAUTO;
147		timeout_set(&sc->mii_phy_timo, mii_phy_auto_timeout, sc);
148		timeout_add(&sc->mii_phy_timo, hz / 2);
149	}
150	return (EJUSTRETURN);
151}
152
153void
154mii_phy_auto_timeout(arg)
155	void *arg;
156{
157	struct mii_softc *sc = arg;
158	int s, bmsr;
159
160	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
161		return;
162
163	s = splnet();
164	sc->mii_flags &= ~MIIF_DOINGAUTO;
165	bmsr = PHY_READ(sc, MII_BMSR);
166
167	/* Update the media status. */
168	(void) (*sc->mii_service)(sc, sc->mii_pdata, MII_POLLSTAT);
169	splx(s);
170}
171
172int
173mii_phy_tick(sc)
174	struct mii_softc *sc;
175{
176	struct mii_data *mii = sc->mii_pdata;
177	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178	int reg;
179
180	/* Just bail now if the interface is down. */
181	if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
182		return (EJUSTRETURN);
183
184	/*
185	 * If we're not doing autonegotiation, we don't need to do
186	 * any extra work here.  However, we need to check the link
187	 * status so we can generate an announcement if the status
188	 * changes.
189	 */
190	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
191		return (0);
192
193	/* Read the status register twice; BMSR_LINK is latch-low. */
194	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
195	if (reg & BMSR_LINK) {
196		/*
197		 * See above.
198		 */
199		return (0);
200	}
201
202	/*
203	 * Only retry autonegotiation every 5 seconds.
204	 */
205	if (++sc->mii_ticks != 5)
206		return (EJUSTRETURN);
207
208	sc->mii_ticks = 0;
209	mii_phy_reset(sc);
210
211	if (mii_phy_auto(sc, 0) == EJUSTRETURN)
212		return (EJUSTRETURN);
213
214	/*
215	 * Might need to generate a status message if autonegotiation
216	 * failed.
217	 */
218	return (0);
219}
220
221void
222mii_phy_reset(sc)
223	struct mii_softc *sc;
224{
225	int reg, i;
226
227	if (sc->mii_flags & MIIF_NOISOLATE)
228		reg = BMCR_RESET;
229	else
230		reg = BMCR_RESET | BMCR_ISO;
231	PHY_WRITE(sc, MII_BMCR, reg);
232
233	/* Wait 100ms for it to complete. */
234	for (i = 0; i < 100; i++) {
235		reg = PHY_READ(sc, MII_BMCR);
236		if ((reg & BMCR_RESET) == 0)
237			break;
238		delay(1000);
239	}
240
241	if (sc->mii_inst != 0 && ((sc->mii_flags & MIIF_NOISOLATE) == 0))
242		PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
243}
244
245void
246mii_phy_down(sc)
247	struct mii_softc *sc;
248{
249	if (sc->mii_flags & MIIF_DOINGAUTO) {
250		sc->mii_flags &= ~MIIF_DOINGAUTO;
251		timeout_del(&sc->mii_phy_timo);
252	}
253}
254
255
256void
257mii_phy_status(sc)
258	struct mii_softc *sc;
259{
260
261	(*sc->mii_status)(sc);
262}
263
264void
265mii_phy_update(sc, cmd)
266	struct mii_softc *sc;
267	int cmd;
268{
269	struct mii_data *mii = sc->mii_pdata;
270
271	if (sc->mii_media_active != mii->mii_media_active ||
272	    sc->mii_media_status != mii->mii_media_status ||
273	    cmd == MII_MEDIACHG) {
274		(*mii->mii_statchg)(sc->mii_dev.dv_parent);
275		mii_phy_statusmsg(sc);
276		sc->mii_media_active = mii->mii_media_active;
277		sc->mii_media_status = mii->mii_media_status;
278	}
279}
280
281void
282mii_phy_statusmsg(sc)
283	struct mii_softc *sc;
284{
285	struct mii_data *mii = sc->mii_pdata;
286	struct ifnet *ifp = mii->mii_ifp;
287	int s, baudrate, link_state, announce = 0;
288
289	if (mii->mii_media_status & IFM_AVALID) {
290		if (mii->mii_media_status & IFM_ACTIVE)
291			link_state = LINK_STATE_UP;
292		else
293			link_state = LINK_STATE_DOWN;
294	} else
295		link_state = LINK_STATE_UNKNOWN;
296
297	baudrate = ifmedia_baudrate(mii->mii_media_active);
298
299	if (link_state != ifp->if_link_state) {
300		ifp->if_link_state = link_state;
301		/*
302		 * XXX Right here we'd like to notify protocols
303		 * XXX that the link status has changed, so that
304		 * XXX e.g. Duplicate Address Detection can restart.
305		 */
306		announce = 1;
307	}
308
309	if (baudrate != ifp->if_baudrate) {
310		ifp->if_baudrate = baudrate;
311		announce = 1;
312	}
313
314	if (announce) {
315		s = splimp();	/* XXX Should be splnet() */
316		rt_ifmsg(ifp);
317		splx(s);
318	}
319}
320
321/*
322 * Initialize generic PHY media based on BMSR, called when a PHY is
323 * attached.  We expect to be set up to print a comma-separated list
324 * of media names.  Does not print a newline.
325 */
326void
327mii_phy_add_media(sc)
328	struct mii_softc *sc;
329{
330	struct mii_data *mii = sc->mii_pdata;
331
332#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
333
334	if ((sc->mii_flags & MIIF_NOISOLATE) == 0)
335		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
336		    MII_MEDIA_NONE);
337
338	if (sc->mii_capabilities & BMSR_10THDX) {
339		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
340		    MII_MEDIA_10_T);
341#if 0
342		if ((sc->mii_flags & MIIF_NOLOOP) == 0)
343			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_LOOP,
344			    sc->mii_inst), MII_MEDIA_10_T);
345#endif
346	}
347	if (sc->mii_capabilities & BMSR_10TFDX) {
348		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
349		    MII_MEDIA_10_T_FDX);
350	}
351	if (sc->mii_capabilities & BMSR_100TXHDX) {
352		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
353		    MII_MEDIA_100_TX);
354#if 0
355		if ((sc->mii_flags & MIIF_NOLOOP) == 0)
356			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
357			    sc->mii_inst), MII_MEDIA_100_TX);
358#endif
359	}
360	if (sc->mii_capabilities & BMSR_100TXFDX) {
361		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
362		    MII_MEDIA_100_TX_FDX);
363	}
364	if (sc->mii_capabilities & BMSR_100T4) {
365		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, sc->mii_inst),
366		    MII_MEDIA_100_T4);
367#if 0
368		if ((sc->mii_flags & MIIF_NOLOOP) == 0)
369			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, IFM_LOOP,
370			    sc->mii_inst), MII_MEDIA_100_T4);
371#endif
372	}
373	if (sc->mii_capabilities & BMSR_ANEG) {
374		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst),
375		    MII_NMEDIA);	/* intentionally invalid index */
376	}
377#undef ADD
378}
379
380void
381mii_phy_delete_media(sc)
382	struct mii_softc *sc;
383{
384	struct mii_data *mii = sc->mii_pdata;
385
386	ifmedia_delete_instance(&mii->mii_media, sc->mii_inst);
387}
388
389int
390mii_phy_activate(self, act)
391	struct device *self;
392	enum devact act;
393{
394	int rv = 0;
395
396	switch (act) {
397	case DVACT_ACTIVATE:
398		rv = EOPNOTSUPP;
399		break;
400
401	case DVACT_DEACTIVATE:
402		/* Nothing special to do. */
403		break;
404	}
405
406	return (rv);
407}
408
409int
410mii_phy_detach(self, flags)
411	struct device *self;
412	int flags;
413{
414	struct mii_softc *sc = (void *) self;
415
416	if (sc->mii_flags & MIIF_DOINGAUTO)
417		timeout_del(&sc->mii_phy_timo);
418
419	mii_phy_delete_media(sc);
420
421	return (0);
422}
423