brgphy.c revision 160078
1139749Simp/*-
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
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/mii/brgphy.c 160078 2006-07-03 08:01:27Z yongari $");
35119418Sobrien
3659477Swpaul/*
3759477Swpaul * Driver for the Broadcom BCR5400 1000baseTX PHY. Speed is always
3859477Swpaul * 1000mbps; all we need to negotiate here is full or half duplex.
3959477Swpaul */
4059477Swpaul
4159477Swpaul#include <sys/param.h>
4259477Swpaul#include <sys/systm.h>
4359477Swpaul#include <sys/kernel.h>
44129876Sphk#include <sys/module.h>
4559477Swpaul#include <sys/socket.h>
4659477Swpaul#include <sys/bus.h>
4759477Swpaul
4859477Swpaul
4959477Swpaul#include <net/if.h>
50157642Sps#include <net/ethernet.h>
5159477Swpaul#include <net/if_media.h>
5259477Swpaul
5359477Swpaul#include <dev/mii/mii.h>
5459477Swpaul#include <dev/mii/miivar.h>
55109514Sobrien#include "miidevs.h"
5659477Swpaul
5759477Swpaul#include <dev/mii/brgphyreg.h>
58117659Swpaul#include <net/if_arp.h>
59117659Swpaul#include <machine/bus.h>
60117659Swpaul#include <dev/bge/if_bgereg.h>
61157642Sps#include <dev/bce/if_bcereg.h>
6259477Swpaul
63119285Simp#include <dev/pci/pcireg.h>
64119285Simp#include <dev/pci/pcivar.h>
65118814Swpaul
6659477Swpaul#include "miibus_if.h"
6759477Swpaul
68105135Salfredstatic int brgphy_probe(device_t);
69105135Salfredstatic int brgphy_attach(device_t);
7059477Swpaul
7159477Swpaulstatic device_method_t brgphy_methods[] = {
7259477Swpaul	/* device interface */
7359477Swpaul	DEVMETHOD(device_probe,		brgphy_probe),
7459477Swpaul	DEVMETHOD(device_attach,	brgphy_attach),
7595722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
7659477Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
7759477Swpaul	{ 0, 0 }
7859477Swpaul};
7959477Swpaul
8059477Swpaulstatic devclass_t brgphy_devclass;
8159477Swpaul
8259477Swpaulstatic driver_t brgphy_driver = {
8359477Swpaul	"brgphy",
8459477Swpaul	brgphy_methods,
8559477Swpaul	sizeof(struct mii_softc)
8659477Swpaul};
8759477Swpaul
8859477SwpaulDRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
8959477Swpaul
9084145Sjlemonstatic int	brgphy_service(struct mii_softc *, struct mii_data *, int);
9184145Sjlemonstatic void	brgphy_status(struct mii_softc *);
9296026Sphkstatic int	brgphy_mii_phy_auto(struct mii_softc *);
93114590Spsstatic void	brgphy_reset(struct mii_softc *);
94114590Spsstatic void	brgphy_loop(struct mii_softc *);
95114590Spsstatic void	bcm5401_load_dspcode(struct mii_softc *);
96114590Spsstatic void	bcm5411_load_dspcode(struct mii_softc *);
97114590Spsstatic void	bcm5703_load_dspcode(struct mii_softc *);
98135772Spsstatic void	bcm5750_load_dspcode(struct mii_softc *);
99114590Spsstatic int	brgphy_mii_model;
10059477Swpaul
101160078Syongaristatic const struct mii_phydesc brgphys[] = {
102160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5400),
103160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5401),
104160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5411),
105160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5701),
106160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5703),
107160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5704),
108160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5705),
109160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5750),
110160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5714),
111160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5780),
112160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5706C),
113160078Syongari	MII_PHY_DESC(xxBROADCOM, BCM5708C),
114160078Syongari	MII_PHY_END
115160078Syongari};
116160078Syongari
117105135Salfredstatic int
118150763Simpbrgphy_probe(device_t dev)
11959477Swpaul{
12059477Swpaul	struct mii_attach_args *ma;
121160078Syongari	const struct mii_phydesc *mpd;
12259477Swpaul
12359477Swpaul	ma = device_get_ivars(dev);
124160078Syongari	mpd = mii_phy_match(ma, brgphys);
125160078Syongari	if (mpd != NULL) {
126160078Syongari		device_set_desc(dev, mpd->mpd_name);
127160076Syongari		return (BUS_PROBE_DEFAULT);
128157642Sps	}
129157642Sps
130160078Syongari	return (ENXIO);
13159477Swpaul}
13259477Swpaul
133105135Salfredstatic int
134150763Simpbrgphy_attach(device_t dev)
13559477Swpaul{
13659477Swpaul	struct mii_softc *sc;
13759477Swpaul	struct mii_attach_args *ma;
13859477Swpaul	struct mii_data *mii;
13959477Swpaul	const char *sep = "";
140157642Sps	struct bge_softc *bge_sc = NULL;
141157642Sps	struct bce_softc *bce_sc = NULL;
142118814Swpaul	int fast_ether_only = FALSE;
14359477Swpaul
14459477Swpaul	sc = device_get_softc(dev);
14559477Swpaul	ma = device_get_ivars(dev);
14659477Swpaul	sc->mii_dev = device_get_parent(dev);
14759477Swpaul	mii = device_get_softc(sc->mii_dev);
14859477Swpaul	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
14959477Swpaul
15059477Swpaul	sc->mii_inst = mii->mii_instance;
15159477Swpaul	sc->mii_phy = ma->mii_phyno;
15259477Swpaul	sc->mii_service = brgphy_service;
15359477Swpaul	sc->mii_pdata = mii;
15459477Swpaul
15559477Swpaul	sc->mii_flags |= MIIF_NOISOLATE;
15659477Swpaul	mii->mii_instance++;
15759477Swpaul
15859477Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
15959477Swpaul#define PRINT(s)	printf("%s%s", sep, s); sep = ", "
16059477Swpaul
16159477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
16259477Swpaul	    BMCR_ISO);
16359477Swpaul#if 0
16459477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
16559477Swpaul	    BMCR_LOOP|BMCR_S100);
16659477Swpaul#endif
16759477Swpaul
168114590Sps	brgphy_mii_model = MII_MODEL(ma->mii_id2);
169114590Sps	brgphy_reset(sc);
17059477Swpaul
17183029Swpaul
17295667Sphk	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
17395667Sphk	sc->mii_capabilities &= ~BMSR_ANEG;
17459477Swpaul	device_printf(dev, " ");
17595667Sphk	mii_add_media(sc);
176118814Swpaul
177157642Sps	/* Find the driver associated with this PHY. */
178157642Sps	if (strcmp(mii->mii_ifp->if_dname, "bge") == 0)	{
179157642Sps 		bge_sc = mii->mii_ifp->if_softc;
180157642Sps	} else if (strcmp(mii->mii_ifp->if_dname, "bce") == 0) {
181157642Sps		bce_sc = mii->mii_ifp->if_softc;
182157642Sps	}
183157642Sps
184118814Swpaul	/* The 590x chips are 10/100 only. */
185121816Sbrooks	if (strcmp(mii->mii_ifp->if_dname, "bge") == 0 &&
186118814Swpaul	    pci_get_vendor(bge_sc->bge_dev) == BCOM_VENDORID &&
187118814Swpaul	    (pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5901 ||
188118814Swpaul	    pci_get_device(bge_sc->bge_dev) == BCOM_DEVICEID_BCM5901A2))
189118814Swpaul		fast_ether_only = TRUE;
190118814Swpaul
191118814Swpaul	if (fast_ether_only == FALSE) {
192118814Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0,
193118814Swpaul		    sc->mii_inst), BRGPHY_BMCR_FDX);
194118814Swpaul		PRINT(", 1000baseTX");
195118814Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T,
196118814Swpaul		    IFM_FDX, sc->mii_inst), 0);
197118814Swpaul		PRINT("1000baseTX-FDX");
198118814Swpaul	}
199118814Swpaul
20059477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
20159477Swpaul	PRINT("auto");
20259477Swpaul
20359477Swpaul	printf("\n");
20459477Swpaul#undef ADD
20559477Swpaul#undef PRINT
20659477Swpaul
20759477Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
20859477Swpaul	return(0);
20959477Swpaul}
21059477Swpaul
21184145Sjlemonstatic int
212150763Simpbrgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
21359477Swpaul{
21459477Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
215114590Sps	int reg, speed, gig;
21659477Swpaul
21759477Swpaul	switch (cmd) {
21859477Swpaul	case MII_POLLSTAT:
21959477Swpaul		/*
22059477Swpaul		 * If we're not polling our PHY instance, just return.
22159477Swpaul		 */
22259477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
22359477Swpaul			return (0);
22459477Swpaul		break;
22559477Swpaul
22659477Swpaul	case MII_MEDIACHG:
22759477Swpaul		/*
22859477Swpaul		 * If the media indicates a different PHY instance,
22959477Swpaul		 * isolate ourselves.
23059477Swpaul		 */
23159477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
23259477Swpaul			reg = PHY_READ(sc, MII_BMCR);
23359477Swpaul			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
23459477Swpaul			return (0);
23559477Swpaul		}
23659477Swpaul
23759477Swpaul		/*
23859477Swpaul		 * If the interface is not up, don't do anything.
23959477Swpaul		 */
24059477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
24159477Swpaul			break;
24259477Swpaul
243114590Sps		brgphy_reset(sc);	/* XXX hardware bug work-around */
24459477Swpaul
24559477Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
24659477Swpaul		case IFM_AUTO:
24759477Swpaul#ifdef foo
24859477Swpaul			/*
24959477Swpaul			 * If we're already in auto mode, just return.
25059477Swpaul			 */
25159477Swpaul			if (PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_AUTOEN)
25259477Swpaul				return (0);
25359477Swpaul#endif
25496026Sphk			(void) brgphy_mii_phy_auto(sc);
25559477Swpaul			break;
25695673Sphk		case IFM_1000_T:
25783029Swpaul			speed = BRGPHY_S1000;
25883029Swpaul			goto setit;
25983029Swpaul		case IFM_100_TX:
26083029Swpaul			speed = BRGPHY_S100;
26183029Swpaul			goto setit;
26283029Swpaul		case IFM_10_T:
26383029Swpaul			speed = BRGPHY_S10;
26483029Swpaulsetit:
265114590Sps			brgphy_loop(sc);
26659477Swpaul			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
267114590Sps				speed |= BRGPHY_BMCR_FDX;
268114590Sps				gig = BRGPHY_1000CTL_AFD;
26959477Swpaul			} else {
270114590Sps				gig = BRGPHY_1000CTL_AHD;
27159477Swpaul			}
272114590Sps
273114590Sps			PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
274114590Sps			PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
27559477Swpaul			PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
27659477Swpaul
277114590Sps			if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
27883029Swpaul				break;
27983029Swpaul
280114590Sps			PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
281114590Sps			PHY_WRITE(sc, BRGPHY_MII_BMCR,
282114590Sps			    speed|BRGPHY_BMCR_AUTOEN|BRGPHY_BMCR_STARTNEG);
283114590Sps
284114590Sps			if (brgphy_mii_model != MII_MODEL_xxBROADCOM_BCM5701)
285114590Sps				break;
286114590Sps
28759477Swpaul			/*
28859477Swpaul			 * When settning the link manually, one side must
28959477Swpaul			 * be the master and the other the slave. However
29059477Swpaul			 * ifmedia doesn't give us a good way to specify
29159477Swpaul			 * this, so we fake it by using one of the LINK
29259477Swpaul			 * flags. If LINK0 is set, we program the PHY to
29359477Swpaul			 * be a master, otherwise it's a slave.
29459477Swpaul			 */
29559477Swpaul			if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
29659477Swpaul				PHY_WRITE(sc, BRGPHY_MII_1000CTL,
297114590Sps				    gig|BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC);
29859477Swpaul			} else {
29959477Swpaul				PHY_WRITE(sc, BRGPHY_MII_1000CTL,
300114590Sps				    gig|BRGPHY_1000CTL_MSE);
30159477Swpaul			}
30259477Swpaul			break;
30383597Swpaul#ifdef foo
30483597Swpaul		case IFM_NONE:
30583597Swpaul			PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
30683597Swpaul			break;
30783597Swpaul#endif
30859477Swpaul		case IFM_100_T4:
30959477Swpaul		default:
31059477Swpaul			return (EINVAL);
31159477Swpaul		}
31259477Swpaul		break;
31359477Swpaul
31459477Swpaul	case MII_TICK:
31559477Swpaul		/*
31659477Swpaul		 * If we're not currently selected, just return.
31759477Swpaul		 */
31859477Swpaul		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
31959477Swpaul			return (0);
32059477Swpaul
32159477Swpaul		/*
32259477Swpaul		 * Is the interface even up?
32359477Swpaul		 */
32459477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
32559477Swpaul			return (0);
32659477Swpaul
32759477Swpaul		/*
32884145Sjlemon		 * Only used for autonegotiation.
32959477Swpaul		 */
33084145Sjlemon		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
33184145Sjlemon			break;
33259477Swpaul
33359477Swpaul		/*
33459477Swpaul		 * Check to see if we have link.  If we do, we don't
33559477Swpaul		 * need to restart the autonegotiation process.  Read
33659477Swpaul		 * the BMSR twice in case it's latched.
33759477Swpaul		 */
33859477Swpaul		reg = PHY_READ(sc, BRGPHY_MII_AUXSTS);
33959477Swpaul		if (reg & BRGPHY_AUXSTS_LINK)
34059477Swpaul			break;
34159477Swpaul
34284145Sjlemon		/*
34384145Sjlemon		 * Only retry autonegotiation every 5 seconds.
34484145Sjlemon		 */
345128870Sandre		if (++sc->mii_ticks <= 5)
346128870Sandre			break;
34784145Sjlemon
34884145Sjlemon		sc->mii_ticks = 0;
34996026Sphk		brgphy_mii_phy_auto(sc);
350153234Soleg		break;
35159477Swpaul	}
35259477Swpaul
35359477Swpaul	/* Update the media status. */
35459477Swpaul	brgphy_status(sc);
35559477Swpaul
356114628Sps	/*
357114628Sps	 * Callback if something changed. Note that we need to poke
358114628Sps	 * the DSP on the Broadcom PHYs if the media changes.
359114628Sps	 *
360114628Sps	 */
361114628Sps	if (sc->mii_media_active != mii->mii_media_active ||
362114628Sps	    sc->mii_media_status != mii->mii_media_status ||
363114628Sps	    cmd == MII_MEDIACHG) {
364114628Sps		switch (brgphy_mii_model) {
365151370Sgrehan		case MII_MODEL_xxBROADCOM_BCM5400:
366114628Sps		case MII_MODEL_xxBROADCOM_BCM5401:
367114628Sps			bcm5401_load_dspcode(sc);
368114628Sps			break;
369114628Sps		case MII_MODEL_xxBROADCOM_BCM5411:
370114628Sps			bcm5411_load_dspcode(sc);
371114628Sps			break;
372114628Sps		}
373114628Sps	}
374128870Sandre	mii_phy_update(sc, cmd);
37559477Swpaul	return (0);
37659477Swpaul}
37759477Swpaul
37884145Sjlemonstatic void
379150763Simpbrgphy_status(struct mii_softc *sc)
38059477Swpaul{
38159477Swpaul	struct mii_data *mii = sc->mii_pdata;
38283029Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
38383029Swpaul	int bmsr, bmcr;
38459477Swpaul
38559477Swpaul	mii->mii_media_status = IFM_AVALID;
38659477Swpaul	mii->mii_media_active = IFM_ETHER;
38759477Swpaul
38859477Swpaul	bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
38959477Swpaul	if (PHY_READ(sc, BRGPHY_MII_AUXSTS) & BRGPHY_AUXSTS_LINK)
39059477Swpaul		mii->mii_media_status |= IFM_ACTIVE;
39159477Swpaul
39259477Swpaul	bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
39359477Swpaul
39459477Swpaul	if (bmcr & BRGPHY_BMCR_LOOP)
39559477Swpaul		mii->mii_media_active |= IFM_LOOP;
39659477Swpaul
39759477Swpaul	if (bmcr & BRGPHY_BMCR_AUTOEN) {
39859477Swpaul		if ((bmsr & BRGPHY_BMSR_ACOMP) == 0) {
39959477Swpaul			/* Erg, still trying, I guess... */
40059477Swpaul			mii->mii_media_active |= IFM_NONE;
40159477Swpaul			return;
40259477Swpaul		}
40359477Swpaul
40483029Swpaul		switch (PHY_READ(sc, BRGPHY_MII_AUXSTS) &
40583029Swpaul		    BRGPHY_AUXSTS_AN_RES) {
40683029Swpaul		case BRGPHY_RES_1000FD:
40795673Sphk			mii->mii_media_active |= IFM_1000_T | IFM_FDX;
40883029Swpaul			break;
40983029Swpaul		case BRGPHY_RES_1000HD:
41095673Sphk			mii->mii_media_active |= IFM_1000_T | IFM_HDX;
41183029Swpaul			break;
41283029Swpaul		case BRGPHY_RES_100FD:
41383029Swpaul			mii->mii_media_active |= IFM_100_TX | IFM_FDX;
41483029Swpaul			break;
41583029Swpaul		case BRGPHY_RES_100T4:
41683029Swpaul			mii->mii_media_active |= IFM_100_T4;
41783029Swpaul			break;
41883029Swpaul		case BRGPHY_RES_100HD:
41983029Swpaul			mii->mii_media_active |= IFM_100_TX | IFM_HDX;
42083029Swpaul			break;
42183029Swpaul		case BRGPHY_RES_10FD:
42283029Swpaul			mii->mii_media_active |= IFM_10_T | IFM_FDX;
42383029Swpaul			break;
42483029Swpaul		case BRGPHY_RES_10HD:
42583029Swpaul			mii->mii_media_active |= IFM_10_T | IFM_HDX;
42683029Swpaul			break;
42783029Swpaul		default:
42883029Swpaul			mii->mii_media_active |= IFM_NONE;
42983029Swpaul			break;
43083029Swpaul		}
43159477Swpaul		return;
43259477Swpaul	}
43359477Swpaul
43483029Swpaul	mii->mii_media_active = ife->ifm_media;
43559477Swpaul
43659477Swpaul	return;
43759477Swpaul}
43859477Swpaul
43959477Swpaul
44059477Swpaulstatic int
441150763Simpbrgphy_mii_phy_auto(struct mii_softc *mii)
44259477Swpaul{
44396026Sphk	int ktcr = 0;
44459477Swpaul
445114590Sps	brgphy_loop(mii);
446114590Sps	brgphy_reset(mii);
447114590Sps	ktcr = BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD;
448114590Sps	if (brgphy_mii_model == MII_MODEL_xxBROADCOM_BCM5701)
449114590Sps		ktcr |= BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC;
450114590Sps	PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr);
45196026Sphk	ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL);
45296026Sphk	DELAY(1000);
45396026Sphk	PHY_WRITE(mii, BRGPHY_MII_ANAR,
45496026Sphk	    BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA);
45596026Sphk	DELAY(1000);
45696026Sphk	PHY_WRITE(mii, BRGPHY_MII_BMCR,
45796026Sphk	    BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
45896026Sphk	PHY_WRITE(mii, BRGPHY_MII_IMR, 0xFF00);
45959477Swpaul	return (EJUSTRETURN);
46059477Swpaul}
461114590Sps
462114590Spsstatic void
463114590Spsbrgphy_loop(struct mii_softc *sc)
464114590Sps{
465114590Sps	u_int32_t bmsr;
466114590Sps	int i;
467114590Sps
468114590Sps	PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
469114590Sps	for (i = 0; i < 15000; i++) {
470114590Sps		bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
471114590Sps		if (!(bmsr & BRGPHY_BMSR_LINK)) {
472114590Sps#if 0
473114590Sps			device_printf(sc->mii_dev, "looped %d\n", i);
474114590Sps#endif
475114590Sps			break;
476114590Sps		}
477114590Sps		DELAY(10);
478114590Sps	}
479114590Sps}
480114590Sps
481114590Sps/* Turn off tap power management on 5401. */
482114590Spsstatic void
483114590Spsbcm5401_load_dspcode(struct mii_softc *sc)
484114590Sps{
485114590Sps	static const struct {
486114590Sps		int		reg;
487114590Sps		uint16_t	val;
488114590Sps	} dspcode[] = {
489114590Sps		{ BRGPHY_MII_AUXCTL,		0x0c20 },
490114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0012 },
491114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1804 },
492114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x0013 },
493114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x1204 },
494114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
495114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0132 },
496114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x8006 },
497114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0232 },
498114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
499114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x0a20 },
500114590Sps		{ 0,				0 },
501114590Sps	};
502114590Sps	int i;
503114590Sps
504114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
505114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
506114590Sps	DELAY(40);
507114590Sps}
508114590Sps
509114590Spsstatic void
510114590Spsbcm5411_load_dspcode(struct mii_softc *sc)
511114590Sps{
512114590Sps	static const struct {
513114590Sps		int		reg;
514114590Sps		uint16_t	val;
515114590Sps	} dspcode[] = {
516114590Sps		{ 0x1c,				0x8c23 },
517114590Sps		{ 0x1c,				0x8ca3 },
518114590Sps		{ 0x1c,				0x8c23 },
519114590Sps		{ 0,				0 },
520114590Sps	};
521114590Sps	int i;
522114590Sps
523114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
524114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
525114590Sps}
526114590Sps
527114590Spsstatic void
528114590Spsbcm5703_load_dspcode(struct mii_softc *sc)
529114590Sps{
530114590Sps	static const struct {
531114590Sps		int		reg;
532114590Sps		uint16_t	val;
533114590Sps	} dspcode[] = {
534114590Sps		{ BRGPHY_MII_AUXCTL,		0x0c00 },
535114590Sps		{ BRGPHY_MII_DSP_ADDR_REG,	0x201f },
536114590Sps		{ BRGPHY_MII_DSP_RW_PORT,	0x2aaa },
537114590Sps		{ 0,				0 },
538114590Sps	};
539114590Sps	int i;
540114590Sps
541114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
542114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
543114590Sps}
544114590Sps
545114590Spsstatic void
546114590Spsbcm5704_load_dspcode(struct mii_softc *sc)
547114590Sps{
548114590Sps	static const struct {
549114590Sps		int		reg;
550114590Sps		u_int16_t	val;
551114590Sps	} dspcode[] = {
552114590Sps		{ 0x1c,				0x8d68 },
553114590Sps		{ 0x1c,				0x8d68 },
554114590Sps		{ 0,				0 },
555114590Sps	};
556114590Sps	int i;
557114590Sps
558114590Sps	for (i = 0; dspcode[i].reg != 0; i++)
559114590Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
560114590Sps}
561114590Sps
562114590Spsstatic void
563135772Spsbcm5750_load_dspcode(struct mii_softc *sc)
564135772Sps{
565135772Sps	static const struct {
566135772Sps		int		reg;
567135772Sps		u_int16_t	val;
568135772Sps	} dspcode[] = {
569135772Sps		{ 0x18,				0x0c00 },
570135772Sps		{ 0x17,				0x000a },
571135772Sps		{ 0x15,				0x310b },
572135772Sps		{ 0x17,				0x201f },
573135772Sps		{ 0x15,				0x9506 },
574135772Sps		{ 0x17,				0x401f },
575135772Sps		{ 0x15,				0x14e2 },
576135772Sps		{ 0x18,				0x0400 },
577135772Sps		{ 0,				0 },
578135772Sps	};
579135772Sps	int i;
580135772Sps
581135772Sps	for (i = 0; dspcode[i].reg != 0; i++)
582135772Sps		PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
583135772Sps}
584135772Sps
585135772Spsstatic void
586114590Spsbrgphy_reset(struct mii_softc *sc)
587114590Sps{
588114590Sps	u_int32_t	val;
589117659Swpaul	struct ifnet	*ifp;
590157642Sps	struct bge_softc	*bge_sc = NULL;
591157642Sps	struct bce_softc	*bce_sc = NULL;
592114590Sps
593114590Sps	mii_phy_reset(sc);
594114590Sps
595114590Sps	switch (brgphy_mii_model) {
596151370Sgrehan	case MII_MODEL_xxBROADCOM_BCM5400:
597114590Sps	case MII_MODEL_xxBROADCOM_BCM5401:
598114590Sps		bcm5401_load_dspcode(sc);
599114590Sps		break;
600114590Sps	case MII_MODEL_xxBROADCOM_BCM5411:
601114590Sps		bcm5411_load_dspcode(sc);
602114590Sps		break;
603114590Sps	case MII_MODEL_xxBROADCOM_BCM5703:
604114590Sps		bcm5703_load_dspcode(sc);
605114590Sps		break;
606114590Sps	case MII_MODEL_xxBROADCOM_BCM5704:
607114590Sps		bcm5704_load_dspcode(sc);
608114590Sps		break;
609135772Sps	case MII_MODEL_xxBROADCOM_BCM5750:
610146413Sps	case MII_MODEL_xxBROADCOM_BCM5714:
611157041Soleg	case MII_MODEL_xxBROADCOM_BCM5780:
612157642Sps	case MII_MODEL_xxBROADCOM_BCM5706C:
613157642Sps	case MII_MODEL_xxBROADCOM_BCM5708C:
614135772Sps		bcm5750_load_dspcode(sc);
615135772Sps		break;
616114590Sps	}
617114590Sps
618117659Swpaul	ifp = sc->mii_pdata->mii_ifp;
619117659Swpaul
620157642Sps	/* Find the driver associated with this PHY. */
621157642Sps	if (strcmp(ifp->if_dname, "bge") == 0)	{
622157642Sps 		bge_sc = ifp->if_softc;
623157642Sps	} else if (strcmp(ifp->if_dname, "bce") == 0) {
624157642Sps		bce_sc = ifp->if_softc;
625157642Sps	}
626117659Swpaul
627157642Sps	/* Handle any NetXtreme/bge workarounds. */
628157642Sps	if (bge_sc) {
629157642Sps	 	/*
630157642Sps		 * Don't enable Ethernet@WireSpeed for the 5700 or the
631157642Sps		 * 5705 A1 and A2 chips. Make sure we only do this test
632157642Sps		 * on "bge" NICs, since other drivers may use this same
633157642Sps		 * PHY subdriver.
634157642Sps		 */
635157642Sps		if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5700 ||
636157642Sps		    bge_sc->bge_chipid == BGE_CHIPID_BCM5705_A1 ||
637157642Sps		    bge_sc->bge_chipid == BGE_CHIPID_BCM5705_A2)
638157642Sps			return;
639119157Sambrisko
640157642Sps		/* Enable Ethernet@WireSpeed. */
641157642Sps		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
642157642Sps		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
643157642Sps		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) | (1 << 4));
644157642Sps
645157642Sps		/* Enable Link LED on Dell boxes */
646157642Sps		if (bge_sc->bge_no_3_led) {
647157642Sps			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
648157642Sps		    	PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL)
649157642Sps			    & ~BRGPHY_PHY_EXTCTL_3_LED);
650157642Sps		}
651157642Sps	} else if (bce_sc) {
652157642Sps
653157642Sps		/* Set or clear jumbo frame settings in the PHY. */
654157642Sps		if (ifp->if_mtu > ETHER_MAX_LEN) {
655157642Sps			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
656157642Sps			val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
657157642Sps			PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
658157642Sps				val | BRGPHY_AUXCTL_LONG_PKT);
659157642Sps
660157642Sps			val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
661157642Sps			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
662157642Sps				val | BRGPHY_PHY_EXTCTL_HIGH_LA);
663157642Sps		} else {
664157642Sps			PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
665157642Sps			val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
666157642Sps			PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
667157642Sps				val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
668157642Sps
669157642Sps			val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
670157642Sps			PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
671157642Sps				val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
672157642Sps		}
673157642Sps
674157642Sps		/* Enable Ethernet@Wirespeed */
675157642Sps		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
676157642Sps		val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
677157642Sps		PHY_WRITE(sc, BRGPHY_MII_AUXCTL, (val | (1 << 15) | (1 << 4)));
678119157Sambrisko	}
679114590Sps}
680