1356290Sjkim/*-
2238405Sjkim * Copyright (c) 1997, 1998, 1999
3238405Sjkim *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4238405Sjkim *
5238405Sjkim * Redistribution and use in source and binary forms, with or without
6238405Sjkim * modification, are permitted provided that the following conditions
7238405Sjkim * are met:
8238405Sjkim * 1. Redistributions of source code must retain the above copyright
9238405Sjkim *    notice, this list of conditions and the following disclaimer.
10238405Sjkim * 2. Redistributions in binary form must reproduce the above copyright
11238405Sjkim *    notice, this list of conditions and the following disclaimer in the
12238405Sjkim *    documentation and/or other materials provided with the distribution.
13238405Sjkim * 3. All advertising materials mentioning features or use of this software
14238405Sjkim *    must display the following acknowledgement:
15238405Sjkim *	This product includes software developed by Bill Paul.
16238405Sjkim * 4. Neither the name of the author nor the names of any co-contributors
17238405Sjkim *    may be used to endorse or promote products derived from this software
18238405Sjkim *    without specific prior written permission.
19238405Sjkim *
20238405Sjkim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21238405Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22238405Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23238405Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24238405Sjkim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25238405Sjkim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26238405Sjkim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27238405Sjkim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28238405Sjkim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29238405Sjkim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30238405Sjkim * THE POSSIBILITY OF SUCH DAMAGE.
31238405Sjkim */
32238405Sjkim
33238405Sjkim#include <sys/cdefs.h>
34238405Sjkim__FBSDID("$FreeBSD$");
35238405Sjkim
36238405Sjkim/*
37238405Sjkim * driver for RealTek 8139 internal PHYs
38238405Sjkim */
39238405Sjkim
40238405Sjkim#include <sys/param.h>
41276861Sjkim#include <sys/systm.h>
42276861Sjkim#include <sys/kernel.h>
43238405Sjkim#include <sys/module.h>
44238405Sjkim#include <sys/socket.h>
45238405Sjkim#include <sys/bus.h>
46238405Sjkim
47238405Sjkim#include <net/if.h>
48238405Sjkim#include <net/if_arp.h>
49312826Sjkim#include <net/if_media.h>
50238405Sjkim
51238405Sjkim#include <dev/mii/mii.h>
52238405Sjkim#include <dev/mii/miivar.h>
53276861Sjkim#include "miidevs.h"
54276861Sjkim
55276861Sjkim#include <machine/bus.h>
56238405Sjkim#include <pci/if_rlreg.h>
57344604Sjkim
58344604Sjkim#include "miibus_if.h"
59344604Sjkim
60344604Sjkimstatic int rlphy_probe(device_t);
61344604Sjkimstatic int rlphy_attach(device_t);
62344604Sjkim
63238405Sjkimstatic device_method_t rlphy_methods[] = {
64344604Sjkim	/* device interface */
65344604Sjkim	DEVMETHOD(device_probe,		rlphy_probe),
66344604Sjkim	DEVMETHOD(device_attach,	rlphy_attach),
67344604Sjkim	DEVMETHOD(device_detach,	mii_phy_detach),
68276861Sjkim	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
69238405Sjkim	DEVMETHOD_END
70344604Sjkim};
71238405Sjkim
72238405Sjkimstatic devclass_t rlphy_devclass;
73238405Sjkim
74238405Sjkimstatic driver_t rlphy_driver = {
75238405Sjkim	"rlphy",
76238405Sjkim	rlphy_methods,
77238405Sjkim	sizeof(struct mii_softc)
78238405Sjkim};
79238405Sjkim
80238405SjkimDRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0);
81238405Sjkim
82238405Sjkimstatic int	rlphy_service(struct mii_softc *, struct mii_data *, int);
83238405Sjkimstatic void	rlphy_status(struct mii_softc *);
84238405Sjkim
85238405Sjkim/*
86238405Sjkim * RealTek internal PHYs don't have vendor/device ID registers;
87238405Sjkim * re(4) and rl(4) fake up a return value of all zeros.
88238405Sjkim */
89238405Sjkimstatic const struct mii_phydesc rlintphys[] = {
90238405Sjkim	{ 0, 0, "RealTek internal media interface" },
91238405Sjkim	MII_PHY_END
92238405Sjkim};
93238405Sjkim
94238405Sjkimstatic const struct mii_phydesc rlphys[] = {
95238405Sjkim	MII_PHY_DESC(yyREALTEK, RTL8201L),
96238405Sjkim	MII_PHY_DESC(REALTEK, RTL8201E),
97238405Sjkim	MII_PHY_DESC(xxICPLUS, IP101),
98238405Sjkim	MII_PHY_END
99238405Sjkim};
100238405Sjkim
101238405Sjkimstatic const struct mii_phy_funcs rlphy_funcs = {
102238405Sjkim	rlphy_service,
103238405Sjkim	rlphy_status,
104238405Sjkim	mii_phy_reset
105238405Sjkim};
106238405Sjkim
107238405Sjkimstatic int
108238405Sjkimrlphy_probe(device_t dev)
109238405Sjkim{
110238405Sjkim	const char *nic;
111238405Sjkim	int rv;
112238405Sjkim
113238405Sjkim	rv = mii_phy_dev_probe(dev, rlphys, BUS_PROBE_DEFAULT);
114238405Sjkim	if (rv <= 0)
115238405Sjkim		return (rv);
116238405Sjkim
117238405Sjkim	nic = device_get_name(device_get_parent(device_get_parent(dev)));
118238405Sjkim	if (strcmp(nic, "rl") == 0 || strcmp(nic, "re") == 0)
119238405Sjkim		return (mii_phy_dev_probe(dev, rlintphys, BUS_PROBE_DEFAULT));
120238405Sjkim	return (ENXIO);
121238405Sjkim}
122238405Sjkim
123238405Sjkimstatic int
124238405Sjkimrlphy_attach(device_t dev)
125238405Sjkim{
126238405Sjkim
127238405Sjkim	/*
128238405Sjkim	 * The RealTek PHY can never be isolated.
129238405Sjkim	 */
130238405Sjkim	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
131238405Sjkim	    &rlphy_funcs, 1);
132238405Sjkim	return (0);
133238405Sjkim}
134238405Sjkim
135238405Sjkimstatic int
136356290Sjkimrlphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
137238405Sjkim{
138238405Sjkim
139238405Sjkim	switch (cmd) {
140238405Sjkim	case MII_POLLSTAT:
141238405Sjkim		break;
142238405Sjkim
143238405Sjkim	case MII_MEDIACHG:
144238405Sjkim		/*
145238405Sjkim		 * If the interface is not up, don't do anything.
146238405Sjkim		 */
147238405Sjkim		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
148238405Sjkim			break;
149238405Sjkim
150238405Sjkim		mii_phy_setmedia(sc);
151238405Sjkim		break;
152238405Sjkim
153238405Sjkim	case MII_TICK:
154344604Sjkim		/*
155238405Sjkim		 * Is the interface even up?
156238405Sjkim		 */
157238405Sjkim		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
158238405Sjkim			return (0);
159238405Sjkim
160238405Sjkim		/*
161238405Sjkim		 * The RealTek PHY's autonegotiation doesn't need to be
162238405Sjkim		 * kicked; it continues in the background.
163344604Sjkim		 */
164238405Sjkim		break;
165238405Sjkim	}
166238405Sjkim
167238405Sjkim	/* Update the media status. */
168238405Sjkim	PHY_STATUS(sc);
169344604Sjkim
170344604Sjkim	/* Callback if something changed. */
171238405Sjkim	mii_phy_update(sc, cmd);
172238405Sjkim	return (0);
173344604Sjkim}
174344604Sjkim
175344604Sjkimstatic void
176238405Sjkimrlphy_status(struct mii_softc *phy)
177238405Sjkim{
178344604Sjkim	struct mii_data *mii = phy->mii_pdata;
179	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
180	int bmsr, bmcr, anlpar;
181
182	mii->mii_media_status = IFM_AVALID;
183	mii->mii_media_active = IFM_ETHER;
184
185	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
186	if (bmsr & BMSR_LINK)
187		mii->mii_media_status |= IFM_ACTIVE;
188
189	bmcr = PHY_READ(phy, MII_BMCR);
190	if (bmcr & BMCR_ISO) {
191		mii->mii_media_active |= IFM_NONE;
192		mii->mii_media_status = 0;
193		return;
194	}
195
196	if (bmcr & BMCR_LOOP)
197		mii->mii_media_active |= IFM_LOOP;
198
199	if (bmcr & BMCR_AUTOEN) {
200		/*
201		 * NWay autonegotiation takes the highest-order common
202		 * bit of the ANAR and ANLPAR (i.e. best media advertised
203		 * both by us and our link partner).
204		 */
205		if ((bmsr & BMSR_ACOMP) == 0) {
206			/* Erg, still trying, I guess... */
207			mii->mii_media_active |= IFM_NONE;
208			return;
209		}
210
211		if ((anlpar = PHY_READ(phy, MII_ANAR) &
212		    PHY_READ(phy, MII_ANLPAR))) {
213			if (anlpar & ANLPAR_TX_FD)
214				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
215			else if (anlpar & ANLPAR_T4)
216				mii->mii_media_active |= IFM_100_T4|IFM_HDX;
217			else if (anlpar & ANLPAR_TX)
218				mii->mii_media_active |= IFM_100_TX|IFM_HDX;
219			else if (anlpar & ANLPAR_10_FD)
220				mii->mii_media_active |= IFM_10_T|IFM_FDX;
221			else if (anlpar & ANLPAR_10)
222				mii->mii_media_active |= IFM_10_T|IFM_HDX;
223			else
224				mii->mii_media_active |= IFM_NONE;
225			if ((mii->mii_media_active & IFM_FDX) != 0)
226				mii->mii_media_active |=
227				    mii_phy_flowstatus(phy);
228			return;
229		}
230		/*
231		 * If the other side doesn't support NWAY, then the
232		 * best we can do is determine if we have a 10Mbps or
233		 * 100Mbps link. There's no way to know if the link
234		 * is full or half duplex, so we default to half duplex
235		 * and hope that the user is clever enough to manually
236		 * change the media settings if we're wrong.
237		 */
238
239		/*
240		 * The RealTek PHY supports non-NWAY link speed
241		 * detection, however it does not report the link
242		 * detection results via the ANLPAR or BMSR registers.
243		 * (What? RealTek doesn't do things the way everyone
244		 * else does? I'm just shocked, shocked I tell you.)
245		 * To determine the link speed, we have to do one
246		 * of two things:
247		 *
248		 * - If this is a standalone RealTek RTL8201(L) or
249		 *   workalike PHY, we can determine the link speed by
250		 *   testing bit 0 in the magic, vendor-specific register
251		 *   at offset 0x19.
252		 *
253		 * - If this is a RealTek MAC with integrated PHY, we
254		 *   can test the 'SPEED10' bit of the MAC's media status
255		 *   register.
256		 */
257		if (!(phy->mii_mpd_model == 0 && phy->mii_mpd_rev == 0)) {
258			if (PHY_READ(phy, 0x0019) & 0x01)
259				mii->mii_media_active |= IFM_100_TX;
260			else
261				mii->mii_media_active |= IFM_10_T;
262		} else {
263			if (PHY_READ(phy, RL_MEDIASTAT) &
264			    RL_MEDIASTAT_SPEED10)
265				mii->mii_media_active |= IFM_10_T;
266			else
267				mii->mii_media_active |= IFM_100_TX;
268		}
269		mii->mii_media_active |= IFM_HDX;
270	} else
271		mii->mii_media_active = ife->ifm_media;
272}
273