brgphy.c revision 96026
159477Swpaul/*
259477Swpaul * Copyright (c) 2000
359477Swpaul *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
459477Swpaul *
559477Swpaul * Redistribution and use in source and binary forms, with or without
659477Swpaul * modification, are permitted provided that the following conditions
759477Swpaul * are met:
859477Swpaul * 1. Redistributions of source code must retain the above copyright
959477Swpaul *    notice, this list of conditions and the following disclaimer.
1059477Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1159477Swpaul *    notice, this list of conditions and the following disclaimer in the
1259477Swpaul *    documentation and/or other materials provided with the distribution.
1359477Swpaul * 3. All advertising materials mentioning features or use of this software
1459477Swpaul *    must display the following acknowledgement:
1559477Swpaul *	This product includes software developed by Bill Paul.
1659477Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1759477Swpaul *    may be used to endorse or promote products derived from this software
1859477Swpaul *    without specific prior written permission.
1959477Swpaul *
2059477Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2159477Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259477Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359477Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2459477Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2559477Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2659477Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2759477Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2859477Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2959477Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3059477Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3159477Swpaul *
3259477Swpaul * $FreeBSD: head/sys/dev/mii/brgphy.c 96026 2002-05-04 11:00:30Z phk $
3359477Swpaul */
3459477Swpaul
3559477Swpaul/*
3659477Swpaul * Driver for the Broadcom BCR5400 1000baseTX PHY. Speed is always
3759477Swpaul * 1000mbps; all we need to negotiate here is full or half duplex.
3859477Swpaul */
3959477Swpaul
4059477Swpaul#include <sys/param.h>
4159477Swpaul#include <sys/systm.h>
4259477Swpaul#include <sys/kernel.h>
4359477Swpaul#include <sys/malloc.h>
4459477Swpaul#include <sys/socket.h>
4559477Swpaul#include <sys/bus.h>
4659477Swpaul
4783029Swpaul#include <machine/clock.h>
4859477Swpaul
4959477Swpaul#include <net/if.h>
5059477Swpaul#include <net/if_media.h>
5159477Swpaul
5259477Swpaul#include <dev/mii/mii.h>
5359477Swpaul#include <dev/mii/miivar.h>
5459477Swpaul#include <dev/mii/miidevs.h>
5559477Swpaul
5659477Swpaul#include <dev/mii/brgphyreg.h>
5759477Swpaul
5859477Swpaul#include "miibus_if.h"
5959477Swpaul
6059477Swpaul#if !defined(lint)
6159477Swpaulstatic const char rcsid[] =
6259477Swpaul  "$FreeBSD: head/sys/dev/mii/brgphy.c 96026 2002-05-04 11:00:30Z phk $";
6359477Swpaul#endif
6459477Swpaul
6592739Salfredstatic int brgphy_probe		(device_t);
6692739Salfredstatic int brgphy_attach		(device_t);
6759477Swpaul
6859477Swpaulstatic device_method_t brgphy_methods[] = {
6959477Swpaul	/* device interface */
7059477Swpaul	DEVMETHOD(device_probe,		brgphy_probe),
7159477Swpaul	DEVMETHOD(device_attach,	brgphy_attach),
7295722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
7359477Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
7459477Swpaul	{ 0, 0 }
7559477Swpaul};
7659477Swpaul
7759477Swpaulstatic devclass_t brgphy_devclass;
7859477Swpaul
7959477Swpaulstatic driver_t brgphy_driver = {
8059477Swpaul	"brgphy",
8159477Swpaul	brgphy_methods,
8259477Swpaul	sizeof(struct mii_softc)
8359477Swpaul};
8459477Swpaul
8559477SwpaulDRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
8659477Swpaul
8784145Sjlemonstatic int	brgphy_service(struct mii_softc *, struct mii_data *, int);
8884145Sjlemonstatic void	brgphy_status(struct mii_softc *);
8996026Sphkstatic int	brgphy_mii_phy_auto(struct mii_softc *);
9059477Swpaul
9159477Swpaulstatic int brgphy_probe(dev)
9259477Swpaul	device_t		dev;
9359477Swpaul{
9459477Swpaul	struct mii_attach_args *ma;
9559477Swpaul
9659477Swpaul	ma = device_get_ivars(dev);
9759477Swpaul
9883029Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM &&
9983029Swpaul	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5400) {
10083029Swpaul		device_set_desc(dev, MII_STR_xxBROADCOM_BCM5400);
10183029Swpaul		return(0);
10283029Swpaul	}
10359477Swpaul
10483029Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM &&
10583029Swpaul	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5401) {
10683029Swpaul		device_set_desc(dev, MII_STR_xxBROADCOM_BCM5401);
10783029Swpaul		return(0);
10883029Swpaul	}
10959477Swpaul
11083029Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM &&
11183029Swpaul	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5411) {
11283029Swpaul		device_set_desc(dev, MII_STR_xxBROADCOM_BCM5411);
11383029Swpaul		return(0);
11483029Swpaul	}
11583029Swpaul
11692931Swpaul	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxBROADCOM &&
11792931Swpaul	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxBROADCOM_BCM5701) {
11892931Swpaul		device_set_desc(dev, MII_STR_xxBROADCOM_BCM5701);
11992931Swpaul		return(0);
12092931Swpaul	}
12192931Swpaul
12283029Swpaul	return(ENXIO);
12359477Swpaul}
12459477Swpaul
12559477Swpaulstatic int brgphy_attach(dev)
12659477Swpaul	device_t		dev;
12759477Swpaul{
12859477Swpaul	struct mii_softc *sc;
12959477Swpaul	struct mii_attach_args *ma;
13059477Swpaul	struct mii_data *mii;
13159477Swpaul	const char *sep = "";
13259477Swpaul
13359477Swpaul	sc = device_get_softc(dev);
13459477Swpaul	ma = device_get_ivars(dev);
13559477Swpaul	sc->mii_dev = device_get_parent(dev);
13659477Swpaul	mii = device_get_softc(sc->mii_dev);
13759477Swpaul	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
13859477Swpaul
13959477Swpaul	sc->mii_inst = mii->mii_instance;
14059477Swpaul	sc->mii_phy = ma->mii_phyno;
14159477Swpaul	sc->mii_service = brgphy_service;
14259477Swpaul	sc->mii_pdata = mii;
14359477Swpaul
14459477Swpaul	sc->mii_flags |= MIIF_NOISOLATE;
14559477Swpaul	mii->mii_instance++;
14659477Swpaul
14759477Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
14859477Swpaul#define PRINT(s)	printf("%s%s", sep, s); sep = ", "
14959477Swpaul
15059477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
15159477Swpaul	    BMCR_ISO);
15259477Swpaul#if 0
15359477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
15459477Swpaul	    BMCR_LOOP|BMCR_S100);
15559477Swpaul#endif
15659477Swpaul
15759477Swpaul	mii_phy_reset(sc);
15859477Swpaul
15983029Swpaul
16095667Sphk	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
16195667Sphk	sc->mii_capabilities &= ~BMSR_ANEG;
16259477Swpaul	device_printf(dev, " ");
16395667Sphk	mii_add_media(sc);
16495673Sphk	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
16559477Swpaul	    BRGPHY_BMCR_FDX);
16683029Swpaul	PRINT(", 1000baseTX");
16795673Sphk	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), 0);
16859477Swpaul	PRINT("1000baseTX-FDX");
16959477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
17059477Swpaul	PRINT("auto");
17159477Swpaul
17259477Swpaul	printf("\n");
17359477Swpaul#undef ADD
17459477Swpaul#undef PRINT
17559477Swpaul
17659477Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
17759477Swpaul	return(0);
17859477Swpaul}
17959477Swpaul
18084145Sjlemonstatic int
18159477Swpaulbrgphy_service(sc, mii, cmd)
18259477Swpaul	struct mii_softc *sc;
18359477Swpaul	struct mii_data *mii;
18459477Swpaul	int cmd;
18559477Swpaul{
18659477Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
18783029Swpaul	int reg, speed;
18859477Swpaul
18959477Swpaul	switch (cmd) {
19059477Swpaul	case MII_POLLSTAT:
19159477Swpaul		/*
19259477Swpaul		 * If we're not polling our PHY instance, just return.
19359477Swpaul		 */
19459477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
19559477Swpaul			return (0);
19659477Swpaul		break;
19759477Swpaul
19859477Swpaul	case MII_MEDIACHG:
19959477Swpaul		/*
20059477Swpaul		 * If the media indicates a different PHY instance,
20159477Swpaul		 * isolate ourselves.
20259477Swpaul		 */
20359477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
20459477Swpaul			reg = PHY_READ(sc, MII_BMCR);
20559477Swpaul			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
20659477Swpaul			return (0);
20759477Swpaul		}
20859477Swpaul
20959477Swpaul		/*
21059477Swpaul		 * If the interface is not up, don't do anything.
21159477Swpaul		 */
21259477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
21359477Swpaul			break;
21459477Swpaul
21559477Swpaul		PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
21659477Swpaul		    BRGPHY_PHY_EXTCTL_HIGH_LA|BRGPHY_PHY_EXTCTL_EN_LTR);
21759477Swpaul		PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
21859477Swpaul		    BRGPHY_AUXCTL_LONG_PKT|BRGPHY_AUXCTL_TX_TST);
21959477Swpaul		PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
22059477Swpaul
22159477Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
22259477Swpaul		case IFM_AUTO:
22359477Swpaul#ifdef foo
22459477Swpaul			/*
22559477Swpaul			 * If we're already in auto mode, just return.
22659477Swpaul			 */
22759477Swpaul			if (PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_AUTOEN)
22859477Swpaul				return (0);
22959477Swpaul#endif
23096026Sphk			(void) brgphy_mii_phy_auto(sc);
23159477Swpaul			break;
23295673Sphk		case IFM_1000_T:
23383029Swpaul			speed = BRGPHY_S1000;
23483029Swpaul			goto setit;
23583029Swpaul		case IFM_100_TX:
23683029Swpaul			speed = BRGPHY_S100;
23783029Swpaul			goto setit;
23883029Swpaul		case IFM_10_T:
23983029Swpaul			speed = BRGPHY_S10;
24083029Swpaulsetit:
24159477Swpaul			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
24259477Swpaul				PHY_WRITE(sc, BRGPHY_MII_BMCR,
24383029Swpaul				    BRGPHY_BMCR_FDX|speed);
24459477Swpaul			} else {
24583029Swpaul				PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
24659477Swpaul			}
24759477Swpaul			PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
24859477Swpaul
24995673Sphk			if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
25083029Swpaul				break;
25183029Swpaul
25259477Swpaul			/*
25359477Swpaul			 * When settning the link manually, one side must
25459477Swpaul			 * be the master and the other the slave. However
25559477Swpaul			 * ifmedia doesn't give us a good way to specify
25659477Swpaul			 * this, so we fake it by using one of the LINK
25759477Swpaul			 * flags. If LINK0 is set, we program the PHY to
25859477Swpaul			 * be a master, otherwise it's a slave.
25959477Swpaul			 */
26059477Swpaul			if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
26159477Swpaul				PHY_WRITE(sc, BRGPHY_MII_1000CTL,
26259477Swpaul				    BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC);
26359477Swpaul			} else {
26459477Swpaul				PHY_WRITE(sc, BRGPHY_MII_1000CTL,
26559477Swpaul				    BRGPHY_1000CTL_MSE);
26659477Swpaul			}
26759477Swpaul			break;
26883597Swpaul#ifdef foo
26983597Swpaul		case IFM_NONE:
27083597Swpaul			PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
27183597Swpaul			break;
27283597Swpaul#endif
27359477Swpaul		case IFM_100_T4:
27459477Swpaul		default:
27559477Swpaul			return (EINVAL);
27659477Swpaul		}
27759477Swpaul		break;
27859477Swpaul
27959477Swpaul	case MII_TICK:
28059477Swpaul		/*
28159477Swpaul		 * If we're not currently selected, just return.
28259477Swpaul		 */
28359477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
28459477Swpaul			return (0);
28559477Swpaul
28659477Swpaul		/*
28759477Swpaul		 * Is the interface even up?
28859477Swpaul		 */
28959477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
29059477Swpaul			return (0);
29159477Swpaul
29259477Swpaul		/*
29384145Sjlemon		 * Only used for autonegotiation.
29459477Swpaul		 */
29584145Sjlemon		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
29684145Sjlemon			break;
29759477Swpaul
29859477Swpaul		/*
29959477Swpaul		 * Check to see if we have link.  If we do, we don't
30059477Swpaul		 * need to restart the autonegotiation process.  Read
30159477Swpaul		 * the BMSR twice in case it's latched.
30259477Swpaul		 */
30359477Swpaul		reg = PHY_READ(sc, BRGPHY_MII_AUXSTS);
30459477Swpaul		if (reg & BRGPHY_AUXSTS_LINK)
30559477Swpaul			break;
30659477Swpaul
30784145Sjlemon		/*
30884145Sjlemon		 * Only retry autonegotiation every 5 seconds.
30984145Sjlemon		 */
31084145Sjlemon		if (++sc->mii_ticks != 5)
31184145Sjlemon			return (0);
31284145Sjlemon
31384145Sjlemon		sc->mii_ticks = 0;
31459477Swpaul		mii_phy_reset(sc);
31596026Sphk		brgphy_mii_phy_auto(sc);
31696026Sphk		return (0);
31759477Swpaul	}
31859477Swpaul
31959477Swpaul	/* Update the media status. */
32059477Swpaul	brgphy_status(sc);
32159477Swpaul
32259477Swpaul	/* Callback if something changed. */
32384145Sjlemon	mii_phy_update(sc, cmd);
32459477Swpaul	return (0);
32559477Swpaul}
32659477Swpaul
32784145Sjlemonstatic void
32859477Swpaulbrgphy_status(sc)
32959477Swpaul	struct mii_softc *sc;
33059477Swpaul{
33159477Swpaul	struct mii_data *mii = sc->mii_pdata;
33283029Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
33383029Swpaul	int bmsr, bmcr;
33459477Swpaul
33559477Swpaul	mii->mii_media_status = IFM_AVALID;
33659477Swpaul	mii->mii_media_active = IFM_ETHER;
33759477Swpaul
33859477Swpaul	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
33959477Swpaul	if (PHY_READ(sc, BRGPHY_MII_AUXSTS) & BRGPHY_AUXSTS_LINK)
34059477Swpaul		mii->mii_media_status |= IFM_ACTIVE;
34159477Swpaul
34259477Swpaul	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
34359477Swpaul
34459477Swpaul	if (bmcr & BRGPHY_BMCR_LOOP)
34559477Swpaul		mii->mii_media_active |= IFM_LOOP;
34659477Swpaul
34759477Swpaul	if (bmcr & BRGPHY_BMCR_AUTOEN) {
34859477Swpaul		if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) {
34959477Swpaul			/* Erg, still trying, I guess... */
35059477Swpaul			mii->mii_media_active |= IFM_NONE;
35159477Swpaul			return;
35259477Swpaul		}
35359477Swpaul
35483029Swpaul		switch (PHY_READ(sc, BRGPHY_MII_AUXSTS) &
35583029Swpaul		    BRGPHY_AUXSTS_AN_RES) {
35683029Swpaul		case BRGPHY_RES_1000FD:
35795673Sphk			mii->mii_media_active |= IFM_1000_T | IFM_FDX;
35883029Swpaul			break;
35983029Swpaul		case BRGPHY_RES_1000HD:
36095673Sphk			mii->mii_media_active |= IFM_1000_T | IFM_HDX;
36183029Swpaul			break;
36283029Swpaul		case BRGPHY_RES_100FD:
36383029Swpaul			mii->mii_media_active |= IFM_100_TX | IFM_FDX;
36483029Swpaul			break;
36583029Swpaul		case BRGPHY_RES_100T4:
36683029Swpaul			mii->mii_media_active |= IFM_100_T4;
36783029Swpaul			break;
36883029Swpaul		case BRGPHY_RES_100HD:
36983029Swpaul			mii->mii_media_active |= IFM_100_TX | IFM_HDX;
37083029Swpaul			break;
37183029Swpaul		case BRGPHY_RES_10FD:
37283029Swpaul			mii->mii_media_active |= IFM_10_T | IFM_FDX;
37383029Swpaul			break;
37483029Swpaul		case BRGPHY_RES_10HD:
37583029Swpaul			mii->mii_media_active |= IFM_10_T | IFM_HDX;
37683029Swpaul			break;
37783029Swpaul		default:
37883029Swpaul			mii->mii_media_active |= IFM_NONE;
37983029Swpaul			break;
38083029Swpaul		}
38159477Swpaul		return;
38259477Swpaul	}
38359477Swpaul
38483029Swpaul	mii->mii_media_active = ife->ifm_media;
38559477Swpaul
38659477Swpaul	return;
38759477Swpaul}
38859477Swpaul
38959477Swpaul
39059477Swpaulstatic int
39196026Sphkbrgphy_mii_phy_auto(mii)
39259477Swpaul	struct mii_softc *mii;
39359477Swpaul{
39496026Sphk	int ktcr = 0;
39559477Swpaul
39696026Sphk	mii_phy_reset(mii);
39796026Sphk	PHY_WRITE(mii, BRGPHY_MII_BMCR, 0);
39896026Sphk	DELAY(1000);
39996026Sphk	ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL);
40096026Sphk	PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr |
40196026Sphk	    BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD);
40296026Sphk	ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL);
40396026Sphk	DELAY(1000);
40496026Sphk	PHY_WRITE(mii, BRGPHY_MII_ANAR,
40596026Sphk	    BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA);
40696026Sphk	DELAY(1000);
40796026Sphk	PHY_WRITE(mii, BRGPHY_MII_BMCR,
40896026Sphk	    BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
40996026Sphk	PHY_WRITE(mii, BRGPHY_MII_IMR, 0xFF00);
41059477Swpaul	return (EJUSTRETURN);
41159477Swpaul}
412