mii_physubr.c revision 95665
150120Swpaul/*	$NetBSD: mii_physubr.c,v 1.5 1999/08/03 19:41:49 drochner 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.
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 * Subroutines common to all PHYs.
4250120Swpaul */
4350120Swpaul
4450120Swpaul#include <sys/param.h>
4550120Swpaul#include <sys/systm.h>
4650120Swpaul#include <sys/kernel.h>
4750120Swpaul#include <sys/socket.h>
4850120Swpaul#include <sys/errno.h>
4950120Swpaul#include <sys/module.h>
5050120Swpaul#include <sys/bus.h>
5150120Swpaul
5250120Swpaul
5350120Swpaul#include <net/if.h>
5450120Swpaul#include <net/if_media.h>
5550120Swpaul
5650120Swpaul#include <dev/mii/mii.h>
5750120Swpaul#include <dev/mii/miivar.h>
5850120Swpaul
5950120Swpaul#include "miibus_if.h"
6050120Swpaul
6150120Swpaul#if !defined(lint)
6250120Swpaulstatic const char rcsid[] =
6350477Speter  "$FreeBSD: head/sys/dev/mii/mii_physubr.c 95665 2002-04-28 19:01:32Z phk $";
6450120Swpaul#endif
6550120Swpaul
6692739Salfredvoid	mii_phy_auto_timeout(void *);
6750120Swpaul
6850120Swpaulint
6950120Swpaulmii_phy_auto(mii, waitfor)
7050120Swpaul	struct mii_softc *mii;
7150120Swpaul	int waitfor;
7250120Swpaul{
7350120Swpaul	int bmsr, i;
7450120Swpaul
7550120Swpaul	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
7650120Swpaul		PHY_WRITE(mii, MII_ANAR,
7750120Swpaul		    BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA);
7850120Swpaul		PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
7950120Swpaul	}
8050120Swpaul
8150120Swpaul	if (waitfor) {
8250120Swpaul		/* Wait 500ms for it to complete. */
8350120Swpaul		for (i = 0; i < 500; i++) {
8450120Swpaul			if ((bmsr = PHY_READ(mii, MII_BMSR)) & BMSR_ACOMP)
8550120Swpaul				return (0);
8650120Swpaul			DELAY(1000);
8750120Swpaul#if 0
8850120Swpaul		if ((bmsr & BMSR_ACOMP) == 0)
8950120Swpaul			printf("%s: autonegotiation failed to complete\n",
9050120Swpaul			    mii->mii_dev.dv_xname);
9150120Swpaul#endif
9250120Swpaul		}
9350120Swpaul
9450120Swpaul		/*
9550120Swpaul		 * Don't need to worry about clearing MIIF_DOINGAUTO.
9650120Swpaul		 * If that's set, a timeout is pending, and it will
9750120Swpaul		 * clear the flag.
9850120Swpaul		 */
9950120Swpaul		return (EIO);
10050120Swpaul	}
10150120Swpaul
10250120Swpaul	/*
10350120Swpaul	 * Just let it finish asynchronously.  This is for the benefit of
10450120Swpaul	 * the tick handler driving autonegotiation.  Don't want 500ms
10550120Swpaul	 * delays all the time while the system is running!
10650120Swpaul	 */
10750120Swpaul	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
10850120Swpaul		mii->mii_flags |= MIIF_DOINGAUTO;
10969925Swpaul		mii->mii_auto_ch = timeout(mii_phy_auto_timeout, mii, hz >> 1);
11050120Swpaul	}
11150120Swpaul	return (EJUSTRETURN);
11250120Swpaul}
11350120Swpaul
11450120Swpaulvoid
11569925Swpaulmii_phy_auto_stop(sc)
11669925Swpaul	struct mii_softc *sc;
11769925Swpaul{
11869925Swpaul	if (sc->mii_flags & MIIF_DOINGAUTO) {
11969925Swpaul		sc->mii_flags &= ~MIIF_DOINGAUTO;
12069925Swpaul		untimeout(mii_phy_auto_timeout, sc, sc->mii_auto_ch);
12169925Swpaul	}
12269925Swpaul}
12369925Swpaul
12469925Swpaulvoid
12550120Swpaulmii_phy_auto_timeout(arg)
12650120Swpaul	void *arg;
12750120Swpaul{
12850120Swpaul	struct mii_softc *mii = arg;
12950120Swpaul	int s, bmsr;
13050120Swpaul
13150120Swpaul	s = splnet();
13250120Swpaul	mii->mii_flags &= ~MIIF_DOINGAUTO;
13350120Swpaul	bmsr = PHY_READ(mii, MII_BMSR);
13450120Swpaul#if 0
13550120Swpaul	if ((bmsr & BMSR_ACOMP) == 0)
13650120Swpaul		printf("%s: autonegotiation failed to complete\n",
13750120Swpaul		    sc->sc_dev.dv_xname);
13850120Swpaul#endif
13950120Swpaul
14050120Swpaul	/* Update the media status. */
14150120Swpaul	(void) (*mii->mii_service)(mii, mii->mii_pdata, MII_POLLSTAT);
14250120Swpaul	splx(s);
14350120Swpaul}
14450120Swpaul
14584140Sjlemonint
14684140Sjlemonmii_phy_tick(sc)
14784140Sjlemon	struct mii_softc *sc;
14884140Sjlemon{
14984140Sjlemon	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
15084140Sjlemon	struct ifnet *ifp = sc->mii_pdata->mii_ifp;
15184140Sjlemon	int reg;
15284140Sjlemon
15384140Sjlemon	/*
15484140Sjlemon	 * Is the interface even up?
15584140Sjlemon	 */
15684140Sjlemon	if ((ifp->if_flags & IFF_UP) == 0)
15784140Sjlemon		return (EJUSTRETURN);
15884140Sjlemon
15984140Sjlemon	/*
16084140Sjlemon	 * If we're not doing autonegotiation, we don't need to do
16184140Sjlemon	 * any extra work here.  However, we need to check the link
16284140Sjlemon	 * status so we can generate an announcement if the status
16384140Sjlemon	 * changes.
16484140Sjlemon	 */
16584140Sjlemon	if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
16684140Sjlemon		return (0);
16784140Sjlemon
16884140Sjlemon	/*
16984140Sjlemon	 * check for link.
17084140Sjlemon	 * Read the status register twice; BMSR_LINK is latch-low.
17184140Sjlemon	 */
17284140Sjlemon	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
17384140Sjlemon	if (reg & BMSR_LINK)
17484140Sjlemon		return (0);
17584140Sjlemon
17684140Sjlemon	/*
17784140Sjlemon	 * Only retry autonegotiation every 5 seconds.
17884140Sjlemon	 */
17984140Sjlemon	if (++sc->mii_ticks != 5)
18084140Sjlemon		return (EJUSTRETURN);
18184140Sjlemon
18284140Sjlemon	sc->mii_ticks = 0;
18384140Sjlemon	mii_phy_reset(sc);
18484140Sjlemon	if (mii_phy_auto(sc, 0) == EJUSTRETURN)
18584140Sjlemon		return (EJUSTRETURN);
18684140Sjlemon
18784140Sjlemon	/*
18884140Sjlemon	 * Might need to generate a status message if autonegotiation
18984140Sjlemon	 * failed.
19084140Sjlemon	 */
19184140Sjlemon	return (0);
19284140Sjlemon}
19384140Sjlemon
19450120Swpaulvoid
19550120Swpaulmii_phy_reset(mii)
19650120Swpaul	struct mii_softc *mii;
19750120Swpaul{
19850120Swpaul	int reg, i;
19950120Swpaul
20050120Swpaul	if (mii->mii_flags & MIIF_NOISOLATE)
20150120Swpaul		reg = BMCR_RESET;
20250120Swpaul	else
20350120Swpaul		reg = BMCR_RESET | BMCR_ISO;
20450120Swpaul	PHY_WRITE(mii, MII_BMCR, reg);
20550120Swpaul
20650120Swpaul	/* Wait 100ms for it to complete. */
20750120Swpaul	for (i = 0; i < 100; i++) {
20850120Swpaul		reg = PHY_READ(mii, MII_BMCR);
20950120Swpaul		if ((reg & BMCR_RESET) == 0)
21050120Swpaul			break;
21150120Swpaul		DELAY(1000);
21250120Swpaul	}
21350120Swpaul
21450120Swpaul	if (mii->mii_inst != 0 && ((mii->mii_flags & MIIF_NOISOLATE) == 0))
21550120Swpaul		PHY_WRITE(mii, MII_BMCR, reg | BMCR_ISO);
21650120Swpaul}
21750120Swpaul
21884140Sjlemonvoid
21984140Sjlemonmii_phy_update(sc, cmd)
22084140Sjlemon	struct mii_softc *sc;
22184140Sjlemon	int cmd;
22284140Sjlemon{
22384140Sjlemon	struct mii_data *mii = sc->mii_pdata;
22484140Sjlemon
22584140Sjlemon	if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
22684140Sjlemon		MIIBUS_STATCHG(sc->mii_dev);
22784140Sjlemon		sc->mii_active = mii->mii_media_active;
22884140Sjlemon	}
22984140Sjlemon	if (sc->mii_status != mii->mii_media_status) {
23084140Sjlemon		MIIBUS_LINKCHG(sc->mii_dev);
23184140Sjlemon		sc->mii_status = mii->mii_media_status;
23284140Sjlemon	}
23384140Sjlemon}
23484140Sjlemon
23550120Swpaul/*
23650120Swpaul * Given an ifmedia word, return the corresponding ANAR value.
23750120Swpaul */
23850120Swpaulint
23950120Swpaulmii_anar(media)
24050120Swpaul	int media;
24150120Swpaul{
24250120Swpaul	int rv;
24350120Swpaul
24450120Swpaul	switch (media & (IFM_TMASK|IFM_NMASK|IFM_FDX)) {
24550120Swpaul	case IFM_ETHER|IFM_10_T:
24650120Swpaul		rv = ANAR_10|ANAR_CSMA;
24750120Swpaul		break;
24850120Swpaul	case IFM_ETHER|IFM_10_T|IFM_FDX:
24950120Swpaul		rv = ANAR_10_FD|ANAR_CSMA;
25050120Swpaul		break;
25150120Swpaul	case IFM_ETHER|IFM_100_TX:
25250120Swpaul		rv = ANAR_TX|ANAR_CSMA;
25350120Swpaul		break;
25450120Swpaul	case IFM_ETHER|IFM_100_TX|IFM_FDX:
25550120Swpaul		rv = ANAR_TX_FD|ANAR_CSMA;
25650120Swpaul		break;
25750120Swpaul	case IFM_ETHER|IFM_100_T4:
25850120Swpaul		rv = ANAR_T4|ANAR_CSMA;
25950120Swpaul		break;
26050120Swpaul	default:
26150120Swpaul		rv = 0;
26250120Swpaul		break;
26350120Swpaul	}
26450120Swpaul
26550120Swpaul	return (rv);
26650120Swpaul}
26750120Swpaul
26850120Swpaul/*
26950120Swpaul * Given a BMCR value, return the corresponding ifmedia word.
27050120Swpaul */
27150120Swpaulint
27250120Swpaulmii_media_from_bmcr(bmcr)
27350120Swpaul	int bmcr;
27450120Swpaul{
27550120Swpaul	int rv = IFM_ETHER;
27650120Swpaul
27750120Swpaul	if (bmcr & BMCR_S100)
27850120Swpaul		rv |= IFM_100_TX;
27950120Swpaul	else
28050120Swpaul		rv |= IFM_10_T;
28150120Swpaul	if (bmcr & BMCR_FDX)
28250120Swpaul		rv |= IFM_FDX;
28350120Swpaul
28450120Swpaul	return (rv);
28550120Swpaul}
28650120Swpaul
28750120Swpaul/*
28850120Swpaul * Initialize generic PHY media based on BMSR, called when a PHY is
28950120Swpaul * attached.  We expect to be set up to print a comma-separated list
29050120Swpaul * of media names.  Does not print a newline.
29150120Swpaul */
29250120Swpaulvoid
29350120Swpaulmii_add_media(mii, bmsr, instance)
29450120Swpaul	struct mii_data *mii;
29550120Swpaul	int bmsr, instance;
29650120Swpaul{
29750120Swpaul	const char *sep = "";
29850120Swpaul
29995665Sphk	if ((bmsr & BMSR_MEDIAMASK) == 0) {
30095665Sphk		printf("no media present");
30195665Sphk		return;
30295665Sphk	}
30395665Sphk
30450120Swpaul#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
30550120Swpaul#define	PRINT(s)	printf("%s%s", sep, s); sep = ", "
30650120Swpaul
30750120Swpaul	if (bmsr & BMSR_10THDX) {
30850120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, instance), 0);
30950120Swpaul		PRINT("10baseT");
31050120Swpaul	}
31150120Swpaul	if (bmsr & BMSR_10TFDX) {
31250120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, instance),
31350120Swpaul		    BMCR_FDX);
31450120Swpaul		PRINT("10baseT-FDX");
31550120Swpaul	}
31650120Swpaul	if (bmsr & BMSR_100TXHDX) {
31750120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, instance),
31850120Swpaul		    BMCR_S100);
31950120Swpaul		PRINT("100baseTX");
32050120Swpaul	}
32150120Swpaul	if (bmsr & BMSR_100TXFDX) {
32250120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, instance),
32350120Swpaul		    BMCR_S100|BMCR_FDX);
32450120Swpaul		PRINT("100baseTX-FDX");
32550120Swpaul	}
32650120Swpaul	if (bmsr & BMSR_100T4) {
32750120Swpaul		/*
32850120Swpaul		 * XXX How do you enable 100baseT4?  I assume we set
32950120Swpaul		 * XXX BMCR_S100 and then assume the PHYs will take
33050120Swpaul		 * XXX watever action is necessary to switch themselves
33150120Swpaul		 * XXX into T4 mode.
33250120Swpaul		 */
33350120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, instance),
33450120Swpaul		    BMCR_S100);
33550120Swpaul		PRINT("100baseT4");
33650120Swpaul	}
33750120Swpaul	if (bmsr & BMSR_ANEG) {
33850120Swpaul		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, instance),
33950120Swpaul		    BMCR_AUTOEN);
34050120Swpaul		PRINT("auto");
34150120Swpaul	}
34250120Swpaul#undef ADD
34350120Swpaul#undef PRINT
34450120Swpaul}
345