xlphy.c revision 92739
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/socket.h>
7750120Swpaul#include <sys/module.h>
7850120Swpaul#include <sys/bus.h>
7950120Swpaul
8050120Swpaul#include <net/if.h>
8150120Swpaul#include <net/if_media.h>
8250120Swpaul
8350120Swpaul#include <dev/mii/mii.h>
8450120Swpaul#include <dev/mii/miivar.h>
8550120Swpaul#include <dev/mii/miidevs.h>
8650120Swpaul
8750120Swpaul#include "miibus_if.h"
8850120Swpaul
8950120Swpaul#if !defined(lint)
9050120Swpaulstatic const char rcsid[] =
9150758Swpaul  "$FreeBSD: head/sys/dev/mii/exphy.c 92739 2002-03-20 02:08:01Z alfred $";
9250120Swpaul#endif
9350120Swpaul
9492739Salfredstatic int exphy_probe		(device_t);
9592739Salfredstatic int exphy_attach		(device_t);
9692739Salfredstatic int exphy_detach		(device_t);
9750120Swpaul
9850120Swpaulstatic device_method_t exphy_methods[] = {
9950120Swpaul	/* device interface */
10050120Swpaul	DEVMETHOD(device_probe,		exphy_probe),
10150120Swpaul	DEVMETHOD(device_attach,	exphy_attach),
10250120Swpaul	DEVMETHOD(device_detach,	exphy_detach),
10350120Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10450120Swpaul	{ 0, 0 }
10550120Swpaul};
10650120Swpaul
10750120Swpaulstatic devclass_t exphy_devclass;
10850120Swpaul
10950120Swpaulstatic driver_t exphy_driver = {
11050120Swpaul	"xlphy",
11150120Swpaul	exphy_methods,
11250120Swpaul	sizeof(struct mii_softc)
11350120Swpaul};
11450120Swpaul
11550120SwpaulDRIVER_MODULE(xlphy, miibus, exphy_driver, exphy_devclass, 0, 0);
11650120Swpaul
11792739Salfredstatic int	exphy_service(struct mii_softc *, struct mii_data *, int);
11892739Salfredstatic void	exphy_reset(struct mii_softc *);
11950120Swpaul
12050120Swpaulstatic int exphy_probe(dev)
12150120Swpaul	device_t		dev;
12250120Swpaul{
12350120Swpaul	struct mii_attach_args *ma;
12450120Swpaul	device_t		parent;
12550120Swpaul
12650120Swpaul	ma = device_get_ivars(dev);
12750120Swpaul	parent = device_get_parent(device_get_parent(dev));
12850120Swpaul
12950120Swpaul	/*
13050120Swpaul	 * Argh, 3Com PHY reports oui == 0 model == 0!
13150120Swpaul	 */
13250577Swpaul	if ((MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
13350577Swpaul	    MII_MODEL(ma->mii_id2) != 0) &&
13450577Swpaul	    (MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_BROADCOM ||
13550577Swpaul	    MII_MODEL(ma->mii_id2) != MII_MODEL_BROADCOM_3c905Cphy))
13650120Swpaul		return (ENXIO);
13750120Swpaul
13850120Swpaul	/*
13950120Swpaul	 * Make sure the parent is an `ex'.
14050120Swpaul	 */
14150120Swpaul	if (strcmp(device_get_name(parent), "xl") != 0)
14250120Swpaul		return (ENXIO);
14350120Swpaul
14450577Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == 0)
14550577Swpaul		device_set_desc(dev, "3Com internal media interface");
14650577Swpaul	else
14750577Swpaul		device_set_desc(dev, MII_STR_BROADCOM_3c905Cphy);
14850120Swpaul
14950120Swpaul	return (0);
15050120Swpaul}
15150120Swpaul
15250120Swpaulstatic int exphy_attach(dev)
15350120Swpaul	device_t		dev;
15450120Swpaul{
15550120Swpaul	struct mii_softc *sc;
15650120Swpaul	struct mii_attach_args *ma;
15750120Swpaul	struct mii_data *mii;
15850120Swpaul
15950120Swpaul	sc = device_get_softc(dev);
16050120Swpaul	ma = device_get_ivars(dev);
16150120Swpaul	sc->mii_dev = device_get_parent(dev);
16250120Swpaul	mii = device_get_softc(sc->mii_dev);
16350120Swpaul
16450120Swpaul	/*
16550120Swpaul	 * The 3Com PHY can never be isolated, so never allow non-zero
16650120Swpaul	 * instances!
16750120Swpaul	 */
16850120Swpaul	if (mii->mii_instance != 0) {
16950120Swpaul		device_printf(dev, "ignoring this PHY, non-zero instance\n");
17050120Swpaul		return(ENXIO);
17150120Swpaul	}
17250120Swpaul
17350758Swpaul	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
17450758Swpaul
17550758Swpaul	sc->mii_inst = mii->mii_instance;
17650758Swpaul	sc->mii_phy = ma->mii_phyno;
17750758Swpaul	sc->mii_service = exphy_service;
17850758Swpaul	sc->mii_pdata = mii;
17950120Swpaul	mii->mii_instance++;
18050120Swpaul
18150120Swpaul	sc->mii_flags |= MIIF_NOISOLATE;
18250120Swpaul
18350120Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
18450120Swpaul
18550120Swpaul#if 0 /* See above. */
18650120Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
18750120Swpaul	    BMCR_ISO);
18850120Swpaul#endif
18950120Swpaul
19050120Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
19150120Swpaul	    BMCR_LOOP|BMCR_S100);
19250120Swpaul
19350120Swpaul	exphy_reset(sc);
19450120Swpaul
19550120Swpaul	sc->mii_capabilities =
19650120Swpaul	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
19750120Swpaul	device_printf(dev, " ");
19850120Swpaul	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
19950120Swpaul		printf("no media present");
20050120Swpaul	else
20150120Swpaul		mii_add_media(mii, sc->mii_capabilities,
20250120Swpaul		    sc->mii_inst);
20350120Swpaul	printf("\n");
20450120Swpaul#undef ADD
20550120Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
20650120Swpaul	return(0);
20750120Swpaul}
20850120Swpaul
20950120Swpaulstatic int exphy_detach(dev)
21050120Swpaul	device_t		dev;
21150120Swpaul{
21250120Swpaul	struct mii_softc *sc;
21350120Swpaul	struct mii_data *mii;
21450120Swpaul
21550120Swpaul	sc = device_get_softc(dev);
21650120Swpaul	mii = device_get_softc(device_get_parent(dev));
21769925Swpaul	mii_phy_auto_stop(sc);
21850120Swpaul	sc->mii_dev = NULL;
21950120Swpaul	LIST_REMOVE(sc, mii_list);
22050120Swpaul
22150120Swpaul	return(0);
22250120Swpaul}
22350120Swpaul
22484145Sjlemonstatic int
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		/*
27584145Sjlemon		 * Is the interface even up?
27650120Swpaul		 */
27784145Sjlemon		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
27850120Swpaul			return (0);
27950120Swpaul
28050120Swpaul		/*
28184145Sjlemon		 * Only used for autonegotiation.
28250120Swpaul		 */
28384145Sjlemon		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
28484145Sjlemon			break;
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. */
29784145Sjlemon	mii_phy_update(sc, cmd);
29850120Swpaul	return (0);
29950120Swpaul}
30050120Swpaul
30184145Sjlemonstatic void
30250120Swpaulexphy_reset(sc)
30350120Swpaul	struct mii_softc *sc;
30450120Swpaul{
30550120Swpaul
30650120Swpaul	mii_phy_reset(sc);
30750120Swpaul
30850120Swpaul	/*
30950120Swpaul	 * XXX 3Com PHY doesn't set the BMCR properly after
31050120Swpaul	 * XXX reset, which breaks autonegotiation.
31150120Swpaul	 */
31250120Swpaul	PHY_WRITE(sc, MII_BMCR, BMCR_S100|BMCR_AUTOEN|BMCR_FDX);
31350120Swpaul}
314