xref: /freebsd/sys/dev/mii/xmphy.c (revision de1add1e)
1098ca2bdSWarner Losh /*-
2c0d7d4d4SBill Paul  * Copyright (c) 2000
3c0d7d4d4SBill Paul  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
4c0d7d4d4SBill Paul  *
5c0d7d4d4SBill Paul  * Redistribution and use in source and binary forms, with or without
6c0d7d4d4SBill Paul  * modification, are permitted provided that the following conditions
7c0d7d4d4SBill Paul  * are met:
8c0d7d4d4SBill Paul  * 1. Redistributions of source code must retain the above copyright
9c0d7d4d4SBill Paul  *    notice, this list of conditions and the following disclaimer.
10c0d7d4d4SBill Paul  * 2. Redistributions in binary form must reproduce the above copyright
11c0d7d4d4SBill Paul  *    notice, this list of conditions and the following disclaimer in the
12c0d7d4d4SBill Paul  *    documentation and/or other materials provided with the distribution.
13c0d7d4d4SBill Paul  * 3. All advertising materials mentioning features or use of this software
14c0d7d4d4SBill Paul  *    must display the following acknowledgement:
15c0d7d4d4SBill Paul  *	This product includes software developed by Bill Paul.
16c0d7d4d4SBill Paul  * 4. Neither the name of the author nor the names of any co-contributors
17c0d7d4d4SBill Paul  *    may be used to endorse or promote products derived from this software
18c0d7d4d4SBill Paul  *    without specific prior written permission.
19c0d7d4d4SBill Paul  *
20c0d7d4d4SBill Paul  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21c0d7d4d4SBill Paul  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22c0d7d4d4SBill Paul  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23c0d7d4d4SBill Paul  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24c0d7d4d4SBill Paul  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25c0d7d4d4SBill Paul  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26c0d7d4d4SBill Paul  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27c0d7d4d4SBill Paul  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28c0d7d4d4SBill Paul  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29c0d7d4d4SBill Paul  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30c0d7d4d4SBill Paul  * THE POSSIBILITY OF SUCH DAMAGE.
31c0d7d4d4SBill Paul  */
32c0d7d4d4SBill Paul 
33aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
34aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
35aad970f1SDavid E. O'Brien 
36c0d7d4d4SBill Paul /*
37c0d7d4d4SBill Paul  * driver for the XaQti XMAC II's internal PHY. This is sort of
38c0d7d4d4SBill Paul  * like a 10/100 PHY, except the only thing we're really autoselecting
39c0d7d4d4SBill Paul  * here is full/half duplex. Speed is always 1000mbps.
40c0d7d4d4SBill Paul  */
41c0d7d4d4SBill Paul 
42c0d7d4d4SBill Paul #include <sys/param.h>
43c0d7d4d4SBill Paul #include <sys/systm.h>
44c0d7d4d4SBill Paul #include <sys/kernel.h>
4541ee9f1cSPoul-Henning Kamp #include <sys/module.h>
46c0d7d4d4SBill Paul #include <sys/socket.h>
47c0d7d4d4SBill Paul #include <sys/bus.h>
48c0d7d4d4SBill Paul 
49c0d7d4d4SBill Paul #include <net/if.h>
50c0d7d4d4SBill Paul #include <net/if_media.h>
51c0d7d4d4SBill Paul 
52c0d7d4d4SBill Paul #include <dev/mii/mii.h>
53c0d7d4d4SBill Paul #include <dev/mii/miivar.h>
542d3ce713SDavid E. O'Brien #include "miidevs.h"
55c0d7d4d4SBill Paul 
56c0d7d4d4SBill Paul #include <dev/mii/xmphyreg.h>
57c0d7d4d4SBill Paul 
58c0d7d4d4SBill Paul #include "miibus_if.h"
59c0d7d4d4SBill Paul 
60e51a25f8SAlfred Perlstein static int xmphy_probe(device_t);
61e51a25f8SAlfred Perlstein static int xmphy_attach(device_t);
62c0d7d4d4SBill Paul 
63c0d7d4d4SBill Paul static device_method_t xmphy_methods[] = {
64c0d7d4d4SBill Paul 	/* device interface */
65c0d7d4d4SBill Paul 	DEVMETHOD(device_probe,		xmphy_probe),
66c0d7d4d4SBill Paul 	DEVMETHOD(device_attach,	xmphy_attach),
67279fe8d1SPoul-Henning Kamp 	DEVMETHOD(device_detach,	mii_phy_detach),
68c0d7d4d4SBill Paul 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
69c0d7d4d4SBill Paul 	{ 0, 0 }
70c0d7d4d4SBill Paul };
71c0d7d4d4SBill Paul 
72c0d7d4d4SBill Paul static devclass_t xmphy_devclass;
73c0d7d4d4SBill Paul 
74c0d7d4d4SBill Paul static driver_t xmphy_driver = {
75c0d7d4d4SBill Paul 	"xmphy",
76c0d7d4d4SBill Paul 	xmphy_methods,
77c0d7d4d4SBill Paul 	sizeof(struct mii_softc)
78c0d7d4d4SBill Paul };
79c0d7d4d4SBill Paul 
80c0d7d4d4SBill Paul DRIVER_MODULE(xmphy, miibus, xmphy_driver, xmphy_devclass, 0, 0);
81c0d7d4d4SBill Paul 
82e51a25f8SAlfred Perlstein static int	xmphy_service(struct mii_softc *, struct mii_data *, int);
83e51a25f8SAlfred Perlstein static void	xmphy_status(struct mii_softc *);
84fd94424cSPoul-Henning Kamp static int	xmphy_mii_phy_auto(struct mii_softc *);
85c0d7d4d4SBill Paul 
86a35b9333SMarius Strobl static const struct mii_phydesc xmphys[] = {
87a35b9333SMarius Strobl 	{ MII_OUI_xxXAQTI, MII_MODEL_XAQTI_XMACII, MII_STR_XAQTI_XMACII },
88a35b9333SMarius Strobl 	MII_PHY_DESC(JATO, BASEX),
89a35b9333SMarius Strobl 	MII_PHY_END
90a35b9333SMarius Strobl };
91a35b9333SMarius Strobl 
929c1c2e99SAlfred Perlstein static int
937d830ac9SWarner Losh xmphy_probe(device_t dev)
94c0d7d4d4SBill Paul {
95c0d7d4d4SBill Paul 
96a35b9333SMarius Strobl 	return (mii_phy_dev_probe(dev, xmphys, BUS_PROBE_DEFAULT));
97134c58d3SBill Paul }
98134c58d3SBill Paul 
999c1c2e99SAlfred Perlstein static int
1007d830ac9SWarner Losh xmphy_attach(device_t dev)
101c0d7d4d4SBill Paul {
102c0d7d4d4SBill Paul 	struct mii_softc *sc;
103c0d7d4d4SBill Paul 	struct mii_attach_args *ma;
104c0d7d4d4SBill Paul 	struct mii_data *mii;
105c0d7d4d4SBill Paul 	const char *sep = "";
106c0d7d4d4SBill Paul 
107c0d7d4d4SBill Paul 	sc = device_get_softc(dev);
108c0d7d4d4SBill Paul 	ma = device_get_ivars(dev);
109c0d7d4d4SBill Paul 	sc->mii_dev = device_get_parent(dev);
1107b9a15f7SMarius Strobl 	mii = ma->mii_data;
111c0d7d4d4SBill Paul 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
112c0d7d4d4SBill Paul 
113de1add1eSMarius Strobl 	sc->mii_inst = mii->mii_instance++;
114c0d7d4d4SBill Paul 	sc->mii_phy = ma->mii_phyno;
115c0d7d4d4SBill Paul 	sc->mii_service = xmphy_service;
116c0d7d4d4SBill Paul 	sc->mii_pdata = mii;
117c0d7d4d4SBill Paul 
118c0d7d4d4SBill Paul 	sc->mii_flags |= MIIF_NOISOLATE;
119de1add1eSMarius Strobl 	sc->mii_anegticks = MII_ANEGTICKS;
120de1add1eSMarius Strobl 
121de1add1eSMarius Strobl 	mii_phy_reset(sc);
122c0d7d4d4SBill Paul 
123c0d7d4d4SBill Paul #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
124c0d7d4d4SBill Paul #define PRINT(s)	printf("%s%s", sep, s); sep = ", "
125c0d7d4d4SBill Paul 
126c0d7d4d4SBill Paul 	device_printf(dev, " ");
127c0d7d4d4SBill Paul 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst),
128c0d7d4d4SBill Paul 	    XMPHY_BMCR_FDX);
129c0d7d4d4SBill Paul 	PRINT("1000baseSX");
130c0d7d4d4SBill Paul 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 0);
131c0d7d4d4SBill Paul 	PRINT("1000baseSX-FDX");
132c0d7d4d4SBill Paul 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
133c0d7d4d4SBill Paul 	PRINT("auto");
134c0d7d4d4SBill Paul 
135c0d7d4d4SBill Paul 	printf("\n");
136c0d7d4d4SBill Paul #undef ADD
137c0d7d4d4SBill Paul #undef PRINT
138c0d7d4d4SBill Paul 
139c0d7d4d4SBill Paul 	MIIBUS_MEDIAINIT(sc->mii_dev);
140c0d7d4d4SBill Paul 	return (0);
141c0d7d4d4SBill Paul }
142c0d7d4d4SBill Paul 
143d9730b8bSJonathan Lemon static int
1447d830ac9SWarner Losh xmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
145c0d7d4d4SBill Paul {
146c0d7d4d4SBill Paul 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
147c0d7d4d4SBill Paul 	int reg;
148c0d7d4d4SBill Paul 
149c0d7d4d4SBill Paul 	switch (cmd) {
150c0d7d4d4SBill Paul 	case MII_POLLSTAT:
151c0d7d4d4SBill Paul 		break;
152c0d7d4d4SBill Paul 
153c0d7d4d4SBill Paul 	case MII_MEDIACHG:
154c0d7d4d4SBill Paul 		/*
155c0d7d4d4SBill Paul 		 * If the interface is not up, don't do anything.
156c0d7d4d4SBill Paul 		 */
157c0d7d4d4SBill Paul 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
158c0d7d4d4SBill Paul 			break;
159c0d7d4d4SBill Paul 
160c0d7d4d4SBill Paul 		switch (IFM_SUBTYPE(ife->ifm_media)) {
161c0d7d4d4SBill Paul 		case IFM_AUTO:
162134c58d3SBill Paul #ifdef foo
163c0d7d4d4SBill Paul 			/*
164c0d7d4d4SBill Paul 			 * If we're already in auto mode, just return.
165c0d7d4d4SBill Paul 			 */
166c0d7d4d4SBill Paul 			if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN)
167c0d7d4d4SBill Paul 				return (0);
168134c58d3SBill Paul #endif
169fd94424cSPoul-Henning Kamp 			(void) xmphy_mii_phy_auto(sc);
170c0d7d4d4SBill Paul 			break;
171c0d7d4d4SBill Paul 		case IFM_1000_SX:
172c0d7d4d4SBill Paul 			mii_phy_reset(sc);
173c0d7d4d4SBill Paul 			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
174c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX);
175c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX);
176c0d7d4d4SBill Paul 			} else {
177c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX);
178c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_BMCR, 0);
179c0d7d4d4SBill Paul 			}
180c0d7d4d4SBill Paul 			break;
181c0d7d4d4SBill Paul 		case IFM_100_T4:
182c0d7d4d4SBill Paul 		case IFM_100_TX:
183c0d7d4d4SBill Paul 		case IFM_10_T:
184c0d7d4d4SBill Paul 		default:
185c0d7d4d4SBill Paul 			return (EINVAL);
186c0d7d4d4SBill Paul 		}
187c0d7d4d4SBill Paul 		break;
188c0d7d4d4SBill Paul 
189c0d7d4d4SBill Paul 	case MII_TICK:
190c0d7d4d4SBill Paul 		/*
191c0d7d4d4SBill Paul 		 * Is the interface even up?
192c0d7d4d4SBill Paul 		 */
193c0d7d4d4SBill Paul 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
194c0d7d4d4SBill Paul 			return (0);
195c0d7d4d4SBill Paul 
196c0d7d4d4SBill Paul 		/*
197d9730b8bSJonathan Lemon 		 * Only used for autonegotiation.
198d9730b8bSJonathan Lemon 		 */
199d9730b8bSJonathan Lemon 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
200d9730b8bSJonathan Lemon 			break;
201d9730b8bSJonathan Lemon 
202d9730b8bSJonathan Lemon 		/*
203d9730b8bSJonathan Lemon 		 * Check to see if we have link.  If we do, we don't
204d9730b8bSJonathan Lemon 		 * need to restart the autonegotiation process.  Read
205d9730b8bSJonathan Lemon 		 * the BMSR twice in case it's latched.
206d9730b8bSJonathan Lemon 		 */
207d9730b8bSJonathan Lemon 		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
208d9730b8bSJonathan Lemon 		if (reg & BMSR_LINK)
209d9730b8bSJonathan Lemon 			break;
210d9730b8bSJonathan Lemon 
211de1add1eSMarius Strobl 		/* Only retry autonegotiation every mii_anegticks seconds. */
212de1add1eSMarius Strobl 		if (sc->mii_ticks <= sc->mii_anegticks)
2139a54cbb9SAndre Oppermann 			break;
214c0d7d4d4SBill Paul 
215c0d7d4d4SBill Paul 		sc->mii_ticks = 0;
216c0d7d4d4SBill Paul 
217c0d7d4d4SBill Paul 		mii_phy_reset(sc);
218fd94424cSPoul-Henning Kamp 		xmphy_mii_phy_auto(sc);
219c0d7d4d4SBill Paul 		return (0);
220c0d7d4d4SBill Paul 	}
221c0d7d4d4SBill Paul 
222c0d7d4d4SBill Paul 	/* Update the media status. */
223c0d7d4d4SBill Paul 	xmphy_status(sc);
224c0d7d4d4SBill Paul 
225c0d7d4d4SBill Paul 	/* Callback if something changed. */
226d9730b8bSJonathan Lemon 	mii_phy_update(sc, cmd);
227c0d7d4d4SBill Paul 	return (0);
228c0d7d4d4SBill Paul }
229c0d7d4d4SBill Paul 
230d9730b8bSJonathan Lemon static void
2317d830ac9SWarner Losh xmphy_status(struct mii_softc *sc)
232c0d7d4d4SBill Paul {
233c0d7d4d4SBill Paul 	struct mii_data *mii = sc->mii_pdata;
234c0d7d4d4SBill Paul 	int bmsr, bmcr, anlpar;
235c0d7d4d4SBill Paul 
236c0d7d4d4SBill Paul 	mii->mii_media_status = IFM_AVALID;
237c0d7d4d4SBill Paul 	mii->mii_media_active = IFM_ETHER;
238c0d7d4d4SBill Paul 
239c0d7d4d4SBill Paul 	bmsr = PHY_READ(sc, XMPHY_MII_BMSR) |
240c0d7d4d4SBill Paul 	    PHY_READ(sc, XMPHY_MII_BMSR);
241c0d7d4d4SBill Paul 	if (bmsr & XMPHY_BMSR_LINK)
242c0d7d4d4SBill Paul 		mii->mii_media_status |= IFM_ACTIVE;
243c0d7d4d4SBill Paul 
244c0d7d4d4SBill Paul 	/* Do dummy read of extended status register. */
245c0d7d4d4SBill Paul 	bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS);
246c0d7d4d4SBill Paul 
247c0d7d4d4SBill Paul 	bmcr = PHY_READ(sc, XMPHY_MII_BMCR);
248c0d7d4d4SBill Paul 
249c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_LOOP)
250c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_LOOP;
251c0d7d4d4SBill Paul 
252c0d7d4d4SBill Paul 
253c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_AUTOEN) {
254c0d7d4d4SBill Paul 		if ((bmsr & XMPHY_BMSR_ACOMP) == 0) {
255c0d7d4d4SBill Paul 			if (bmsr & XMPHY_BMSR_LINK) {
256c0d7d4d4SBill Paul 				mii->mii_media_active |= IFM_1000_SX|IFM_HDX;
257c0d7d4d4SBill Paul 				return;
258c0d7d4d4SBill Paul 			}
259c0d7d4d4SBill Paul 			/* Erg, still trying, I guess... */
260c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_NONE;
261c0d7d4d4SBill Paul 			return;
262c0d7d4d4SBill Paul 		}
263c0d7d4d4SBill Paul 
264c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_1000_SX;
265c0d7d4d4SBill Paul 		anlpar = PHY_READ(sc, XMPHY_MII_ANAR) &
266c0d7d4d4SBill Paul 		    PHY_READ(sc, XMPHY_MII_ANLPAR);
267c0d7d4d4SBill Paul 		if (anlpar & XMPHY_ANLPAR_FDX)
268c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_FDX;
269c0d7d4d4SBill Paul 		else
270c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_HDX;
271c0d7d4d4SBill Paul 		return;
272c0d7d4d4SBill Paul 	}
273c0d7d4d4SBill Paul 
274c0d7d4d4SBill Paul 	mii->mii_media_active |= IFM_1000_SX;
275c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_FDX)
276c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_FDX;
277c0d7d4d4SBill Paul 	else
278c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_HDX;
279c0d7d4d4SBill Paul }
280c0d7d4d4SBill Paul 
281c0d7d4d4SBill Paul static int
2827d830ac9SWarner Losh xmphy_mii_phy_auto(struct mii_softc *mii)
283c0d7d4d4SBill Paul {
284fd94424cSPoul-Henning Kamp 	int anar = 0;
285c0d7d4d4SBill Paul 
286134c58d3SBill Paul 	anar = PHY_READ(mii, XMPHY_MII_ANAR);
287134c58d3SBill Paul 	anar |= XMPHY_ANAR_FDX|XMPHY_ANAR_HDX;
288134c58d3SBill Paul 	PHY_WRITE(mii, XMPHY_MII_ANAR, anar);
289134c58d3SBill Paul 	DELAY(1000);
290c0d7d4d4SBill Paul 	PHY_WRITE(mii, XMPHY_MII_BMCR,
291c0d7d4d4SBill Paul 	    XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG);
292c0d7d4d4SBill Paul 
293c0d7d4d4SBill Paul 	return (EJUSTRETURN);
294c0d7d4d4SBill Paul }
295