xref: /freebsd/sys/dev/mii/rlphy.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1098ca2bdSWarner Losh /*-
2df57947fSPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni  *
4a4f02d20SBill Paul  * Copyright (c) 1997, 1998, 1999
5a4f02d20SBill Paul  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
6a4f02d20SBill Paul  *
7a4f02d20SBill Paul  * Redistribution and use in source and binary forms, with or without
8a4f02d20SBill Paul  * modification, are permitted provided that the following conditions
9a4f02d20SBill Paul  * are met:
10a4f02d20SBill Paul  * 1. Redistributions of source code must retain the above copyright
11a4f02d20SBill Paul  *    notice, this list of conditions and the following disclaimer.
12a4f02d20SBill Paul  * 2. Redistributions in binary form must reproduce the above copyright
13a4f02d20SBill Paul  *    notice, this list of conditions and the following disclaimer in the
14a4f02d20SBill Paul  *    documentation and/or other materials provided with the distribution.
15a4f02d20SBill Paul  * 3. All advertising materials mentioning features or use of this software
16a4f02d20SBill Paul  *    must display the following acknowledgement:
17a4f02d20SBill Paul  *	This product includes software developed by Bill Paul.
18a4f02d20SBill Paul  * 4. Neither the name of the author nor the names of any co-contributors
19a4f02d20SBill Paul  *    may be used to endorse or promote products derived from this software
20a4f02d20SBill Paul  *    without specific prior written permission.
21a4f02d20SBill Paul  *
22a4f02d20SBill Paul  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23a4f02d20SBill Paul  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24a4f02d20SBill Paul  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25a4f02d20SBill Paul  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26a4f02d20SBill Paul  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27a4f02d20SBill Paul  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28a4f02d20SBill Paul  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29a4f02d20SBill Paul  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30a4f02d20SBill Paul  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31a4f02d20SBill Paul  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32a4f02d20SBill Paul  * THE POSSIBILITY OF SUCH DAMAGE.
33a4f02d20SBill Paul  */
34a4f02d20SBill Paul 
35aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
36a4f02d20SBill Paul /*
37a4f02d20SBill Paul  * driver for RealTek 8139 internal PHYs
38a4f02d20SBill Paul  */
39a4f02d20SBill Paul 
40a4f02d20SBill Paul #include <sys/param.h>
41a4f02d20SBill Paul #include <sys/systm.h>
42a4f02d20SBill Paul #include <sys/kernel.h>
4341ee9f1cSPoul-Henning Kamp #include <sys/module.h>
44a4f02d20SBill Paul #include <sys/socket.h>
45a4f02d20SBill Paul #include <sys/bus.h>
4661a3ac6eSGleb Smirnoff #include <sys/taskqueue.h>	/* XXXGL: if_rlreg.h contamination */
47a4f02d20SBill Paul 
48a4f02d20SBill Paul #include <net/if.h>
49a72c060fSBill Paul #include <net/if_arp.h>
50a4f02d20SBill Paul #include <net/if_media.h>
51a4f02d20SBill Paul 
52a4f02d20SBill Paul #include <dev/mii/mii.h>
53a4f02d20SBill Paul #include <dev/mii/miivar.h>
542d3ce713SDavid E. O'Brien #include "miidevs.h"
55a72c060fSBill Paul 
56a72c060fSBill Paul #include <machine/bus.h>
57b2d3d26fSGleb Smirnoff #include <dev/rl/if_rlreg.h>
58a4f02d20SBill Paul 
59a4f02d20SBill Paul #include "miibus_if.h"
60a4f02d20SBill Paul 
61e51a25f8SAlfred Perlstein static int rlphy_probe(device_t);
62e51a25f8SAlfred Perlstein static int rlphy_attach(device_t);
63a4f02d20SBill Paul 
64a4f02d20SBill Paul static device_method_t rlphy_methods[] = {
65a4f02d20SBill Paul 	/* device interface */
66a4f02d20SBill Paul 	DEVMETHOD(device_probe,		rlphy_probe),
67a4f02d20SBill Paul 	DEVMETHOD(device_attach,	rlphy_attach),
68279fe8d1SPoul-Henning Kamp 	DEVMETHOD(device_detach,	mii_phy_detach),
69a4f02d20SBill Paul 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
70604f5f1fSMarius Strobl 	DEVMETHOD_END
71a4f02d20SBill Paul };
72a4f02d20SBill Paul 
73a4f02d20SBill Paul static driver_t rlphy_driver = {
74a4f02d20SBill Paul 	"rlphy",
75a4f02d20SBill Paul 	rlphy_methods,
763fcb7a53SMarius Strobl 	sizeof(struct mii_softc)
77a4f02d20SBill Paul };
78a4f02d20SBill Paul 
79*f438c2ffSJohn Baldwin DRIVER_MODULE(rlphy, miibus, rlphy_driver, 0, 0);
80a4f02d20SBill Paul 
81e51a25f8SAlfred Perlstein static int	rlphy_service(struct mii_softc *, struct mii_data *, int);
82a72c060fSBill Paul static void	rlphy_status(struct mii_softc *);
83a4f02d20SBill Paul 
842e0b4663SMarius Strobl /*
852e0b4663SMarius Strobl  * RealTek internal PHYs don't have vendor/device ID registers;
862e0b4663SMarius Strobl  * re(4) and rl(4) fake up a return value of all zeros.
872e0b4663SMarius Strobl  */
882e0b4663SMarius Strobl static const struct mii_phydesc rlintphys[] = {
892e0b4663SMarius Strobl 	{ 0, 0, "RealTek internal media interface" },
902e0b4663SMarius Strobl 	MII_PHY_END
912e0b4663SMarius Strobl };
922e0b4663SMarius Strobl 
93a35b9333SMarius Strobl static const struct mii_phydesc rlphys[] = {
943fcb7a53SMarius Strobl 	MII_PHY_DESC(yyREALTEK, RTL8201L),
953fcb7a53SMarius Strobl 	MII_PHY_DESC(REALTEK, RTL8201E),
963fcb7a53SMarius Strobl 	MII_PHY_DESC(xxICPLUS, IP101),
97a35b9333SMarius Strobl 	MII_PHY_END
98a35b9333SMarius Strobl };
99a35b9333SMarius Strobl 
1003fcb7a53SMarius Strobl static const struct mii_phy_funcs rlphy_funcs = {
1013fcb7a53SMarius Strobl 	rlphy_service,
1023fcb7a53SMarius Strobl 	rlphy_status,
1033fcb7a53SMarius Strobl 	mii_phy_reset
1043fcb7a53SMarius Strobl };
1053fcb7a53SMarius Strobl 
1069c1c2e99SAlfred Perlstein static int
rlphy_probe(device_t dev)1077d830ac9SWarner Losh rlphy_probe(device_t dev)
108a4f02d20SBill Paul {
109a35b9333SMarius Strobl 	int rv;
110a4f02d20SBill Paul 
111a35b9333SMarius Strobl 	rv = mii_phy_dev_probe(dev, rlphys, BUS_PROBE_DEFAULT);
112a35b9333SMarius Strobl 	if (rv <= 0)
113a35b9333SMarius Strobl 		return (rv);
114a72c060fSBill Paul 
1157e310d2dSGleb Smirnoff 	if (mii_dev_mac_match(dev, "rl") || mii_dev_mac_match(dev, "re"))
1162e0b4663SMarius Strobl 		return (mii_phy_dev_probe(dev, rlintphys, BUS_PROBE_DEFAULT));
117a4f02d20SBill Paul 	return (ENXIO);
118a4f02d20SBill Paul }
119a4f02d20SBill Paul 
1209c1c2e99SAlfred Perlstein static int
rlphy_attach(device_t dev)1217d830ac9SWarner Losh rlphy_attach(device_t dev)
122a4f02d20SBill Paul {
123a4f02d20SBill Paul 
124de1add1eSMarius Strobl 	/*
125de1add1eSMarius Strobl 	 * The RealTek PHY can never be isolated.
126de1add1eSMarius Strobl 	 */
1273fcb7a53SMarius Strobl 	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
1283fcb7a53SMarius Strobl 	    &rlphy_funcs, 1);
129a4f02d20SBill Paul 	return (0);
130a4f02d20SBill Paul }
131a4f02d20SBill Paul 
132d9730b8bSJonathan Lemon static int
rlphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)1337d830ac9SWarner Losh rlphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
134a4f02d20SBill Paul {
135a4f02d20SBill Paul 
136a4f02d20SBill Paul 	switch (cmd) {
137a4f02d20SBill Paul 	case MII_POLLSTAT:
138a4f02d20SBill Paul 		break;
139a4f02d20SBill Paul 
140a4f02d20SBill Paul 	case MII_MEDIACHG:
14138ee137bSMarius Strobl 		mii_phy_setmedia(sc);
142a4f02d20SBill Paul 		break;
143a4f02d20SBill Paul 
144a4f02d20SBill Paul 	case MII_TICK:
145a4f02d20SBill Paul 		/*
146a4f02d20SBill Paul 		 * The RealTek PHY's autonegotiation doesn't need to be
147a4f02d20SBill Paul 		 * kicked; it continues in the background.
148a4f02d20SBill Paul 		 */
149a4f02d20SBill Paul 		break;
150a4f02d20SBill Paul 	}
151a4f02d20SBill Paul 
152a4f02d20SBill Paul 	/* Update the media status. */
1533fcb7a53SMarius Strobl 	PHY_STATUS(sc);
154a4f02d20SBill Paul 
155a4f02d20SBill Paul 	/* Callback if something changed. */
156d9730b8bSJonathan Lemon 	mii_phy_update(sc, cmd);
157a4f02d20SBill Paul 	return (0);
158a4f02d20SBill Paul }
159a72c060fSBill Paul 
16037c84183SPoul-Henning Kamp static void
rlphy_status(struct mii_softc * phy)1617d830ac9SWarner Losh rlphy_status(struct mii_softc *phy)
162a72c060fSBill Paul {
163a72c060fSBill Paul 	struct mii_data *mii = phy->mii_pdata;
16438ee137bSMarius Strobl 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
165a72c060fSBill Paul 	int bmsr, bmcr, anlpar;
166a72c060fSBill Paul 
167a72c060fSBill Paul 	mii->mii_media_status = IFM_AVALID;
168a72c060fSBill Paul 	mii->mii_media_active = IFM_ETHER;
169a72c060fSBill Paul 
170a72c060fSBill Paul 	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
171a72c060fSBill Paul 	if (bmsr & BMSR_LINK)
172a72c060fSBill Paul 		mii->mii_media_status |= IFM_ACTIVE;
173a72c060fSBill Paul 
174a72c060fSBill Paul 	bmcr = PHY_READ(phy, MII_BMCR);
175a72c060fSBill Paul 	if (bmcr & BMCR_ISO) {
176a72c060fSBill Paul 		mii->mii_media_active |= IFM_NONE;
177a72c060fSBill Paul 		mii->mii_media_status = 0;
178a72c060fSBill Paul 		return;
179a72c060fSBill Paul 	}
180a72c060fSBill Paul 
181a72c060fSBill Paul 	if (bmcr & BMCR_LOOP)
182a72c060fSBill Paul 		mii->mii_media_active |= IFM_LOOP;
183a72c060fSBill Paul 
184a72c060fSBill Paul 	if (bmcr & BMCR_AUTOEN) {
185a72c060fSBill Paul 		/*
186a72c060fSBill Paul 		 * NWay autonegotiation takes the highest-order common
187a72c060fSBill Paul 		 * bit of the ANAR and ANLPAR (i.e. best media advertised
188a72c060fSBill Paul 		 * both by us and our link partner).
189a72c060fSBill Paul 		 */
190a72c060fSBill Paul 		if ((bmsr & BMSR_ACOMP) == 0) {
191a72c060fSBill Paul 			/* Erg, still trying, I guess... */
192a72c060fSBill Paul 			mii->mii_media_active |= IFM_NONE;
193a72c060fSBill Paul 			return;
194a72c060fSBill Paul 		}
195a72c060fSBill Paul 
196a72c060fSBill Paul 		if ((anlpar = PHY_READ(phy, MII_ANAR) &
197a72c060fSBill Paul 		    PHY_READ(phy, MII_ANLPAR))) {
198d612cc59SPyun YongHyeon 			if (anlpar & ANLPAR_TX_FD)
199a72c060fSBill Paul 				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
200d612cc59SPyun YongHyeon 			else if (anlpar & ANLPAR_T4)
201d7a9ad56SMarius Strobl 				mii->mii_media_active |= IFM_100_T4|IFM_HDX;
202a72c060fSBill Paul 			else if (anlpar & ANLPAR_TX)
203d7a9ad56SMarius Strobl 				mii->mii_media_active |= IFM_100_TX|IFM_HDX;
204a72c060fSBill Paul 			else if (anlpar & ANLPAR_10_FD)
205a72c060fSBill Paul 				mii->mii_media_active |= IFM_10_T|IFM_FDX;
206a72c060fSBill Paul 			else if (anlpar & ANLPAR_10)
207d7a9ad56SMarius Strobl 				mii->mii_media_active |= IFM_10_T|IFM_HDX;
208a72c060fSBill Paul 			else
209a72c060fSBill Paul 				mii->mii_media_active |= IFM_NONE;
2103fcb7a53SMarius Strobl 			if ((mii->mii_media_active & IFM_FDX) != 0)
2113fcb7a53SMarius Strobl 				mii->mii_media_active |=
2123fcb7a53SMarius Strobl 				    mii_phy_flowstatus(phy);
213a72c060fSBill Paul 			return;
214a72c060fSBill Paul 		}
215a72c060fSBill Paul 		/*
216a72c060fSBill Paul 		 * If the other side doesn't support NWAY, then the
217a72c060fSBill Paul 		 * best we can do is determine if we have a 10Mbps or
218a72c060fSBill Paul 		 * 100Mbps link. There's no way to know if the link
219a72c060fSBill Paul 		 * is full or half duplex, so we default to half duplex
220a72c060fSBill Paul 		 * and hope that the user is clever enough to manually
221a72c060fSBill Paul 		 * change the media settings if we're wrong.
222a72c060fSBill Paul 		 */
223a72c060fSBill Paul 
224a72c060fSBill Paul 		/*
225a72c060fSBill Paul 		 * The RealTek PHY supports non-NWAY link speed
226a72c060fSBill Paul 		 * detection, however it does not report the link
227a72c060fSBill Paul 		 * detection results via the ANLPAR or BMSR registers.
228a72c060fSBill Paul 		 * (What? RealTek doesn't do things the way everyone
229a72c060fSBill Paul 		 * else does? I'm just shocked, shocked I tell you.)
230a72c060fSBill Paul 		 * To determine the link speed, we have to do one
231a72c060fSBill Paul 		 * of two things:
232a72c060fSBill Paul 		 *
2333fcb7a53SMarius Strobl 		 * - If this is a standalone RealTek RTL8201(L) or
2343fcb7a53SMarius Strobl 		 *   workalike PHY, we can determine the link speed by
2353fcb7a53SMarius Strobl 		 *   testing bit 0 in the magic, vendor-specific register
2363fcb7a53SMarius Strobl 		 *   at offset 0x19.
237a72c060fSBill Paul 		 *
238a72c060fSBill Paul 		 * - If this is a RealTek MAC with integrated PHY, we
239a72c060fSBill Paul 		 *   can test the 'SPEED10' bit of the MAC's media status
240a72c060fSBill Paul 		 *   register.
241a72c060fSBill Paul 		 */
2423fcb7a53SMarius Strobl 		if (!(phy->mii_mpd_model == 0 && phy->mii_mpd_rev == 0)) {
243a72c060fSBill Paul 			if (PHY_READ(phy, 0x0019) & 0x01)
244a72c060fSBill Paul 				mii->mii_media_active |= IFM_100_TX;
245a72c060fSBill Paul 			else
246a72c060fSBill Paul 				mii->mii_media_active |= IFM_10_T;
247a72c060fSBill Paul 		} else {
248a72c060fSBill Paul 			if (PHY_READ(phy, RL_MEDIASTAT) &
249a72c060fSBill Paul 			    RL_MEDIASTAT_SPEED10)
250a72c060fSBill Paul 				mii->mii_media_active |= IFM_10_T;
251a72c060fSBill Paul 			else
252a72c060fSBill Paul 				mii->mii_media_active |= IFM_100_TX;
253a72c060fSBill Paul 		}
254d7a9ad56SMarius Strobl 		mii->mii_media_active |= IFM_HDX;
255a72c060fSBill Paul 	} else
25638ee137bSMarius Strobl 		mii->mii_media_active = ife->ifm_media;
257a72c060fSBill Paul }
258