rlphy.c revision 50758
150702Swpaul/*
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 * $FreeBSD: head/sys/dev/mii/rlphy.c 50758 1999-09-01 17:07:27Z wpaul $
3350702Swpaul */
3450702Swpaul
3550702Swpaul/*
3650702Swpaul * driver for RealTek 8139 internal PHYs
3750702Swpaul */
3850702Swpaul
3950702Swpaul#include <sys/param.h>
4050702Swpaul#include <sys/systm.h>
4150702Swpaul#include <sys/kernel.h>
4250702Swpaul#include <sys/malloc.h>
4350702Swpaul#include <sys/socket.h>
4450702Swpaul#include <sys/bus.h>
4550702Swpaul
4650702Swpaul#include <net/if.h>
4750702Swpaul#include <net/if_media.h>
4850702Swpaul
4950702Swpaul#include <dev/mii/mii.h>
5050702Swpaul#include <dev/mii/miivar.h>
5150702Swpaul
5250702Swpaul#include "miibus_if.h"
5350702Swpaul
5450702Swpaul#if !defined(lint)
5550702Swpaulstatic const char rcsid[] =
5650702Swpaul   "$FreeBSD: head/sys/dev/mii/rlphy.c 50758 1999-09-01 17:07:27Z wpaul $";
5750702Swpaul#endif
5850702Swpaul
5950702Swpaulstatic int rlphy_probe		__P((device_t));
6050702Swpaulstatic int rlphy_attach		__P((device_t));
6150702Swpaulstatic int rlphy_detach		__P((device_t));
6250702Swpaul
6350702Swpaulstatic device_method_t rlphy_methods[] = {
6450702Swpaul	/* device interface */
6550702Swpaul	DEVMETHOD(device_probe,		rlphy_probe),
6650702Swpaul	DEVMETHOD(device_attach,	rlphy_attach),
6750702Swpaul	DEVMETHOD(device_detach,	rlphy_detach),
6850702Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
6950702Swpaul	{ 0, 0 }
7050702Swpaul};
7150702Swpaul
7250702Swpaulstatic devclass_t rlphy_devclass;
7350702Swpaul
7450702Swpaulstatic driver_t rlphy_driver = {
7550702Swpaul	"rlphy",
7650702Swpaul	rlphy_methods,
7750702Swpaul	sizeof(struct mii_softc)
7850702Swpaul};
7950702Swpaul
8050702SwpaulDRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0);
8150702Swpaul
8250702Swpaulint	rlphy_service __P((struct mii_softc *, struct mii_data *, int));
8350702Swpaulvoid	rlphy_reset __P((struct mii_softc *));
8450702Swpaul
8550702Swpaulstatic int rlphy_probe(dev)
8650702Swpaul	device_t		dev;
8750702Swpaul{
8850702Swpaul	struct mii_attach_args *ma;
8950702Swpaul	device_t		parent;
9050702Swpaul
9150702Swpaul	ma = device_get_ivars(dev);
9250702Swpaul	parent = device_get_parent(device_get_parent(dev));
9350702Swpaul
9450702Swpaul	/*
9550702Swpaul	 * RealTek PHY doesn't have vendor/device ID registers:
9650702Swpaul	 * the rl driver fakes up a return value of all zeros.
9750702Swpaul	 */
9850702Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
9950702Swpaul	    MII_MODEL(ma->mii_id2) != 0)
10050702Swpaul		return (ENXIO);
10150702Swpaul
10250702Swpaul	/*
10350702Swpaul	 * Make sure the parent is an `rl'.
10450702Swpaul	 */
10550702Swpaul	if (strcmp(device_get_name(parent), "rl") != 0)
10650702Swpaul		return (ENXIO);
10750702Swpaul
10850702Swpaul	device_set_desc(dev, "RealTek internal media interface");
10950702Swpaul
11050702Swpaul	return (0);
11150702Swpaul}
11250702Swpaul
11350702Swpaulstatic int rlphy_attach(dev)
11450702Swpaul	device_t		dev;
11550702Swpaul{
11650702Swpaul	struct mii_softc	*sc;
11750702Swpaul	struct mii_attach_args	*ma;
11850702Swpaul	struct mii_data		*mii;
11950702Swpaul
12050702Swpaul	sc = device_get_softc(dev);
12150702Swpaul	ma = device_get_ivars(dev);
12250702Swpaul	sc->mii_dev = device_get_parent(dev);
12350702Swpaul	mii = device_get_softc(sc->mii_dev);
12450702Swpaul
12550702Swpaul	/*
12650702Swpaul	 * The RealTek PHY can never be isolated, so never allow non-zero
12750702Swpaul	 * instances!
12850702Swpaul	 */
12950702Swpaul	if (mii->mii_instance != 0) {
13050702Swpaul		device_printf(dev, "ignoring this PHY, non-zero instance\n");
13150702Swpaul		return(ENXIO);
13250702Swpaul	}
13350702Swpaul
13450758Swpaul	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
13550758Swpaul
13650758Swpaul	sc->mii_inst = mii->mii_instance;
13750758Swpaul	sc->mii_phy = ma->mii_phyno;
13850758Swpaul	sc->mii_service = rlphy_service;
13950758Swpaul	sc->mii_pdata = mii;
14050702Swpaul	mii->mii_instance++;
14150702Swpaul
14250702Swpaul	sc->mii_flags |= MIIF_NOISOLATE;
14350702Swpaul
14450702Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
14550702Swpaul
14650702Swpaul#if 0 /* See above. */
14750702Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
14850702Swpaul	    BMCR_ISO);
14950702Swpaul#endif
15050702Swpaul
15150702Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
15250702Swpaul	    BMCR_LOOP|BMCR_S100);
15350702Swpaul
15450702Swpaul	rlphy_reset(sc);
15550702Swpaul
15650702Swpaul	sc->mii_capabilities =
15750702Swpaul	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
15850702Swpaul	device_printf(dev, " ");
15950702Swpaul	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
16050702Swpaul		printf("no media present");
16150702Swpaul	else
16250702Swpaul		mii_add_media(mii, sc->mii_capabilities,
16350702Swpaul		    sc->mii_inst);
16450702Swpaul	printf("\n");
16550702Swpaul#undef ADD
16650702Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
16750702Swpaul	return(0);
16850702Swpaul}
16950702Swpaul
17050702Swpaulstatic int rlphy_detach(dev)
17150702Swpaul	device_t		dev;
17250702Swpaul{
17350702Swpaul	struct mii_softc	*sc;
17450702Swpaul	struct mii_data		*mii;
17550702Swpaul
17650702Swpaul	sc = device_get_softc(dev);
17750702Swpaul	mii = device_get_softc(device_get_softc(dev));
17850702Swpaul	sc->mii_dev = NULL;
17950702Swpaul	LIST_REMOVE(sc, mii_list);
18050702Swpaul
18150702Swpaul	return(0);
18250702Swpaul}
18350702Swpaul
18450702Swpaulint
18550702Swpaulrlphy_service(sc, mii, cmd)
18650702Swpaul	struct mii_softc *sc;
18750702Swpaul	struct mii_data *mii;
18850702Swpaul	int cmd;
18950702Swpaul{
19050702Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
19150702Swpaul
19250702Swpaul	/*
19350702Swpaul	 * We can't isolate the RealTek PHY, so it has to be the only one!
19450702Swpaul	 */
19550702Swpaul	if (IFM_INST(ife->ifm_media) != sc->mii_inst)
19650702Swpaul		panic("rlphy_service: can't isolate RealTek PHY");
19750702Swpaul
19850702Swpaul	switch (cmd) {
19950702Swpaul	case MII_POLLSTAT:
20050702Swpaul		break;
20150702Swpaul
20250702Swpaul	case MII_MEDIACHG:
20350702Swpaul		/*
20450702Swpaul		 * If the interface is not up, don't do anything.
20550702Swpaul		 */
20650702Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
20750702Swpaul			break;
20850702Swpaul
20950702Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
21050702Swpaul		case IFM_AUTO:
21150702Swpaul			/*
21250702Swpaul			 * If we're already in auto mode, just return.
21350702Swpaul			 */
21450702Swpaul			if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
21550702Swpaul				return (0);
21650702Swpaul			(void) mii_phy_auto(sc, 0);
21750702Swpaul			break;
21850702Swpaul		case IFM_100_T4:
21950702Swpaul			/*
22050702Swpaul			 * XXX Not supported as a manual setting right now.
22150702Swpaul			 */
22250702Swpaul			return (EINVAL);
22350702Swpaul		default:
22450702Swpaul			/*
22550702Swpaul			 * BMCR data is stored in the ifmedia entry.
22650702Swpaul			 */
22750702Swpaul			PHY_WRITE(sc, MII_ANAR,
22850702Swpaul			    mii_anar(ife->ifm_media));
22950702Swpaul			PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
23050702Swpaul		}
23150702Swpaul		break;
23250702Swpaul
23350702Swpaul	case MII_TICK:
23450702Swpaul		/*
23550702Swpaul		 * Only used for autonegotiation.
23650702Swpaul		 */
23750702Swpaul		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
23850702Swpaul			return (0);
23950702Swpaul
24050702Swpaul		/*
24150702Swpaul		 * Is the interface even up?
24250702Swpaul		 */
24350702Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
24450702Swpaul			return (0);
24550702Swpaul
24650702Swpaul		/*
24750702Swpaul		 * Only retry autonegotiation every 5 seconds.
24850702Swpaul		 */
24950702Swpaul		if (++sc->mii_ticks != 5)
25050702Swpaul			return (0);
25150702Swpaul
25250702Swpaul		sc->mii_ticks = 0;
25350702Swpaul
25450702Swpaul		/*
25550702Swpaul		 * The RealTek PHY's autonegotiation doesn't need to be
25650702Swpaul		 * kicked; it continues in the background.
25750702Swpaul		 */
25850702Swpaul		break;
25950702Swpaul	}
26050702Swpaul
26150702Swpaul	/* Update the media status. */
26250702Swpaul	ukphy_status(sc);
26350702Swpaul
26450702Swpaul	/* Callback if something changed. */
26550702Swpaul	if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
26650702Swpaul		MIIBUS_STATCHG(sc->mii_dev);
26750702Swpaul		sc->mii_active = mii->mii_media_active;
26850702Swpaul	}
26950702Swpaul	return (0);
27050702Swpaul}
27150702Swpaul
27250702Swpaulvoid
27350702Swpaulrlphy_reset(sc)
27450702Swpaul	struct mii_softc *sc;
27550702Swpaul{
27650702Swpaul
27750702Swpaul	mii_phy_reset(sc);
27850702Swpaul
27950702Swpaul	/*
28050702Swpaul	 * XXX RealTek PHY doesn't set the BMCR properly after
28150702Swpaul	 * XXX reset, which breaks autonegotiation.
28250702Swpaul	 */
28350702Swpaul	PHY_WRITE(sc, MII_BMCR, BMCR_S100|BMCR_AUTOEN|BMCR_FDX);
28450702Swpaul}
285