1139749Simp/*-
2119976Swpaul * Copyright (c) 2003
3119976Swpaul *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4119976Swpaul *
5119976Swpaul * Redistribution and use in source and binary forms, with or without
6119976Swpaul * modification, are permitted provided that the following conditions
7119976Swpaul * are met:
8119976Swpaul * 1. Redistributions of source code must retain the above copyright
9119976Swpaul *    notice, this list of conditions and the following disclaimer.
10119976Swpaul * 2. Redistributions in binary form must reproduce the above copyright
11119976Swpaul *    notice, this list of conditions and the following disclaimer in the
12119976Swpaul *    documentation and/or other materials provided with the distribution.
13119976Swpaul * 3. All advertising materials mentioning features or use of this software
14119976Swpaul *    must display the following acknowledgement:
15119976Swpaul *	This product includes software developed by Bill Paul.
16119976Swpaul * 4. Neither the name of the author nor the names of any co-contributors
17119976Swpaul *    may be used to endorse or promote products derived from this software
18119976Swpaul *    without specific prior written permission.
19119976Swpaul *
20119976Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21119976Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22119976Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23119976Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24119976Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25119976Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26119976Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27119976Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28119976Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29119976Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30119976Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
31119976Swpaul */
32119976Swpaul
33129844Smarius#include <sys/cdefs.h>
34129844Smarius__FBSDID("$FreeBSD$");
35129844Smarius
36119976Swpaul/*
37180178Syongari * Driver for the RealTek 8169S/8110S/8211B/8211C internal 10/100/1000 PHY.
38119976Swpaul */
39119976Swpaul
40119976Swpaul#include <sys/param.h>
41119976Swpaul#include <sys/systm.h>
42119976Swpaul#include <sys/kernel.h>
43129876Sphk#include <sys/module.h>
44119976Swpaul#include <sys/socket.h>
45119976Swpaul#include <sys/bus.h>
46119976Swpaul
47119976Swpaul#include <net/if.h>
48119976Swpaul#include <net/if_arp.h>
49119976Swpaul#include <net/if_media.h>
50119976Swpaul
51119976Swpaul#include <dev/mii/mii.h>
52119976Swpaul#include <dev/mii/miivar.h>
53119976Swpaul#include "miidevs.h"
54119976Swpaul
55119976Swpaul#include <dev/mii/rgephyreg.h>
56119976Swpaul
57119976Swpaul#include "miibus_if.h"
58119976Swpaul
59119976Swpaul#include <machine/bus.h>
60119976Swpaul#include <pci/if_rlreg.h>
61119976Swpaul
62119976Swpaulstatic int rgephy_probe(device_t);
63119976Swpaulstatic int rgephy_attach(device_t);
64119976Swpaul
65119976Swpaulstatic device_method_t rgephy_methods[] = {
66119976Swpaul	/* device interface */
67119976Swpaul	DEVMETHOD(device_probe,		rgephy_probe),
68119976Swpaul	DEVMETHOD(device_attach,	rgephy_attach),
69119976Swpaul	DEVMETHOD(device_detach,	mii_phy_detach),
70119976Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
71227908Smarius	DEVMETHOD_END
72119976Swpaul};
73119976Swpaul
74119976Swpaulstatic devclass_t rgephy_devclass;
75119976Swpaul
76119976Swpaulstatic driver_t rgephy_driver = {
77119976Swpaul	"rgephy",
78119976Swpaul	rgephy_methods,
79221407Smarius	sizeof(struct mii_softc)
80119976Swpaul};
81119976Swpaul
82119976SwpaulDRIVER_MODULE(rgephy, miibus, rgephy_driver, rgephy_devclass, 0, 0);
83119976Swpaul
84119976Swpaulstatic int	rgephy_service(struct mii_softc *, struct mii_data *, int);
85119976Swpaulstatic void	rgephy_status(struct mii_softc *);
86215298Smariusstatic int	rgephy_mii_phy_auto(struct mii_softc *, int);
87119976Swpaulstatic void	rgephy_reset(struct mii_softc *);
88119976Swpaulstatic void	rgephy_loop(struct mii_softc *);
89119976Swpaulstatic void	rgephy_load_dspcode(struct mii_softc *);
90119976Swpaul
91164827Smariusstatic const struct mii_phydesc rgephys[] = {
92221407Smarius	MII_PHY_DESC(REALTEK, RTL8169S),
93257612Syongari	MII_PHY_DESC(REALTEK, RTL8251),
94164827Smarius	MII_PHY_END
95164827Smarius};
96164827Smarius
97221407Smariusstatic const struct mii_phy_funcs rgephy_funcs = {
98221407Smarius	rgephy_service,
99221407Smarius	rgephy_status,
100221407Smarius	rgephy_reset
101221407Smarius};
102221407Smarius
103119976Swpaulstatic int
104150763Simprgephy_probe(device_t dev)
105119976Swpaul{
106119976Swpaul
107164827Smarius	return (mii_phy_dev_probe(dev, rgephys, BUS_PROBE_DEFAULT));
108119976Swpaul}
109119976Swpaul
110119976Swpaulstatic int
111150763Simprgephy_attach(device_t dev)
112119976Swpaul{
113119976Swpaul	struct mii_softc *sc;
114232246Syongari	struct mii_attach_args *ma;
115232246Syongari	u_int flags;
116119976Swpaul
117221407Smarius	sc = device_get_softc(dev);
118232246Syongari	ma = device_get_ivars(dev);
119232246Syongari	flags = 0;
120232246Syongari	if (strcmp(ma->mii_data->mii_ifp->if_dname, "re") == 0)
121232246Syongari		flags |= MIIF_PHYPRIV0;
122232246Syongari	mii_phy_dev_attach(dev, flags, &rgephy_funcs, 0);
123119976Swpaul
124215298Smarius	/* RTL8169S do not report auto-sense; add manually. */
125215298Smarius	sc->mii_capabilities = (PHY_READ(sc, MII_BMSR) | BMSR_ANEG) &
126221407Smarius	    sc->mii_capmask;
127165313Smarius	if (sc->mii_capabilities & BMSR_EXTSTAT)
128165313Smarius		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
129119976Swpaul	device_printf(dev, " ");
130164831Smarius	mii_phy_add_media(sc);
131119976Swpaul	printf("\n");
132217415Smarius	/*
133217415Smarius	 * Allow IFM_FLAG0 to be set indicating that auto-negotiation with
134217415Smarius	 * manual configuration, which is used to work around issues with
135217415Smarius	 * certain setups by default, should not be triggered as it may in
136217415Smarius	 * turn cause harm in some edge cases.
137217415Smarius	 */
138221407Smarius	sc->mii_pdata->mii_media.ifm_mask |= IFM_FLAG0;
139119976Swpaul
140221407Smarius	PHY_RESET(sc);
141221407Smarius
142119976Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
143164830Smarius	return (0);
144119976Swpaul}
145119976Swpaul
146119976Swpaulstatic int
147150763Simprgephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
148119976Swpaul{
149119976Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
150161237Syongari	int reg, speed, gig, anar;
151119976Swpaul
152119976Swpaul	switch (cmd) {
153119976Swpaul	case MII_POLLSTAT:
154119976Swpaul		break;
155119976Swpaul
156119976Swpaul	case MII_MEDIACHG:
157119976Swpaul		/*
158119976Swpaul		 * If the interface is not up, don't do anything.
159119976Swpaul		 */
160119976Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
161119976Swpaul			break;
162119976Swpaul
163221407Smarius		PHY_RESET(sc);	/* XXX hardware bug work-around */
164119976Swpaul
165161237Syongari		anar = PHY_READ(sc, RGEPHY_MII_ANAR);
166215298Smarius		anar &= ~(RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP |
167215298Smarius		    RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX |
168161237Syongari		    RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10);
169161237Syongari
170119976Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
171119976Swpaul		case IFM_AUTO:
172119976Swpaul#ifdef foo
173119976Swpaul			/*
174119976Swpaul			 * If we're already in auto mode, just return.
175119976Swpaul			 */
176119976Swpaul			if (PHY_READ(sc, RGEPHY_MII_BMCR) & RGEPHY_BMCR_AUTOEN)
177119976Swpaul				return (0);
178119976Swpaul#endif
179215298Smarius			(void)rgephy_mii_phy_auto(sc, ife->ifm_media);
180119976Swpaul			break;
181119976Swpaul		case IFM_1000_T:
182119976Swpaul			speed = RGEPHY_S1000;
183119976Swpaul			goto setit;
184119976Swpaul		case IFM_100_TX:
185119976Swpaul			speed = RGEPHY_S100;
186161237Syongari			anar |= RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_TX;
187119976Swpaul			goto setit;
188119976Swpaul		case IFM_10_T:
189119976Swpaul			speed = RGEPHY_S10;
190161237Syongari			anar |= RGEPHY_ANAR_10_FD | RGEPHY_ANAR_10;
191119976Swpaulsetit:
192217415Smarius			if ((ife->ifm_media & IFM_FLOW) != 0 &&
193217415Smarius			    (mii->mii_media.ifm_media & IFM_FLAG0) != 0)
194217415Smarius				return (EINVAL);
195217415Smarius
196217415Smarius			if ((ife->ifm_media & IFM_FDX) != 0) {
197119976Swpaul				speed |= RGEPHY_BMCR_FDX;
198119976Swpaul				gig = RGEPHY_1000CTL_AFD;
199161237Syongari				anar &= ~(RGEPHY_ANAR_TX | RGEPHY_ANAR_10);
200217415Smarius				if ((ife->ifm_media & IFM_FLOW) != 0 ||
201217415Smarius				    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
202217415Smarius					anar |=
203217415Smarius					    RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP;
204119976Swpaul			} else {
205119976Swpaul				gig = RGEPHY_1000CTL_AHD;
206161237Syongari				anar &=
207161237Syongari				    ~(RGEPHY_ANAR_TX_FD | RGEPHY_ANAR_10_FD);
208119976Swpaul			}
209217415Smarius			if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
210217415Smarius				gig |= RGEPHY_1000CTL_MSE;
211217415Smarius				if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
212217415Smarius				    gig |= RGEPHY_1000CTL_MSC;
213217415Smarius			} else {
214217415Smarius				gig = 0;
215217415Smarius				anar &= ~RGEPHY_ANAR_ASP;
216161237Syongari			}
217217415Smarius			if ((mii->mii_media.ifm_media & IFM_FLAG0) == 0)
218217415Smarius				speed |=
219217415Smarius				    RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG;
220217415Smarius			rgephy_loop(sc);
221215298Smarius			PHY_WRITE(sc, RGEPHY_MII_1000CTL, gig);
222215298Smarius			PHY_WRITE(sc, RGEPHY_MII_ANAR, anar);
223217415Smarius			PHY_WRITE(sc, RGEPHY_MII_BMCR, speed);
224119976Swpaul			break;
225119976Swpaul		case IFM_NONE:
226215298Smarius			PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN);
227119976Swpaul			break;
228119976Swpaul		default:
229119976Swpaul			return (EINVAL);
230119976Swpaul		}
231119976Swpaul		break;
232119976Swpaul
233119976Swpaul	case MII_TICK:
234119976Swpaul		/*
235119976Swpaul		 * Is the interface even up?
236119976Swpaul		 */
237119976Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
238119976Swpaul			return (0);
239119976Swpaul
240119976Swpaul		/*
241119976Swpaul		 * Only used for autonegotiation.
242119976Swpaul		 */
243173128Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
244173128Syongari			sc->mii_ticks = 0;
245119976Swpaul			break;
246173128Syongari		}
247119976Swpaul
248119976Swpaul		/*
249119976Swpaul		 * Check to see if we have link.  If we do, we don't
250217415Smarius		 * need to restart the autonegotiation process.
251119976Swpaul		 */
252232246Syongari		if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 &&
253232246Syongari		    sc->mii_mpd_rev >= 2) {
254173129Syongari			/* RTL8211B(L) */
255173129Syongari			reg = PHY_READ(sc, RGEPHY_MII_SSR);
256173129Syongari			if (reg & RGEPHY_SSR_LINK) {
257173129Syongari				sc->mii_ticks = 0;
258173129Syongari				break;
259173129Syongari			}
260173129Syongari		} else {
261173129Syongari			reg = PHY_READ(sc, RL_GMEDIASTAT);
262173129Syongari			if (reg & RL_GMEDIASTAT_LINK) {
263173129Syongari				sc->mii_ticks = 0;
264173129Syongari				break;
265173129Syongari			}
266173128Syongari		}
267119976Swpaul
268173128Syongari		/* Announce link loss right after it happens. */
269173128Syongari		if (sc->mii_ticks++ == 0)
270128870Sandre			break;
271164830Smarius
272173128Syongari		/* Only retry autonegotiation every mii_anegticks seconds. */
273173128Syongari		if (sc->mii_ticks <= sc->mii_anegticks)
274173128Syongari			return (0);
275173128Syongari
276119976Swpaul		sc->mii_ticks = 0;
277215298Smarius		rgephy_mii_phy_auto(sc, ife->ifm_media);
278173128Syongari		break;
279119976Swpaul	}
280119976Swpaul
281119976Swpaul	/* Update the media status. */
282221407Smarius	PHY_STATUS(sc);
283119976Swpaul
284119976Swpaul	/*
285119976Swpaul	 * Callback if something changed. Note that we need to poke
286119983Swpaul	 * the DSP on the RealTek PHYs if the media changes.
287119976Swpaul	 *
288119976Swpaul	 */
289164830Smarius	if (sc->mii_media_active != mii->mii_media_active ||
290119976Swpaul	    sc->mii_media_status != mii->mii_media_status ||
291119976Swpaul	    cmd == MII_MEDIACHG) {
292119976Swpaul		rgephy_load_dspcode(sc);
293119976Swpaul	}
294128870Sandre	mii_phy_update(sc, cmd);
295119976Swpaul	return (0);
296119976Swpaul}
297119976Swpaul
298119976Swpaulstatic void
299150763Simprgephy_status(struct mii_softc *sc)
300119976Swpaul{
301119976Swpaul	struct mii_data *mii = sc->mii_pdata;
302119983Swpaul	int bmsr, bmcr;
303173129Syongari	uint16_t ssr;
304119976Swpaul
305119976Swpaul	mii->mii_media_status = IFM_AVALID;
306119976Swpaul	mii->mii_media_active = IFM_ETHER;
307119976Swpaul
308232246Syongari	if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev >= 2) {
309173129Syongari		ssr = PHY_READ(sc, RGEPHY_MII_SSR);
310173129Syongari		if (ssr & RGEPHY_SSR_LINK)
311173129Syongari			mii->mii_media_status |= IFM_ACTIVE;
312173129Syongari	} else {
313173129Syongari		bmsr = PHY_READ(sc, RL_GMEDIASTAT);
314173129Syongari		if (bmsr & RL_GMEDIASTAT_LINK)
315173129Syongari			mii->mii_media_status |= IFM_ACTIVE;
316173129Syongari	}
317119976Swpaul
318119976Swpaul	bmsr = PHY_READ(sc, RGEPHY_MII_BMSR);
319119976Swpaul
320119976Swpaul	bmcr = PHY_READ(sc, RGEPHY_MII_BMCR);
321176809Syongari	if (bmcr & RGEPHY_BMCR_ISO) {
322176809Syongari		mii->mii_media_active |= IFM_NONE;
323176809Syongari		mii->mii_media_status = 0;
324176809Syongari		return;
325176809Syongari	}
326119976Swpaul
327119976Swpaul	if (bmcr & RGEPHY_BMCR_LOOP)
328119976Swpaul		mii->mii_media_active |= IFM_LOOP;
329119976Swpaul
330119976Swpaul	if (bmcr & RGEPHY_BMCR_AUTOEN) {
331119976Swpaul		if ((bmsr & RGEPHY_BMSR_ACOMP) == 0) {
332119976Swpaul			/* Erg, still trying, I guess... */
333119976Swpaul			mii->mii_media_active |= IFM_NONE;
334119976Swpaul			return;
335119976Swpaul		}
336119976Swpaul	}
337119976Swpaul
338232246Syongari	if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev >= 2) {
339173129Syongari		ssr = PHY_READ(sc, RGEPHY_MII_SSR);
340173129Syongari		switch (ssr & RGEPHY_SSR_SPD_MASK) {
341173129Syongari		case RGEPHY_SSR_S1000:
342173129Syongari			mii->mii_media_active |= IFM_1000_T;
343173129Syongari			break;
344173129Syongari		case RGEPHY_SSR_S100:
345173129Syongari			mii->mii_media_active |= IFM_100_TX;
346173129Syongari			break;
347173129Syongari		case RGEPHY_SSR_S10:
348173129Syongari			mii->mii_media_active |= IFM_10_T;
349173129Syongari			break;
350173129Syongari		default:
351173129Syongari			mii->mii_media_active |= IFM_NONE;
352173129Syongari			break;
353173129Syongari		}
354173129Syongari		if (ssr & RGEPHY_SSR_FDX)
355173129Syongari			mii->mii_media_active |= IFM_FDX;
356173129Syongari		else
357173129Syongari			mii->mii_media_active |= IFM_HDX;
358173129Syongari	} else {
359173129Syongari		bmsr = PHY_READ(sc, RL_GMEDIASTAT);
360173129Syongari		if (bmsr & RL_GMEDIASTAT_1000MBPS)
361173129Syongari			mii->mii_media_active |= IFM_1000_T;
362173129Syongari		else if (bmsr & RL_GMEDIASTAT_100MBPS)
363173129Syongari			mii->mii_media_active |= IFM_100_TX;
364173129Syongari		else if (bmsr & RL_GMEDIASTAT_10MBPS)
365173129Syongari			mii->mii_media_active |= IFM_10_T;
366173129Syongari		else
367173129Syongari			mii->mii_media_active |= IFM_NONE;
368173129Syongari		if (bmsr & RL_GMEDIASTAT_FDX)
369173129Syongari			mii->mii_media_active |= IFM_FDX;
370173129Syongari		else
371173129Syongari			mii->mii_media_active |= IFM_HDX;
372173129Syongari	}
373215298Smarius
374215298Smarius	if ((mii->mii_media_active & IFM_FDX) != 0)
375215298Smarius		mii->mii_media_active |= mii_phy_flowstatus(sc);
376215298Smarius
377215298Smarius	if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
378215298Smarius	    (PHY_READ(sc, RGEPHY_MII_1000STS) & RGEPHY_1000STS_MSR) != 0)
379215298Smarius		mii->mii_media_active |= IFM_ETH_MASTER;
380119976Swpaul}
381119976Swpaul
382119976Swpaulstatic int
383215298Smariusrgephy_mii_phy_auto(struct mii_softc *sc, int media)
384119976Swpaul{
385215298Smarius	int anar;
386164830Smarius
387215298Smarius	rgephy_loop(sc);
388221407Smarius	PHY_RESET(sc);
389119976Swpaul
390215298Smarius	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
391215298Smarius	if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
392215298Smarius		anar |= RGEPHY_ANAR_PC | RGEPHY_ANAR_ASP;
393215298Smarius	PHY_WRITE(sc, RGEPHY_MII_ANAR, anar);
394119976Swpaul	DELAY(1000);
395215298Smarius	PHY_WRITE(sc, RGEPHY_MII_1000CTL,
396215298Smarius	    RGEPHY_1000CTL_AHD | RGEPHY_1000CTL_AFD);
397119976Swpaul	DELAY(1000);
398215298Smarius	PHY_WRITE(sc, RGEPHY_MII_BMCR,
399119976Swpaul	    RGEPHY_BMCR_AUTOEN | RGEPHY_BMCR_STARTNEG);
400119976Swpaul	DELAY(100);
401119976Swpaul
402119976Swpaul	return (EJUSTRETURN);
403119976Swpaul}
404119976Swpaul
405119976Swpaulstatic void
406119976Swpaulrgephy_loop(struct mii_softc *sc)
407119976Swpaul{
408119976Swpaul	int i;
409119976Swpaul
410257612Syongari	if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 &&
411257612Syongari	    sc->mii_mpd_rev < 2) {
412173129Syongari		PHY_WRITE(sc, RGEPHY_MII_BMCR, RGEPHY_BMCR_PDOWN);
413173129Syongari		DELAY(1000);
414173129Syongari	}
415119976Swpaul
416119976Swpaul	for (i = 0; i < 15000; i++) {
417164830Smarius		if (!(PHY_READ(sc, RGEPHY_MII_BMSR) & RGEPHY_BMSR_LINK)) {
418119976Swpaul#if 0
419119976Swpaul			device_printf(sc->mii_dev, "looped %d\n", i);
420119976Swpaul#endif
421119976Swpaul			break;
422119976Swpaul		}
423119976Swpaul		DELAY(10);
424119976Swpaul	}
425119976Swpaul}
426119976Swpaul
427119976Swpaul#define PHY_SETBIT(x, y, z) \
428119976Swpaul	PHY_WRITE(x, y, (PHY_READ(x, y) | (z)))
429119976Swpaul#define PHY_CLRBIT(x, y, z) \
430119976Swpaul	PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z)))
431119976Swpaul
432119983Swpaul/*
433119983Swpaul * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of
434119983Swpaul * existing revisions of the 8169S/8110S chips need to be tuned in
435159962Swpaul * order to reliably negotiate a 1000Mbps link. This is only needed
436159962Swpaul * for rev 0 and rev 1 of the PHY. Later versions work without
437159962Swpaul * any fixups.
438119983Swpaul */
439119976Swpaulstatic void
440119976Swpaulrgephy_load_dspcode(struct mii_softc *sc)
441119976Swpaul{
442119976Swpaul	int val;
443119976Swpaul
444257612Syongari	if (sc->mii_mpd_model == MII_MODEL_REALTEK_RTL8251 ||
445257612Syongari	    sc->mii_mpd_rev >= 2)
446159962Swpaul		return;
447159962Swpaul
448119976Swpaul	PHY_WRITE(sc, 31, 0x0001);
449119976Swpaul	PHY_WRITE(sc, 21, 0x1000);
450119976Swpaul	PHY_WRITE(sc, 24, 0x65C7);
451119976Swpaul	PHY_CLRBIT(sc, 4, 0x0800);
452119976Swpaul	val = PHY_READ(sc, 4) & 0xFFF;
453119976Swpaul	PHY_WRITE(sc, 4, val);
454119976Swpaul	PHY_WRITE(sc, 3, 0x00A1);
455119976Swpaul	PHY_WRITE(sc, 2, 0x0008);
456119976Swpaul	PHY_WRITE(sc, 1, 0x1020);
457119976Swpaul	PHY_WRITE(sc, 0, 0x1000);
458119976Swpaul	PHY_SETBIT(sc, 4, 0x0800);
459119976Swpaul	PHY_CLRBIT(sc, 4, 0x0800);
460119976Swpaul	val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000;
461119976Swpaul	PHY_WRITE(sc, 4, val);
462119976Swpaul	PHY_WRITE(sc, 3, 0xFF41);
463119976Swpaul	PHY_WRITE(sc, 2, 0xDE60);
464119976Swpaul	PHY_WRITE(sc, 1, 0x0140);
465119976Swpaul	PHY_WRITE(sc, 0, 0x0077);
466119976Swpaul	val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000;
467119976Swpaul	PHY_WRITE(sc, 4, val);
468119976Swpaul	PHY_WRITE(sc, 3, 0xDF01);
469119976Swpaul	PHY_WRITE(sc, 2, 0xDF20);
470119976Swpaul	PHY_WRITE(sc, 1, 0xFF95);
471119976Swpaul	PHY_WRITE(sc, 0, 0xFA00);
472119976Swpaul	val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000;
473119976Swpaul	PHY_WRITE(sc, 4, val);
474119976Swpaul	PHY_WRITE(sc, 3, 0xFF41);
475119976Swpaul	PHY_WRITE(sc, 2, 0xDE20);
476119976Swpaul	PHY_WRITE(sc, 1, 0x0140);
477119976Swpaul	PHY_WRITE(sc, 0, 0x00BB);
478119976Swpaul	val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000;
479119976Swpaul	PHY_WRITE(sc, 4, val);
480119976Swpaul	PHY_WRITE(sc, 3, 0xDF01);
481119976Swpaul	PHY_WRITE(sc, 2, 0xDF20);
482119976Swpaul	PHY_WRITE(sc, 1, 0xFF95);
483119976Swpaul	PHY_WRITE(sc, 0, 0xBF00);
484119976Swpaul	PHY_SETBIT(sc, 4, 0x0800);
485119976Swpaul	PHY_CLRBIT(sc, 4, 0x0800);
486119976Swpaul	PHY_WRITE(sc, 31, 0x0000);
487164830Smarius
488119976Swpaul	DELAY(40);
489119976Swpaul}
490119976Swpaul
491119976Swpaulstatic void
492119976Swpaulrgephy_reset(struct mii_softc *sc)
493119976Swpaul{
494248542Syongari	uint16_t pcr, ssr;
495164830Smarius
496232246Syongari	if ((sc->mii_flags & MIIF_PHYPRIV0) == 0 && sc->mii_mpd_rev == 3) {
497180178Syongari		/* RTL8211C(L) */
498180178Syongari		ssr = PHY_READ(sc, RGEPHY_MII_SSR);
499180178Syongari		if ((ssr & RGEPHY_SSR_ALDPS) != 0) {
500180178Syongari			ssr &= ~RGEPHY_SSR_ALDPS;
501180178Syongari			PHY_WRITE(sc, RGEPHY_MII_SSR, ssr);
502180178Syongari		}
503180178Syongari	}
504180178Syongari
505248542Syongari	if (sc->mii_mpd_rev >= 2) {
506248542Syongari		pcr = PHY_READ(sc, RGEPHY_MII_PCR);
507248542Syongari		if ((pcr & RGEPHY_PCR_MDIX_AUTO) == 0) {
508248542Syongari			pcr &= ~RGEPHY_PCR_MDI_MASK;
509248542Syongari			pcr |= RGEPHY_PCR_MDIX_AUTO;
510248542Syongari			PHY_WRITE(sc, RGEPHY_MII_PCR, pcr);
511248542Syongari		}
512248542Syongari	}
513248542Syongari
514119976Swpaul	mii_phy_reset(sc);
515119976Swpaul	DELAY(1000);
516119976Swpaul	rgephy_load_dspcode(sc);
517119976Swpaul}
518