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