1139749Simp/*-
250702Swpaul * Copyright (c) 1997, 1998, 1999
350702Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
450702Swpaul *
550702Swpaul * Redistribution and use in source and binary forms, with or without
650702Swpaul * modification, are permitted provided that the following conditions
750702Swpaul * are met:
850702Swpaul * 1. Redistributions of source code must retain the above copyright
950702Swpaul *    notice, this list of conditions and the following disclaimer.
1050702Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1150702Swpaul *    notice, this list of conditions and the following disclaimer in the
1250702Swpaul *    documentation and/or other materials provided with the distribution.
1350702Swpaul * 3. All advertising materials mentioning features or use of this software
1450702Swpaul *    must display the following acknowledgement:
1550702Swpaul *	This product includes software developed by Bill Paul.
1650702Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1750702Swpaul *    may be used to endorse or promote products derived from this software
1850702Swpaul *    without specific prior written permission.
1950702Swpaul *
2050702Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2150702Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2250702Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2350702Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2450702Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2550702Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2650702Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2750702Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2850702Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2950702Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3050702Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3150702Swpaul */
3250702Swpaul
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/mii/rlphy.c 277093 2015-01-12 22:27:38Z glebius $");
35119418Sobrien
3650702Swpaul/*
3750702Swpaul * driver for RealTek 8139 internal PHYs
3850702Swpaul */
3950702Swpaul
4050702Swpaul#include <sys/param.h>
4150702Swpaul#include <sys/systm.h>
4250702Swpaul#include <sys/kernel.h>
43129876Sphk#include <sys/module.h>
4450702Swpaul#include <sys/socket.h>
4550702Swpaul#include <sys/bus.h>
46257184Sglebius#include <sys/taskqueue.h>	/* XXXGL: if_rlreg.h contamination */
4750702Swpaul
4850702Swpaul#include <net/if.h>
4994149Swpaul#include <net/if_arp.h>
5050702Swpaul#include <net/if_media.h>
5150702Swpaul
5250702Swpaul#include <dev/mii/mii.h>
5350702Swpaul#include <dev/mii/miivar.h>
54109514Sobrien#include "miidevs.h"
5550702Swpaul
5694149Swpaul#include <machine/bus.h>
57271864Sglebius#include <dev/rl/if_rlreg.h>
5894149Swpaul
5950702Swpaul#include "miibus_if.h"
6050702Swpaul
61105135Salfredstatic int rlphy_probe(device_t);
62105135Salfredstatic int rlphy_attach(device_t);
6350702Swpaul
6450702Swpaulstatic device_method_t rlphy_methods[] = {
6550702Swpaul	/* device interface */
6650702Swpaul	DEVMETHOD(device_probe,		rlphy_probe),
6750702Swpaul	DEVMETHOD(device_attach,	rlphy_attach),
6895722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
6950702Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
70227908Smarius	DEVMETHOD_END
7150702Swpaul};
7250702Swpaul
7350702Swpaulstatic devclass_t rlphy_devclass;
7450702Swpaul
7550702Swpaulstatic driver_t rlphy_driver = {
7650702Swpaul	"rlphy",
7750702Swpaul	rlphy_methods,
78221407Smarius	sizeof(struct mii_softc)
7950702Swpaul};
8050702Swpaul
8150702SwpaulDRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0);
8250702Swpaul
8392739Salfredstatic int	rlphy_service(struct mii_softc *, struct mii_data *, int);
8494149Swpaulstatic void	rlphy_status(struct mii_softc *);
8550702Swpaul
86165991Smarius/*
87165991Smarius * RealTek internal PHYs don't have vendor/device ID registers;
88165991Smarius * re(4) and rl(4) fake up a return value of all zeros.
89165991Smarius */
90165991Smariusstatic const struct mii_phydesc rlintphys[] = {
91165991Smarius	{ 0, 0, "RealTek internal media interface" },
92165991Smarius	MII_PHY_END
93165991Smarius};
94165991Smarius
95164827Smariusstatic const struct mii_phydesc rlphys[] = {
96221407Smarius	MII_PHY_DESC(yyREALTEK, RTL8201L),
97221407Smarius	MII_PHY_DESC(REALTEK, RTL8201E),
98221407Smarius	MII_PHY_DESC(xxICPLUS, IP101),
99164827Smarius	MII_PHY_END
100164827Smarius};
101164827Smarius
102221407Smariusstatic const struct mii_phy_funcs rlphy_funcs = {
103221407Smarius	rlphy_service,
104221407Smarius	rlphy_status,
105221407Smarius	mii_phy_reset
106221407Smarius};
107221407Smarius
108105135Salfredstatic int
109150763Simprlphy_probe(device_t dev)
11050702Swpaul{
111164827Smarius	int rv;
11250702Swpaul
113164827Smarius	rv = mii_phy_dev_probe(dev, rlphys, BUS_PROBE_DEFAULT);
114164827Smarius	if (rv <= 0)
115164827Smarius		return (rv);
11650702Swpaul
117277093Sglebius	if (mii_dev_mac_match(dev, "rl") || mii_dev_mac_match(dev, "re"))
118165991Smarius		return (mii_phy_dev_probe(dev, rlintphys, BUS_PROBE_DEFAULT));
119165991Smarius	return (ENXIO);
12050702Swpaul}
12150702Swpaul
122105135Salfredstatic int
123150763Simprlphy_attach(device_t dev)
12450702Swpaul{
12550702Swpaul
126213364Smarius	/*
127213364Smarius	 * The RealTek PHY can never be isolated.
128213364Smarius	 */
129221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
130221407Smarius	    &rlphy_funcs, 1);
131164711Smarius	return (0);
13250702Swpaul}
13350702Swpaul
13484145Sjlemonstatic int
135150763Simprlphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
13650702Swpaul{
13750702Swpaul
13850702Swpaul	switch (cmd) {
13950702Swpaul	case MII_POLLSTAT:
14050702Swpaul		break;
14150702Swpaul
14250702Swpaul	case MII_MEDIACHG:
143164711Smarius		mii_phy_setmedia(sc);
14450702Swpaul		break;
14550702Swpaul
14650702Swpaul	case MII_TICK:
14750702Swpaul		/*
14850702Swpaul		 * The RealTek PHY's autonegotiation doesn't need to be
14950702Swpaul		 * kicked; it continues in the background.
15050702Swpaul		 */
15150702Swpaul		break;
15250702Swpaul	}
15350702Swpaul
15450702Swpaul	/* Update the media status. */
155221407Smarius	PHY_STATUS(sc);
15650702Swpaul
15750702Swpaul	/* Callback if something changed. */
15884145Sjlemon	mii_phy_update(sc, cmd);
15950702Swpaul	return (0);
16050702Swpaul}
16194149Swpaul
162104094Sphkstatic void
163150763Simprlphy_status(struct mii_softc *phy)
16494149Swpaul{
16594149Swpaul	struct mii_data *mii = phy->mii_pdata;
166164711Smarius	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
16794149Swpaul	int bmsr, bmcr, anlpar;
16894149Swpaul
16994149Swpaul	mii->mii_media_status = IFM_AVALID;
17094149Swpaul	mii->mii_media_active = IFM_ETHER;
17194149Swpaul
17294149Swpaul	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
17394149Swpaul	if (bmsr & BMSR_LINK)
17494149Swpaul		mii->mii_media_status |= IFM_ACTIVE;
17594149Swpaul
17694149Swpaul	bmcr = PHY_READ(phy, MII_BMCR);
17794149Swpaul	if (bmcr & BMCR_ISO) {
17894149Swpaul		mii->mii_media_active |= IFM_NONE;
17994149Swpaul		mii->mii_media_status = 0;
18094149Swpaul		return;
18194149Swpaul	}
18294149Swpaul
18394149Swpaul	if (bmcr & BMCR_LOOP)
18494149Swpaul		mii->mii_media_active |= IFM_LOOP;
18594149Swpaul
18694149Swpaul	if (bmcr & BMCR_AUTOEN) {
18794149Swpaul		/*
18894149Swpaul		 * NWay autonegotiation takes the highest-order common
18994149Swpaul		 * bit of the ANAR and ANLPAR (i.e. best media advertised
19094149Swpaul		 * both by us and our link partner).
19194149Swpaul		 */
19294149Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
19394149Swpaul			/* Erg, still trying, I guess... */
19494149Swpaul			mii->mii_media_active |= IFM_NONE;
19594149Swpaul			return;
19694149Swpaul		}
19794149Swpaul
19894149Swpaul		if ((anlpar = PHY_READ(phy, MII_ANAR) &
19994149Swpaul		    PHY_READ(phy, MII_ANLPAR))) {
200173665Syongari			if (anlpar & ANLPAR_TX_FD)
201173665Syongari				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
202173665Syongari			else if (anlpar & ANLPAR_T4)
203213384Smarius				mii->mii_media_active |= IFM_100_T4|IFM_HDX;
20494149Swpaul			else if (anlpar & ANLPAR_TX)
205213384Smarius				mii->mii_media_active |= IFM_100_TX|IFM_HDX;
20694149Swpaul			else if (anlpar & ANLPAR_10_FD)
20794149Swpaul				mii->mii_media_active |= IFM_10_T|IFM_FDX;
20894149Swpaul			else if (anlpar & ANLPAR_10)
209213384Smarius				mii->mii_media_active |= IFM_10_T|IFM_HDX;
21094149Swpaul			else
211164711Smarius				mii->mii_media_active |= IFM_NONE;
212221407Smarius			if ((mii->mii_media_active & IFM_FDX) != 0)
213221407Smarius				mii->mii_media_active |=
214221407Smarius				    mii_phy_flowstatus(phy);
21594149Swpaul			return;
21694149Swpaul		}
21794149Swpaul		/*
21894149Swpaul		 * If the other side doesn't support NWAY, then the
21994149Swpaul		 * best we can do is determine if we have a 10Mbps or
220164711Smarius		 * 100Mbps link. There's no way to know if the link
22194149Swpaul		 * is full or half duplex, so we default to half duplex
22294149Swpaul		 * and hope that the user is clever enough to manually
22394149Swpaul		 * change the media settings if we're wrong.
22494149Swpaul		 */
22594149Swpaul
22694149Swpaul		/*
22794149Swpaul		 * The RealTek PHY supports non-NWAY link speed
22894149Swpaul		 * detection, however it does not report the link
22994149Swpaul		 * detection results via the ANLPAR or BMSR registers.
23094149Swpaul		 * (What? RealTek doesn't do things the way everyone
23194149Swpaul		 * else does? I'm just shocked, shocked I tell you.)
23294149Swpaul		 * To determine the link speed, we have to do one
23394149Swpaul		 * of two things:
23494149Swpaul		 *
235221407Smarius		 * - If this is a standalone RealTek RTL8201(L) or
236221407Smarius		 *   workalike PHY, we can determine the link speed by
237221407Smarius		 *   testing bit 0 in the magic, vendor-specific register
238221407Smarius		 *   at offset 0x19.
23994149Swpaul		 *
24094149Swpaul		 * - If this is a RealTek MAC with integrated PHY, we
24194149Swpaul		 *   can test the 'SPEED10' bit of the MAC's media status
24294149Swpaul		 *   register.
24394149Swpaul		 */
244221407Smarius		if (!(phy->mii_mpd_model == 0 && phy->mii_mpd_rev == 0)) {
24594149Swpaul			if (PHY_READ(phy, 0x0019) & 0x01)
24694149Swpaul				mii->mii_media_active |= IFM_100_TX;
24794149Swpaul			else
24894149Swpaul				mii->mii_media_active |= IFM_10_T;
24994149Swpaul		} else {
25094149Swpaul			if (PHY_READ(phy, RL_MEDIASTAT) &
25194149Swpaul			    RL_MEDIASTAT_SPEED10)
25294149Swpaul				mii->mii_media_active |= IFM_10_T;
25394149Swpaul			else
25494149Swpaul				mii->mii_media_active |= IFM_100_TX;
25594149Swpaul		}
256213384Smarius		mii->mii_media_active |= IFM_HDX;
25794149Swpaul	} else
258164711Smarius		mii->mii_media_active = ife->ifm_media;
25994149Swpaul}
260