xlphy.c revision 50758
150120Swpaul/*	$NetBSD: exphy.c,v 1.16 1999/04/23 04:24:32 thorpej Exp $	*/
250120Swpaul
350120Swpaul/*-
450120Swpaul * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
550120Swpaul * All rights reserved.
650120Swpaul *
750120Swpaul * This code is derived from software contributed to The NetBSD Foundation
850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
950120Swpaul * NASA Ames Research Center, and by Frank van der Linden.
1050120Swpaul *
1150120Swpaul * Redistribution and use in source and binary forms, with or without
1250120Swpaul * modification, are permitted provided that the following conditions
1350120Swpaul * are met:
1450120Swpaul * 1. Redistributions of source code must retain the above copyright
1550120Swpaul *    notice, this list of conditions and the following disclaimer.
1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1750120Swpaul *    notice, this list of conditions and the following disclaimer in the
1850120Swpaul *    documentation and/or other materials provided with the distribution.
1950120Swpaul * 3. All advertising materials mentioning features or use of this software
2050120Swpaul *    must display the following acknowledgement:
2150120Swpaul *	This product includes software developed by the NetBSD
2250120Swpaul *	Foundation, Inc. and its contributors.
2350120Swpaul * 4. Neither the name of The NetBSD Foundation nor the names of its
2450120Swpaul *    contributors may be used to endorse or promote products derived
2550120Swpaul *    from this software without specific prior written permission.
2650120Swpaul *
2750120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2850120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2950120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3050120Swpaul * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3150120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3250120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3350120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3450120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3550120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3650120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3750120Swpaul * POSSIBILITY OF SUCH DAMAGE.
3850120Swpaul */
3950120Swpaul
4050120Swpaul/*
4150120Swpaul * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
4250120Swpaul *
4350120Swpaul * Redistribution and use in source and binary forms, with or without
4450120Swpaul * modification, are permitted provided that the following conditions
4550120Swpaul * are met:
4650120Swpaul * 1. Redistributions of source code must retain the above copyright
4750120Swpaul *    notice, this list of conditions and the following disclaimer.
4850120Swpaul * 2. Redistributions in binary form must reproduce the above copyright
4950120Swpaul *    notice, this list of conditions and the following disclaimer in the
5050120Swpaul *    documentation and/or other materials provided with the distribution.
5150120Swpaul * 3. All advertising materials mentioning features or use of this software
5250120Swpaul *    must display the following acknowledgement:
5350120Swpaul *	This product includes software developed by Manuel Bouyer.
5450120Swpaul * 4. The name of the author may not be used to endorse or promote products
5550120Swpaul *    derived from this software without specific prior written permission.
5650120Swpaul *
5750120Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5850120Swpaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5950120Swpaul * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
6050120Swpaul * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
6150120Swpaul * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
6250120Swpaul * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
6350120Swpaul * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
6450120Swpaul * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
6550120Swpaul * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
6650120Swpaul * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6750120Swpaul */
6850120Swpaul
6950120Swpaul/*
7050120Swpaul * driver for 3Com internal PHYs
7150120Swpaul */
7250120Swpaul
7350120Swpaul#include <sys/param.h>
7450120Swpaul#include <sys/systm.h>
7550120Swpaul#include <sys/kernel.h>
7650120Swpaul#include <sys/malloc.h>
7750120Swpaul#include <sys/socket.h>
7850120Swpaul#include <sys/module.h>
7950120Swpaul#include <sys/bus.h>
8050120Swpaul
8150120Swpaul#include <net/if.h>
8250120Swpaul#include <net/if_media.h>
8350120Swpaul
8450120Swpaul#include <dev/mii/mii.h>
8550120Swpaul#include <dev/mii/miivar.h>
8650120Swpaul#include <dev/mii/miidevs.h>
8750120Swpaul
8850120Swpaul#include "miibus_if.h"
8950120Swpaul
9050120Swpaul#if !defined(lint)
9150120Swpaulstatic const char rcsid[] =
9250758Swpaul  "$FreeBSD: head/sys/dev/mii/exphy.c 50758 1999-09-01 17:07:27Z wpaul $";
9350120Swpaul#endif
9450120Swpaul
9550120Swpaulstatic int exphy_probe		__P((device_t));
9650120Swpaulstatic int exphy_attach		__P((device_t));
9750120Swpaulstatic int exphy_detach		__P((device_t));
9850120Swpaul
9950120Swpaulstatic device_method_t exphy_methods[] = {
10050120Swpaul	/* device interface */
10150120Swpaul	DEVMETHOD(device_probe,		exphy_probe),
10250120Swpaul	DEVMETHOD(device_attach,	exphy_attach),
10350120Swpaul	DEVMETHOD(device_detach,	exphy_detach),
10450120Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10550120Swpaul	{ 0, 0 }
10650120Swpaul};
10750120Swpaul
10850120Swpaulstatic devclass_t exphy_devclass;
10950120Swpaul
11050120Swpaulstatic driver_t exphy_driver = {
11150120Swpaul	"xlphy",
11250120Swpaul	exphy_methods,
11350120Swpaul	sizeof(struct mii_softc)
11450120Swpaul};
11550120Swpaul
11650120SwpaulDRIVER_MODULE(xlphy, miibus, exphy_driver, exphy_devclass, 0, 0);
11750120Swpaul
11850120Swpaulint	exphy_service __P((struct mii_softc *, struct mii_data *, int));
11950120Swpaulvoid	exphy_reset __P((struct mii_softc *));
12050120Swpaul
12150120Swpaulstatic int exphy_probe(dev)
12250120Swpaul	device_t		dev;
12350120Swpaul{
12450120Swpaul	struct mii_attach_args *ma;
12550120Swpaul	device_t		parent;
12650120Swpaul
12750120Swpaul	ma = device_get_ivars(dev);
12850120Swpaul	parent = device_get_parent(device_get_parent(dev));
12950120Swpaul
13050120Swpaul	/*
13150120Swpaul	 * Argh, 3Com PHY reports oui == 0 model == 0!
13250120Swpaul	 */
13350577Swpaul	if ((MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
13450577Swpaul	    MII_MODEL(ma->mii_id2) != 0) &&
13550577Swpaul	    (MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_BROADCOM ||
13650577Swpaul	    MII_MODEL(ma->mii_id2) != MII_MODEL_BROADCOM_3c905Cphy))
13750120Swpaul		return (ENXIO);
13850120Swpaul
13950120Swpaul	/*
14050120Swpaul	 * Make sure the parent is an `ex'.
14150120Swpaul	 */
14250120Swpaul	if (strcmp(device_get_name(parent), "xl") != 0)
14350120Swpaul		return (ENXIO);
14450120Swpaul
14550577Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == 0)
14650577Swpaul		device_set_desc(dev, "3Com internal media interface");
14750577Swpaul	else
14850577Swpaul		device_set_desc(dev, MII_STR_BROADCOM_3c905Cphy);
14950120Swpaul
15050120Swpaul	return (0);
15150120Swpaul}
15250120Swpaul
15350120Swpaulstatic int exphy_attach(dev)
15450120Swpaul	device_t		dev;
15550120Swpaul{
15650120Swpaul	struct mii_softc *sc;
15750120Swpaul	struct mii_attach_args *ma;
15850120Swpaul	struct mii_data *mii;
15950120Swpaul
16050120Swpaul	sc = device_get_softc(dev);
16150120Swpaul	ma = device_get_ivars(dev);
16250120Swpaul	sc->mii_dev = device_get_parent(dev);
16350120Swpaul	mii = device_get_softc(sc->mii_dev);
16450120Swpaul
16550120Swpaul	/*
16650120Swpaul	 * The 3Com PHY can never be isolated, so never allow non-zero
16750120Swpaul	 * instances!
16850120Swpaul	 */
16950120Swpaul	if (mii->mii_instance != 0) {
17050120Swpaul		device_printf(dev, "ignoring this PHY, non-zero instance\n");
17150120Swpaul		return(ENXIO);
17250120Swpaul	}
17350120Swpaul
17450758Swpaul	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
17550758Swpaul
17650758Swpaul	sc->mii_inst = mii->mii_instance;
17750758Swpaul	sc->mii_phy = ma->mii_phyno;
17850758Swpaul	sc->mii_service = exphy_service;
17950758Swpaul	sc->mii_pdata = mii;
18050120Swpaul	mii->mii_instance++;
18150120Swpaul
18250120Swpaul	sc->mii_flags |= MIIF_NOISOLATE;
18350120Swpaul
18450120Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
18550120Swpaul
18650120Swpaul#if 0 /* See above. */
18750120Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
18850120Swpaul	    BMCR_ISO);
18950120Swpaul#endif
19050120Swpaul
19150120Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
19250120Swpaul	    BMCR_LOOP|BMCR_S100);
19350120Swpaul
19450120Swpaul	exphy_reset(sc);
19550120Swpaul
19650120Swpaul	sc->mii_capabilities =
19750120Swpaul	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
19850120Swpaul	device_printf(dev, " ");
19950120Swpaul	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
20050120Swpaul		printf("no media present");
20150120Swpaul	else
20250120Swpaul		mii_add_media(mii, sc->mii_capabilities,
20350120Swpaul		    sc->mii_inst);
20450120Swpaul	printf("\n");
20550120Swpaul#undef ADD
20650120Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
20750120Swpaul	return(0);
20850120Swpaul}
20950120Swpaul
21050120Swpaulstatic int exphy_detach(dev)
21150120Swpaul	device_t		dev;
21250120Swpaul{
21350120Swpaul	struct mii_softc *sc;
21450120Swpaul	struct mii_data *mii;
21550120Swpaul
21650120Swpaul	sc = device_get_softc(dev);
21750120Swpaul	mii = device_get_softc(device_get_parent(dev));
21850120Swpaul	sc->mii_dev = NULL;
21950120Swpaul	LIST_REMOVE(sc, mii_list);
22050120Swpaul
22150120Swpaul	return(0);
22250120Swpaul}
22350120Swpaul
22450120Swpaulint
22550120Swpaulexphy_service(sc, mii, cmd)
22650120Swpaul	struct mii_softc *sc;
22750120Swpaul	struct mii_data *mii;
22850120Swpaul	int cmd;
22950120Swpaul{
23050120Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
23150120Swpaul
23250120Swpaul	/*
23350120Swpaul	 * We can't isolate the 3Com PHY, so it has to be the only one!
23450120Swpaul	 */
23550120Swpaul	if (IFM_INST(ife->ifm_media) != sc->mii_inst)
23650120Swpaul		panic("exphy_service: can't isolate 3Com PHY");
23750120Swpaul
23850120Swpaul	switch (cmd) {
23950120Swpaul	case MII_POLLSTAT:
24050120Swpaul		break;
24150120Swpaul
24250120Swpaul	case MII_MEDIACHG:
24350120Swpaul		/*
24450120Swpaul		 * If the interface is not up, don't do anything.
24550120Swpaul		 */
24650120Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
24750120Swpaul			break;
24850120Swpaul
24950120Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
25050120Swpaul		case IFM_AUTO:
25150120Swpaul			/*
25250120Swpaul			 * If we're already in auto mode, just return.
25350120Swpaul			 */
25450120Swpaul			if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
25550120Swpaul				return (0);
25650120Swpaul			(void) mii_phy_auto(sc, 1);
25750120Swpaul			break;
25850120Swpaul		case IFM_100_T4:
25950120Swpaul			/*
26050120Swpaul			 * XXX Not supported as a manual setting right now.
26150120Swpaul			 */
26250120Swpaul			return (EINVAL);
26350120Swpaul		default:
26450120Swpaul			/*
26550120Swpaul			 * BMCR data is stored in the ifmedia entry.
26650120Swpaul			 */
26750120Swpaul			PHY_WRITE(sc, MII_ANAR,
26850120Swpaul			    mii_anar(ife->ifm_media));
26950120Swpaul			PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
27050120Swpaul		}
27150120Swpaul		break;
27250120Swpaul
27350120Swpaul	case MII_TICK:
27450120Swpaul		/*
27550120Swpaul		 * Only used for autonegotiation.
27650120Swpaul		 */
27750120Swpaul		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
27850120Swpaul			return (0);
27950120Swpaul
28050120Swpaul		/*
28150120Swpaul		 * Is the interface even up?
28250120Swpaul		 */
28350120Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
28450120Swpaul			return (0);
28550120Swpaul
28650120Swpaul		/*
28750120Swpaul		 * The 3Com PHY's autonegotiation doesn't need to be
28850120Swpaul		 * kicked; it continues in the background.
28950120Swpaul		 */
29050120Swpaul		break;
29150120Swpaul	}
29250120Swpaul
29350120Swpaul	/* Update the media status. */
29450120Swpaul	ukphy_status(sc);
29550120Swpaul
29650120Swpaul	/* Callback if something changed. */
29750120Swpaul	if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
29850120Swpaul		MIIBUS_STATCHG(sc->mii_dev);
29950120Swpaul		sc->mii_active = mii->mii_media_active;
30050120Swpaul	}
30150120Swpaul	return (0);
30250120Swpaul}
30350120Swpaul
30450120Swpaulvoid
30550120Swpaulexphy_reset(sc)
30650120Swpaul	struct mii_softc *sc;
30750120Swpaul{
30850120Swpaul
30950120Swpaul	mii_phy_reset(sc);
31050120Swpaul
31150120Swpaul	/*
31250120Swpaul	 * XXX 3Com PHY doesn't set the BMCR properly after
31350120Swpaul	 * XXX reset, which breaks autonegotiation.
31450120Swpaul	 */
31550120Swpaul	PHY_WRITE(sc, MII_BMCR, BMCR_S100|BMCR_AUTOEN|BMCR_FDX);
31650120Swpaul}
317