xref: /freebsd/sys/dev/mii/xmphy.c (revision 685dc743)
1098ca2bdSWarner Losh /*-
2df57947fSPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni  *
4c0d7d4d4SBill Paul  * Copyright (c) 2000
5c0d7d4d4SBill Paul  *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
6c0d7d4d4SBill Paul  *
7c0d7d4d4SBill Paul  * Redistribution and use in source and binary forms, with or without
8c0d7d4d4SBill Paul  * modification, are permitted provided that the following conditions
9c0d7d4d4SBill Paul  * are met:
10c0d7d4d4SBill Paul  * 1. Redistributions of source code must retain the above copyright
11c0d7d4d4SBill Paul  *    notice, this list of conditions and the following disclaimer.
12c0d7d4d4SBill Paul  * 2. Redistributions in binary form must reproduce the above copyright
13c0d7d4d4SBill Paul  *    notice, this list of conditions and the following disclaimer in the
14c0d7d4d4SBill Paul  *    documentation and/or other materials provided with the distribution.
15c0d7d4d4SBill Paul  * 3. All advertising materials mentioning features or use of this software
16c0d7d4d4SBill Paul  *    must display the following acknowledgement:
17c0d7d4d4SBill Paul  *	This product includes software developed by Bill Paul.
18c0d7d4d4SBill Paul  * 4. Neither the name of the author nor the names of any co-contributors
19c0d7d4d4SBill Paul  *    may be used to endorse or promote products derived from this software
20c0d7d4d4SBill Paul  *    without specific prior written permission.
21c0d7d4d4SBill Paul  *
22c0d7d4d4SBill Paul  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23c0d7d4d4SBill Paul  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24c0d7d4d4SBill Paul  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25c0d7d4d4SBill Paul  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26c0d7d4d4SBill Paul  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27c0d7d4d4SBill Paul  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28c0d7d4d4SBill Paul  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29c0d7d4d4SBill Paul  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30c0d7d4d4SBill Paul  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31c0d7d4d4SBill Paul  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32c0d7d4d4SBill Paul  * THE POSSIBILITY OF SUCH DAMAGE.
33c0d7d4d4SBill Paul  */
34c0d7d4d4SBill Paul 
35aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
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),
69604f5f1fSMarius Strobl 	DEVMETHOD_END
70c0d7d4d4SBill Paul };
71c0d7d4d4SBill Paul 
72c0d7d4d4SBill Paul static driver_t xmphy_driver = {
73c0d7d4d4SBill Paul 	"xmphy",
74c0d7d4d4SBill Paul 	xmphy_methods,
75c0d7d4d4SBill Paul 	sizeof(struct mii_softc)
76c0d7d4d4SBill Paul };
77c0d7d4d4SBill Paul 
78f438c2ffSJohn Baldwin DRIVER_MODULE(xmphy, miibus, xmphy_driver, 0, 0);
79c0d7d4d4SBill Paul 
80e51a25f8SAlfred Perlstein static int	xmphy_service(struct mii_softc *, struct mii_data *, int);
81e51a25f8SAlfred Perlstein static void	xmphy_status(struct mii_softc *);
82fd94424cSPoul-Henning Kamp static int	xmphy_mii_phy_auto(struct mii_softc *);
83c0d7d4d4SBill Paul 
84a35b9333SMarius Strobl static const struct mii_phydesc xmphys[] = {
853fcb7a53SMarius Strobl 	MII_PHY_DESC(xxJATO, BASEX),
863fcb7a53SMarius Strobl 	MII_PHY_DESC(xxXAQTI, XMACII),
87a35b9333SMarius Strobl 	MII_PHY_END
88a35b9333SMarius Strobl };
89a35b9333SMarius Strobl 
903fcb7a53SMarius Strobl static const struct mii_phy_funcs xmphy_funcs = {
913fcb7a53SMarius Strobl 	xmphy_service,
923fcb7a53SMarius Strobl 	xmphy_status,
933fcb7a53SMarius Strobl 	mii_phy_reset
943fcb7a53SMarius Strobl };
953fcb7a53SMarius Strobl 
969c1c2e99SAlfred Perlstein static int
xmphy_probe(device_t dev)977d830ac9SWarner Losh xmphy_probe(device_t dev)
98c0d7d4d4SBill Paul {
99c0d7d4d4SBill Paul 
100a35b9333SMarius Strobl 	return (mii_phy_dev_probe(dev, xmphys, BUS_PROBE_DEFAULT));
101134c58d3SBill Paul }
102134c58d3SBill Paul 
1039c1c2e99SAlfred Perlstein static int
xmphy_attach(device_t dev)1047d830ac9SWarner Losh xmphy_attach(device_t dev)
105c0d7d4d4SBill Paul {
106c0d7d4d4SBill Paul 	struct mii_softc *sc;
107c0d7d4d4SBill Paul 	const char *sep = "";
108c0d7d4d4SBill Paul 
109c0d7d4d4SBill Paul 	sc = device_get_softc(dev);
110c0d7d4d4SBill Paul 
1113fcb7a53SMarius Strobl 	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
1123fcb7a53SMarius Strobl 	    &xmphy_funcs, 0);
113de1add1eSMarius Strobl 	sc->mii_anegticks = MII_ANEGTICKS;
114de1add1eSMarius Strobl 
1153fcb7a53SMarius Strobl 	PHY_RESET(sc);
116c0d7d4d4SBill Paul 
1172eba9bbeSGleb Smirnoff #define	ADD(m)		ifmedia_add(&sc->mii_pdata->mii_media, (m), 0, NULL)
118c0d7d4d4SBill Paul #define PRINT(s)	printf("%s%s", sep, s); sep = ", "
119c0d7d4d4SBill Paul 
120c0d7d4d4SBill Paul 	device_printf(dev, " ");
1212eba9bbeSGleb Smirnoff 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst));
122c0d7d4d4SBill Paul 	PRINT("1000baseSX");
1232eba9bbeSGleb Smirnoff 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst));
124c0d7d4d4SBill Paul 	PRINT("1000baseSX-FDX");
1252eba9bbeSGleb Smirnoff 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst));
126c0d7d4d4SBill Paul 	PRINT("auto");
127c0d7d4d4SBill Paul 
128c0d7d4d4SBill Paul 	printf("\n");
1293fcb7a53SMarius Strobl 
130c0d7d4d4SBill Paul #undef ADD
131c0d7d4d4SBill Paul #undef PRINT
132c0d7d4d4SBill Paul 
133c0d7d4d4SBill Paul 	MIIBUS_MEDIAINIT(sc->mii_dev);
134c0d7d4d4SBill Paul 	return (0);
135c0d7d4d4SBill Paul }
136c0d7d4d4SBill Paul 
137d9730b8bSJonathan Lemon static int
xmphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)1387d830ac9SWarner Losh xmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
139c0d7d4d4SBill Paul {
140c0d7d4d4SBill Paul 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
141c0d7d4d4SBill Paul 	int reg;
142c0d7d4d4SBill Paul 
143c0d7d4d4SBill Paul 	switch (cmd) {
144c0d7d4d4SBill Paul 	case MII_POLLSTAT:
145c0d7d4d4SBill Paul 		break;
146c0d7d4d4SBill Paul 
147c0d7d4d4SBill Paul 	case MII_MEDIACHG:
148c0d7d4d4SBill Paul 		switch (IFM_SUBTYPE(ife->ifm_media)) {
149c0d7d4d4SBill Paul 		case IFM_AUTO:
150134c58d3SBill Paul #ifdef foo
151c0d7d4d4SBill Paul 			/*
152c0d7d4d4SBill Paul 			 * If we're already in auto mode, just return.
153c0d7d4d4SBill Paul 			 */
154c0d7d4d4SBill Paul 			if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN)
155c0d7d4d4SBill Paul 				return (0);
156134c58d3SBill Paul #endif
157fd94424cSPoul-Henning Kamp 			(void)xmphy_mii_phy_auto(sc);
158c0d7d4d4SBill Paul 			break;
159c0d7d4d4SBill Paul 		case IFM_1000_SX:
1603fcb7a53SMarius Strobl 			PHY_RESET(sc);
16187a303dcSMarius Strobl 			if ((ife->ifm_media & IFM_FDX) != 0) {
162c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX);
163c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX);
164c0d7d4d4SBill Paul 			} else {
165c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX);
166c0d7d4d4SBill Paul 				PHY_WRITE(sc, XMPHY_MII_BMCR, 0);
167c0d7d4d4SBill Paul 			}
168c0d7d4d4SBill Paul 			break;
169c0d7d4d4SBill Paul 		default:
170c0d7d4d4SBill Paul 			return (EINVAL);
171c0d7d4d4SBill Paul 		}
172c0d7d4d4SBill Paul 		break;
173c0d7d4d4SBill Paul 
174c0d7d4d4SBill Paul 	case MII_TICK:
175c0d7d4d4SBill Paul 		/*
176d9730b8bSJonathan Lemon 		 * Only used for autonegotiation.
177d9730b8bSJonathan Lemon 		 */
178d9730b8bSJonathan Lemon 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
179d9730b8bSJonathan Lemon 			break;
180d9730b8bSJonathan Lemon 
181d9730b8bSJonathan Lemon 		/*
182d9730b8bSJonathan Lemon 		 * Check to see if we have link.  If we do, we don't
183d9730b8bSJonathan Lemon 		 * need to restart the autonegotiation process.  Read
184d9730b8bSJonathan Lemon 		 * the BMSR twice in case it's latched.
185d9730b8bSJonathan Lemon 		 */
186d9730b8bSJonathan Lemon 		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
187d9730b8bSJonathan Lemon 		if (reg & BMSR_LINK)
188d9730b8bSJonathan Lemon 			break;
189d9730b8bSJonathan Lemon 
190de1add1eSMarius Strobl 		/* Only retry autonegotiation every mii_anegticks seconds. */
191de1add1eSMarius Strobl 		if (sc->mii_ticks <= sc->mii_anegticks)
1929a54cbb9SAndre Oppermann 			break;
193c0d7d4d4SBill Paul 
194c0d7d4d4SBill Paul 		sc->mii_ticks = 0;
195c0d7d4d4SBill Paul 
1963fcb7a53SMarius Strobl 		PHY_RESET(sc);
197fd94424cSPoul-Henning Kamp 		xmphy_mii_phy_auto(sc);
198c0d7d4d4SBill Paul 		return (0);
199c0d7d4d4SBill Paul 	}
200c0d7d4d4SBill Paul 
201c0d7d4d4SBill Paul 	/* Update the media status. */
202c0d7d4d4SBill Paul 	xmphy_status(sc);
203c0d7d4d4SBill Paul 
204c0d7d4d4SBill Paul 	/* Callback if something changed. */
205d9730b8bSJonathan Lemon 	mii_phy_update(sc, cmd);
206c0d7d4d4SBill Paul 	return (0);
207c0d7d4d4SBill Paul }
208c0d7d4d4SBill Paul 
209d9730b8bSJonathan Lemon static void
xmphy_status(struct mii_softc * sc)2107d830ac9SWarner Losh xmphy_status(struct mii_softc *sc)
211c0d7d4d4SBill Paul {
212c0d7d4d4SBill Paul 	struct mii_data *mii = sc->mii_pdata;
213c0d7d4d4SBill Paul 	int bmsr, bmcr, anlpar;
214c0d7d4d4SBill Paul 
215c0d7d4d4SBill Paul 	mii->mii_media_status = IFM_AVALID;
216c0d7d4d4SBill Paul 	mii->mii_media_active = IFM_ETHER;
217c0d7d4d4SBill Paul 
218c0d7d4d4SBill Paul 	bmsr = PHY_READ(sc, XMPHY_MII_BMSR) |
219c0d7d4d4SBill Paul 	    PHY_READ(sc, XMPHY_MII_BMSR);
220c0d7d4d4SBill Paul 	if (bmsr & XMPHY_BMSR_LINK)
221c0d7d4d4SBill Paul 		mii->mii_media_status |= IFM_ACTIVE;
222c0d7d4d4SBill Paul 
223c0d7d4d4SBill Paul 	/* Do dummy read of extended status register. */
224c0d7d4d4SBill Paul 	bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS);
225c0d7d4d4SBill Paul 
226c0d7d4d4SBill Paul 	bmcr = PHY_READ(sc, XMPHY_MII_BMCR);
227c0d7d4d4SBill Paul 
228c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_LOOP)
229c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_LOOP;
230c0d7d4d4SBill Paul 
231c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_AUTOEN) {
232c0d7d4d4SBill Paul 		if ((bmsr & XMPHY_BMSR_ACOMP) == 0) {
233c0d7d4d4SBill Paul 			if (bmsr & XMPHY_BMSR_LINK) {
234c0d7d4d4SBill Paul 				mii->mii_media_active |= IFM_1000_SX|IFM_HDX;
235c0d7d4d4SBill Paul 				return;
236c0d7d4d4SBill Paul 			}
237c0d7d4d4SBill Paul 			/* Erg, still trying, I guess... */
238c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_NONE;
239c0d7d4d4SBill Paul 			return;
240c0d7d4d4SBill Paul 		}
241c0d7d4d4SBill Paul 
242c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_1000_SX;
243c0d7d4d4SBill Paul 		anlpar = PHY_READ(sc, XMPHY_MII_ANAR) &
244c0d7d4d4SBill Paul 		    PHY_READ(sc, XMPHY_MII_ANLPAR);
245c0d7d4d4SBill Paul 		if (anlpar & XMPHY_ANLPAR_FDX)
246c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_FDX;
247c0d7d4d4SBill Paul 		else
248c0d7d4d4SBill Paul 			mii->mii_media_active |= IFM_HDX;
249c0d7d4d4SBill Paul 		return;
250c0d7d4d4SBill Paul 	}
251c0d7d4d4SBill Paul 
252c0d7d4d4SBill Paul 	mii->mii_media_active |= IFM_1000_SX;
253c0d7d4d4SBill Paul 	if (bmcr & XMPHY_BMCR_FDX)
254c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_FDX;
255c0d7d4d4SBill Paul 	else
256c0d7d4d4SBill Paul 		mii->mii_media_active |= IFM_HDX;
257c0d7d4d4SBill Paul }
258c0d7d4d4SBill Paul 
259c0d7d4d4SBill Paul static int
xmphy_mii_phy_auto(struct mii_softc * mii)2607d830ac9SWarner Losh xmphy_mii_phy_auto(struct mii_softc *mii)
261c0d7d4d4SBill Paul {
262fd94424cSPoul-Henning Kamp 	int anar = 0;
263c0d7d4d4SBill Paul 
264134c58d3SBill Paul 	anar = PHY_READ(mii, XMPHY_MII_ANAR);
265134c58d3SBill Paul 	anar |= XMPHY_ANAR_FDX|XMPHY_ANAR_HDX;
266134c58d3SBill Paul 	PHY_WRITE(mii, XMPHY_MII_ANAR, anar);
267134c58d3SBill Paul 	DELAY(1000);
268c0d7d4d4SBill Paul 	PHY_WRITE(mii, XMPHY_MII_BMCR,
269c0d7d4d4SBill Paul 	    XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG);
270c0d7d4d4SBill Paul 
271c0d7d4d4SBill Paul 	return (EJUSTRETURN);
272c0d7d4d4SBill Paul }
273