xref: /dragonfly/sys/dev/netif/mii_layer/ip1000phy.c (revision d3c9c58e)
11da2c9dbSSepherosa Ziehau /*-
21da2c9dbSSepherosa Ziehau  * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org>
31da2c9dbSSepherosa Ziehau  * All rights reserved.
41da2c9dbSSepherosa Ziehau  *
51da2c9dbSSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
61da2c9dbSSepherosa Ziehau  * modification, are permitted provided that the following conditions
71da2c9dbSSepherosa Ziehau  * are met:
81da2c9dbSSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
91da2c9dbSSepherosa Ziehau  *    notice unmodified, this list of conditions, and the following
101da2c9dbSSepherosa Ziehau  *    disclaimer.
111da2c9dbSSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
121da2c9dbSSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
131da2c9dbSSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
141da2c9dbSSepherosa Ziehau  *
151da2c9dbSSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161da2c9dbSSepherosa Ziehau  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171da2c9dbSSepherosa Ziehau  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181da2c9dbSSepherosa Ziehau  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191da2c9dbSSepherosa Ziehau  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201da2c9dbSSepherosa Ziehau  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211da2c9dbSSepherosa Ziehau  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221da2c9dbSSepherosa Ziehau  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231da2c9dbSSepherosa Ziehau  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241da2c9dbSSepherosa Ziehau  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251da2c9dbSSepherosa Ziehau  * SUCH DAMAGE.
261da2c9dbSSepherosa Ziehau  *
271da2c9dbSSepherosa Ziehau  * $FreeBSD: src/sys/dev/mii/ip1000phy.c,v 1.1 2006/07/25 00:16:09 yongari Exp $
281da2c9dbSSepherosa Ziehau  */
291da2c9dbSSepherosa Ziehau 
301da2c9dbSSepherosa Ziehau /*
311da2c9dbSSepherosa Ziehau  * Driver for the IC Plus IP1000A 10/100/1000 PHY.
321da2c9dbSSepherosa Ziehau  */
331da2c9dbSSepherosa Ziehau 
341da2c9dbSSepherosa Ziehau #include <sys/param.h>
351da2c9dbSSepherosa Ziehau #include <sys/bus.h>
361da2c9dbSSepherosa Ziehau #include <sys/kernel.h>
371da2c9dbSSepherosa Ziehau #include <sys/module.h>
381da2c9dbSSepherosa Ziehau #include <sys/socket.h>
391da2c9dbSSepherosa Ziehau #include <sys/sysctl.h>
401da2c9dbSSepherosa Ziehau 
411da2c9dbSSepherosa Ziehau #include <net/if.h>
421da2c9dbSSepherosa Ziehau #include <net/if_arp.h>
431da2c9dbSSepherosa Ziehau #include <net/if_media.h>
449bb7da42SSepherosa Ziehau #include <net/if_poll.h>
451da2c9dbSSepherosa Ziehau 
461da2c9dbSSepherosa Ziehau #include <dev/netif/mii_layer/mii.h>
471da2c9dbSSepherosa Ziehau #include <dev/netif/mii_layer/miivar.h>
481da2c9dbSSepherosa Ziehau #include "miidevs.h"
491da2c9dbSSepherosa Ziehau 
501da2c9dbSSepherosa Ziehau #include <dev/netif/mii_layer/ip1000phyreg.h>
511da2c9dbSSepherosa Ziehau 
521da2c9dbSSepherosa Ziehau #include "miibus_if.h"
531da2c9dbSSepherosa Ziehau 
541da2c9dbSSepherosa Ziehau #include <dev/netif/stge/if_stgereg.h>
551da2c9dbSSepherosa Ziehau #include <dev/netif/stge/if_stgevar.h>
561da2c9dbSSepherosa Ziehau 
571da2c9dbSSepherosa Ziehau static int	ip1000phy_probe(device_t);
581da2c9dbSSepherosa Ziehau static int	ip1000phy_attach(device_t);
591da2c9dbSSepherosa Ziehau static int	ip1000phy_service(struct mii_softc *, struct mii_data *, int);
601da2c9dbSSepherosa Ziehau static void	ip1000phy_status(struct mii_softc *);
611da2c9dbSSepherosa Ziehau static void	ip1000phy_reset(struct mii_softc *);
621da2c9dbSSepherosa Ziehau static int	ip1000phy_mii_phy_auto(struct mii_softc *);
631da2c9dbSSepherosa Ziehau 
641da2c9dbSSepherosa Ziehau static device_method_t ip1000phy_methods[] = {
651da2c9dbSSepherosa Ziehau 	/* device interface */
661da2c9dbSSepherosa Ziehau 	DEVMETHOD(device_probe,		ip1000phy_probe),
671da2c9dbSSepherosa Ziehau 	DEVMETHOD(device_attach,	ip1000phy_attach),
681da2c9dbSSepherosa Ziehau 	DEVMETHOD(device_detach,	ukphy_detach),
691da2c9dbSSepherosa Ziehau 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
70*d3c9c58eSSascha Wildner 	DEVMETHOD_END
711da2c9dbSSepherosa Ziehau };
721da2c9dbSSepherosa Ziehau 
731da2c9dbSSepherosa Ziehau static devclass_t ip1000phy_devclass;
741da2c9dbSSepherosa Ziehau static driver_t ip1000phy_driver = {
751da2c9dbSSepherosa Ziehau 	"ip1000phy",
761da2c9dbSSepherosa Ziehau 	ip1000phy_methods,
771da2c9dbSSepherosa Ziehau 	sizeof (struct mii_softc)
781da2c9dbSSepherosa Ziehau };
791da2c9dbSSepherosa Ziehau 
80aa2b9d05SSascha Wildner DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, NULL, NULL);
811da2c9dbSSepherosa Ziehau 
821da2c9dbSSepherosa Ziehau static const struct mii_phydesc ip1000phys[] = {
831da2c9dbSSepherosa Ziehau 	MII_PHYDESC(ICPLUS,	IP1000A),
841da2c9dbSSepherosa Ziehau 	MII_PHYDESC_NULL
851da2c9dbSSepherosa Ziehau };
861da2c9dbSSepherosa Ziehau 
871da2c9dbSSepherosa Ziehau static int
ip1000phy_probe(device_t dev)881da2c9dbSSepherosa Ziehau ip1000phy_probe(device_t dev)
891da2c9dbSSepherosa Ziehau {
901da2c9dbSSepherosa Ziehau 	struct mii_attach_args *ma;
911da2c9dbSSepherosa Ziehau 	const struct mii_phydesc *mpd;
921da2c9dbSSepherosa Ziehau 
931da2c9dbSSepherosa Ziehau 	ma = device_get_ivars(dev);
941da2c9dbSSepherosa Ziehau 	mpd = mii_phy_match(ma, ip1000phys);
951da2c9dbSSepherosa Ziehau 	if (mpd != NULL) {
961da2c9dbSSepherosa Ziehau 		device_set_desc(dev, mpd->mpd_name);
971da2c9dbSSepherosa Ziehau 		return (0);
981da2c9dbSSepherosa Ziehau 	}
991da2c9dbSSepherosa Ziehau 
1001da2c9dbSSepherosa Ziehau 	return (ENXIO);
1011da2c9dbSSepherosa Ziehau }
1021da2c9dbSSepherosa Ziehau 
1031da2c9dbSSepherosa Ziehau static int
ip1000phy_attach(device_t dev)1041da2c9dbSSepherosa Ziehau ip1000phy_attach(device_t dev)
1051da2c9dbSSepherosa Ziehau {
1061da2c9dbSSepherosa Ziehau 	struct mii_softc *sc;
1071da2c9dbSSepherosa Ziehau 	struct mii_attach_args *ma;
1081da2c9dbSSepherosa Ziehau 	struct mii_data *mii;
1091da2c9dbSSepherosa Ziehau 
1101da2c9dbSSepherosa Ziehau 	sc = device_get_softc(dev);
1111da2c9dbSSepherosa Ziehau 	ma = device_get_ivars(dev);
1121da2c9dbSSepherosa Ziehau 	mii_softc_init(sc, ma);
1131da2c9dbSSepherosa Ziehau 	sc->mii_dev = device_get_parent(dev);
1141da2c9dbSSepherosa Ziehau 	mii = device_get_softc(sc->mii_dev);
1151da2c9dbSSepherosa Ziehau 	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
1161da2c9dbSSepherosa Ziehau 
1171da2c9dbSSepherosa Ziehau 	sc->mii_inst = mii->mii_instance;
1181da2c9dbSSepherosa Ziehau 	sc->mii_service = ip1000phy_service;
1191da2c9dbSSepherosa Ziehau 	sc->mii_pdata = mii;
1201da2c9dbSSepherosa Ziehau 
1211da2c9dbSSepherosa Ziehau 	sc->mii_anegticks = MII_ANEGTICKS_GIGE;
1221da2c9dbSSepherosa Ziehau 	sc->mii_flags |= MIIF_NOISOLATE;
1231da2c9dbSSepherosa Ziehau 
1241da2c9dbSSepherosa Ziehau 	mii->mii_instance++;
1251da2c9dbSSepherosa Ziehau 
1261da2c9dbSSepherosa Ziehau 	device_printf(dev, " ");
1271da2c9dbSSepherosa Ziehau 
1281da2c9dbSSepherosa Ziehau #define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
1291da2c9dbSSepherosa Ziehau 
1301da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
1311da2c9dbSSepherosa Ziehau 	    BMCR_ISO);
1321da2c9dbSSepherosa Ziehau 
1331da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
1341da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_10);
135e3869ec7SSascha Wildner 	kprintf("10baseT, ");
1361da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
1371da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_10 | IP1000PHY_BMCR_FDX);
138e3869ec7SSascha Wildner 	kprintf("10baseT-FDX, ");
1391da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
1401da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_100);
141e3869ec7SSascha Wildner 	kprintf("100baseTX, ");
1421da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
1431da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_100 | IP1000PHY_BMCR_FDX);
144e3869ec7SSascha Wildner 	kprintf("100baseTX-FDX, ");
1451da2c9dbSSepherosa Ziehau 	/* 1000baseT half-duplex, really supported? */
1461da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
1471da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_1000);
1489bbc5585SHasso Tepper 	kprintf("1000baseT, ");
1491da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
1501da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_1000 | IP1000PHY_BMCR_FDX);
1519bbc5585SHasso Tepper 	kprintf("1000baseT-FDX, ");
1521da2c9dbSSepherosa Ziehau 	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
153e3869ec7SSascha Wildner 	kprintf("auto\n");
1541da2c9dbSSepherosa Ziehau #undef ADD
1551da2c9dbSSepherosa Ziehau 
1561da2c9dbSSepherosa Ziehau 	ip1000phy_reset(sc);
1571da2c9dbSSepherosa Ziehau 
1581da2c9dbSSepherosa Ziehau 	MIIBUS_MEDIAINIT(sc->mii_dev);
1591da2c9dbSSepherosa Ziehau 	return(0);
1601da2c9dbSSepherosa Ziehau }
1611da2c9dbSSepherosa Ziehau 
1621da2c9dbSSepherosa Ziehau static int
ip1000phy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)1631da2c9dbSSepherosa Ziehau ip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
1641da2c9dbSSepherosa Ziehau {
1651da2c9dbSSepherosa Ziehau 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
1661da2c9dbSSepherosa Ziehau 	uint32_t gig, reg, speed;
1671da2c9dbSSepherosa Ziehau 
1681da2c9dbSSepherosa Ziehau 	switch (cmd) {
1691da2c9dbSSepherosa Ziehau 	case MII_POLLSTAT:
1701da2c9dbSSepherosa Ziehau 		/*
1711da2c9dbSSepherosa Ziehau 		 * If we're not polling our PHY instance, just return.
1721da2c9dbSSepherosa Ziehau 		 */
1731da2c9dbSSepherosa Ziehau 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
1741da2c9dbSSepherosa Ziehau 			return (0);
1751da2c9dbSSepherosa Ziehau 		break;
1761da2c9dbSSepherosa Ziehau 
1771da2c9dbSSepherosa Ziehau 	case MII_MEDIACHG:
1781da2c9dbSSepherosa Ziehau 		/*
1791da2c9dbSSepherosa Ziehau 		 * If the media indicates a different PHY instance,
1801da2c9dbSSepherosa Ziehau 		 * isolate ourselves.
1811da2c9dbSSepherosa Ziehau 		 */
1821da2c9dbSSepherosa Ziehau 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
1831da2c9dbSSepherosa Ziehau 			reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
1841da2c9dbSSepherosa Ziehau 			PHY_WRITE(sc, IP1000PHY_MII_BMCR,
1851da2c9dbSSepherosa Ziehau 			    reg | IP1000PHY_BMCR_ISO);
1861da2c9dbSSepherosa Ziehau 			return (0);
1871da2c9dbSSepherosa Ziehau 		}
1881da2c9dbSSepherosa Ziehau 
1891da2c9dbSSepherosa Ziehau 		/*
1901da2c9dbSSepherosa Ziehau 		 * If the interface is not up, don't do anything.
1911da2c9dbSSepherosa Ziehau 		 */
1921da2c9dbSSepherosa Ziehau 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
1931da2c9dbSSepherosa Ziehau 			break;
1941da2c9dbSSepherosa Ziehau 
1951da2c9dbSSepherosa Ziehau 		ip1000phy_reset(sc);
1961da2c9dbSSepherosa Ziehau 		switch (IFM_SUBTYPE(ife->ifm_media)) {
1971da2c9dbSSepherosa Ziehau 		case IFM_AUTO:
1981da2c9dbSSepherosa Ziehau 			ip1000phy_mii_phy_auto(sc);
1991da2c9dbSSepherosa Ziehau 			goto done;
2001da2c9dbSSepherosa Ziehau 
2011da2c9dbSSepherosa Ziehau 		case IFM_1000_T:
2021da2c9dbSSepherosa Ziehau 			/*
2031da2c9dbSSepherosa Ziehau 			 * XXX
2041da2c9dbSSepherosa Ziehau 			 * Manual 1000baseT setting doesn't seem to work.
2051da2c9dbSSepherosa Ziehau 			 */
2061da2c9dbSSepherosa Ziehau 			speed = IP1000PHY_BMCR_1000;
2071da2c9dbSSepherosa Ziehau 			break;
2081da2c9dbSSepherosa Ziehau 
2091da2c9dbSSepherosa Ziehau 		case IFM_100_TX:
2101da2c9dbSSepherosa Ziehau 			speed = IP1000PHY_BMCR_100;
2111da2c9dbSSepherosa Ziehau 			break;
2121da2c9dbSSepherosa Ziehau 
2131da2c9dbSSepherosa Ziehau 		case IFM_10_T:
2141da2c9dbSSepherosa Ziehau 			speed = IP1000PHY_BMCR_10;
2151da2c9dbSSepherosa Ziehau 			break;
2161da2c9dbSSepherosa Ziehau 
2171da2c9dbSSepherosa Ziehau 		default:
2181da2c9dbSSepherosa Ziehau 			return (EINVAL);
2191da2c9dbSSepherosa Ziehau 		}
2201da2c9dbSSepherosa Ziehau 
2211da2c9dbSSepherosa Ziehau 		if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) {
2221da2c9dbSSepherosa Ziehau 			speed |= IP1000PHY_BMCR_FDX;
2231da2c9dbSSepherosa Ziehau 			gig = IP1000PHY_1000CR_1000T_FDX;
2241da2c9dbSSepherosa Ziehau 		} else {
2251da2c9dbSSepherosa Ziehau 			gig = IP1000PHY_1000CR_1000T;
2261da2c9dbSSepherosa Ziehau 		}
2271da2c9dbSSepherosa Ziehau 
2281da2c9dbSSepherosa Ziehau 		PHY_WRITE(sc, IP1000PHY_MII_1000CR, 0);
2291da2c9dbSSepherosa Ziehau 		PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
2301da2c9dbSSepherosa Ziehau 
2311da2c9dbSSepherosa Ziehau 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
2321da2c9dbSSepherosa Ziehau 			break;
2331da2c9dbSSepherosa Ziehau 
2341da2c9dbSSepherosa Ziehau 		PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig);
2351da2c9dbSSepherosa Ziehau 		PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
2361da2c9dbSSepherosa Ziehau 
2371da2c9dbSSepherosa Ziehau 		/*
2381da2c9dbSSepherosa Ziehau 		 * When settning the link manually, one side must
2391da2c9dbSSepherosa Ziehau 		 * be the master and the other the slave. However
2401da2c9dbSSepherosa Ziehau 		 * ifmedia doesn't give us a good way to specify
2411da2c9dbSSepherosa Ziehau 		 * this, so we fake it by using one of the LINK
2421da2c9dbSSepherosa Ziehau 		 * flags. If LINK0 is set, we program the PHY to
2431da2c9dbSSepherosa Ziehau 		 * be a master, otherwise it's a slave.
2441da2c9dbSSepherosa Ziehau 		 */
2451da2c9dbSSepherosa Ziehau 		if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
2461da2c9dbSSepherosa Ziehau 			PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
2471da2c9dbSSepherosa Ziehau 			    IP1000PHY_1000CR_MASTER |
2481da2c9dbSSepherosa Ziehau 			    IP1000PHY_1000CR_MMASTER |
2491da2c9dbSSepherosa Ziehau 			    IP1000PHY_1000CR_MANUAL);
2501da2c9dbSSepherosa Ziehau 		} else {
2511da2c9dbSSepherosa Ziehau 			PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig |
2521da2c9dbSSepherosa Ziehau 			    IP1000PHY_1000CR_MASTER |
2531da2c9dbSSepherosa Ziehau 			    IP1000PHY_1000CR_MANUAL);
2541da2c9dbSSepherosa Ziehau 		}
2551da2c9dbSSepherosa Ziehau 
2561da2c9dbSSepherosa Ziehau done:
2571da2c9dbSSepherosa Ziehau 		break;
2581da2c9dbSSepherosa Ziehau 
2591da2c9dbSSepherosa Ziehau 	case MII_TICK:
2601da2c9dbSSepherosa Ziehau 		/*
2611da2c9dbSSepherosa Ziehau 		 * If we're not currently selected, just return.
2621da2c9dbSSepherosa Ziehau 		 */
2631da2c9dbSSepherosa Ziehau 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
2641da2c9dbSSepherosa Ziehau 			return (0);
2651da2c9dbSSepherosa Ziehau 		/*
2661da2c9dbSSepherosa Ziehau 		 * Is the interface even up?
2671da2c9dbSSepherosa Ziehau 		 */
2681da2c9dbSSepherosa Ziehau 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
2691da2c9dbSSepherosa Ziehau 			return (0);
2701da2c9dbSSepherosa Ziehau 
2711da2c9dbSSepherosa Ziehau 		/*
2721da2c9dbSSepherosa Ziehau 		 * Only used for autonegotiation.
2731da2c9dbSSepherosa Ziehau 		 */
27412b5e22dSSepherosa Ziehau 		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
2751da2c9dbSSepherosa Ziehau 			break;
2761da2c9dbSSepherosa Ziehau 
2771da2c9dbSSepherosa Ziehau 		/*
2781da2c9dbSSepherosa Ziehau 		 * check for link.
2791da2c9dbSSepherosa Ziehau 		 */
2801da2c9dbSSepherosa Ziehau 		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
2811da2c9dbSSepherosa Ziehau 		if (reg & BMSR_LINK) {
2821da2c9dbSSepherosa Ziehau 			sc->mii_ticks = 0;
2831da2c9dbSSepherosa Ziehau 			break;
2841da2c9dbSSepherosa Ziehau 		}
2851da2c9dbSSepherosa Ziehau 
2861da2c9dbSSepherosa Ziehau 		/* Announce link loss right after it happens */
2871da2c9dbSSepherosa Ziehau 		if (sc->mii_ticks++ == 0)
2881da2c9dbSSepherosa Ziehau 			break;
2891da2c9dbSSepherosa Ziehau 
2901da2c9dbSSepherosa Ziehau 		/*
2911da2c9dbSSepherosa Ziehau 		 * Only retry autonegotiation every mii_anegticks seconds.
2921da2c9dbSSepherosa Ziehau 		 */
2931da2c9dbSSepherosa Ziehau 		if (sc->mii_ticks <= sc->mii_anegticks)
2941da2c9dbSSepherosa Ziehau 			return (0);
2951da2c9dbSSepherosa Ziehau 
2961da2c9dbSSepherosa Ziehau 		sc->mii_ticks = 0;
2971da2c9dbSSepherosa Ziehau 		ip1000phy_mii_phy_auto(sc);
2981da2c9dbSSepherosa Ziehau 		break;
2991da2c9dbSSepherosa Ziehau 	}
3001da2c9dbSSepherosa Ziehau 
3011da2c9dbSSepherosa Ziehau 	/* Update the media status. */
3021da2c9dbSSepherosa Ziehau 	ip1000phy_status(sc);
3031da2c9dbSSepherosa Ziehau 
3041da2c9dbSSepherosa Ziehau 	/* Callback if something changed. */
3051da2c9dbSSepherosa Ziehau 	mii_phy_update(sc, cmd);
3061da2c9dbSSepherosa Ziehau 	return (0);
3071da2c9dbSSepherosa Ziehau }
3081da2c9dbSSepherosa Ziehau 
3091da2c9dbSSepherosa Ziehau static void
ip1000phy_status(struct mii_softc * sc)3101da2c9dbSSepherosa Ziehau ip1000phy_status(struct mii_softc *sc)
3111da2c9dbSSepherosa Ziehau {
3121da2c9dbSSepherosa Ziehau 	struct mii_data *mii = sc->mii_pdata;
3131da2c9dbSSepherosa Ziehau 	uint32_t bmsr, bmcr, stat;
3141da2c9dbSSepherosa Ziehau 	uint32_t ar, lpar;
3151da2c9dbSSepherosa Ziehau 
3161da2c9dbSSepherosa Ziehau 	mii->mii_media_status = IFM_AVALID;
3171da2c9dbSSepherosa Ziehau 	mii->mii_media_active = IFM_ETHER;
3181da2c9dbSSepherosa Ziehau 
3191da2c9dbSSepherosa Ziehau 	bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) |
3201da2c9dbSSepherosa Ziehau 	    PHY_READ(sc, IP1000PHY_MII_BMSR);
3211da2c9dbSSepherosa Ziehau 	if ((bmsr & IP1000PHY_BMSR_LINK) != 0)
3221da2c9dbSSepherosa Ziehau 		mii->mii_media_status |= IFM_ACTIVE;
3231da2c9dbSSepherosa Ziehau 
3241da2c9dbSSepherosa Ziehau 	bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR);
3251da2c9dbSSepherosa Ziehau 	if ((bmcr & IP1000PHY_BMCR_LOOP) != 0)
3261da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_LOOP;
3271da2c9dbSSepherosa Ziehau 
3281da2c9dbSSepherosa Ziehau 	if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) {
3291da2c9dbSSepherosa Ziehau 		if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) {
3301da2c9dbSSepherosa Ziehau 			/* Erg, still trying, I guess... */
3311da2c9dbSSepherosa Ziehau 			mii->mii_media_active |= IFM_NONE;
3321da2c9dbSSepherosa Ziehau 			return;
3331da2c9dbSSepherosa Ziehau                 }
3341da2c9dbSSepherosa Ziehau         }
3351da2c9dbSSepherosa Ziehau 
3361da2c9dbSSepherosa Ziehau 	stat = PHY_READ(sc, STGE_PhyCtrl);
3371da2c9dbSSepherosa Ziehau 	switch (PC_LinkSpeed(stat)) {
3381da2c9dbSSepherosa Ziehau 	case PC_LinkSpeed_Down:
3391da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_NONE;
3401da2c9dbSSepherosa Ziehau 		return;
3411da2c9dbSSepherosa Ziehau 	case PC_LinkSpeed_10:
3421da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_10_T;
3431da2c9dbSSepherosa Ziehau 		break;
3441da2c9dbSSepherosa Ziehau 	case PC_LinkSpeed_100:
3451da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_100_TX;
3461da2c9dbSSepherosa Ziehau 		break;
3471da2c9dbSSepherosa Ziehau 	case PC_LinkSpeed_1000:
3481da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_1000_T;
3491da2c9dbSSepherosa Ziehau 		break;
3501da2c9dbSSepherosa Ziehau 	}
3511da2c9dbSSepherosa Ziehau 	if ((stat & PC_PhyDuplexStatus) != 0)
3521da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_FDX;
3531da2c9dbSSepherosa Ziehau 	else
3541da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_HDX;
3551da2c9dbSSepherosa Ziehau 
3561da2c9dbSSepherosa Ziehau 	ar = PHY_READ(sc, IP1000PHY_MII_ANAR);
3571da2c9dbSSepherosa Ziehau 	lpar = PHY_READ(sc, IP1000PHY_MII_ANLPAR);
3581da2c9dbSSepherosa Ziehau 
3591da2c9dbSSepherosa Ziehau 	/*
3601da2c9dbSSepherosa Ziehau 	 * FLAG0 : Rx flow-control
3611da2c9dbSSepherosa Ziehau 	 * FLAG1 : Tx flow-control
3621da2c9dbSSepherosa Ziehau 	 */
3631da2c9dbSSepherosa Ziehau 	if ((ar & IP1000PHY_ANAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_PAUSE))
3641da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
3651da2c9dbSSepherosa Ziehau 	else if (!(ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
3661da2c9dbSSepherosa Ziehau 	    (lpar & IP1000PHY_ANLPAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_APAUSE))
3671da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_FLAG1;
3681da2c9dbSSepherosa Ziehau 	else if ((ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) &&
3691da2c9dbSSepherosa Ziehau 	    !(lpar & IP1000PHY_ANLPAR_PAUSE) &&
3701da2c9dbSSepherosa Ziehau 	    (lpar & IP1000PHY_ANLPAR_APAUSE)) {
3711da2c9dbSSepherosa Ziehau 		mii->mii_media_active |= IFM_FLAG0;
3721da2c9dbSSepherosa Ziehau 	}
3731da2c9dbSSepherosa Ziehau 
3741da2c9dbSSepherosa Ziehau 	/*
3751da2c9dbSSepherosa Ziehau 	 * FLAG2 : local PHY resolved to MASTER
3761da2c9dbSSepherosa Ziehau 	 */
3771da2c9dbSSepherosa Ziehau 	if ((mii->mii_media_active & IFM_1000_T) != 0) {
3781da2c9dbSSepherosa Ziehau 		stat = PHY_READ(sc, IP1000PHY_MII_1000SR);
3791da2c9dbSSepherosa Ziehau 		if ((stat & IP1000PHY_1000SR_MASTER) != 0)
3801da2c9dbSSepherosa Ziehau 			mii->mii_media_active |= IFM_FLAG2;
3811da2c9dbSSepherosa Ziehau 	}
3821da2c9dbSSepherosa Ziehau }
3831da2c9dbSSepherosa Ziehau 
3841da2c9dbSSepherosa Ziehau static int
ip1000phy_mii_phy_auto(struct mii_softc * mii)3851da2c9dbSSepherosa Ziehau ip1000phy_mii_phy_auto(struct mii_softc *mii)
3861da2c9dbSSepherosa Ziehau {
3871da2c9dbSSepherosa Ziehau 	uint32_t reg;
3881da2c9dbSSepherosa Ziehau 
3891da2c9dbSSepherosa Ziehau 	PHY_WRITE(mii, IP1000PHY_MII_ANAR,
3901da2c9dbSSepherosa Ziehau 	    IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX |
3911da2c9dbSSepherosa Ziehau 	    IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX |
3921da2c9dbSSepherosa Ziehau 	    IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE);
3931da2c9dbSSepherosa Ziehau 	reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX;
3941da2c9dbSSepherosa Ziehau 	reg |= IP1000PHY_1000CR_MASTER;
3951da2c9dbSSepherosa Ziehau 	PHY_WRITE(mii, IP1000PHY_MII_1000CR, reg);
3961da2c9dbSSepherosa Ziehau 	PHY_WRITE(mii, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX |
3971da2c9dbSSepherosa Ziehau 	    IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG));
3981da2c9dbSSepherosa Ziehau 
3991da2c9dbSSepherosa Ziehau 	return (EJUSTRETURN);
4001da2c9dbSSepherosa Ziehau }
4011da2c9dbSSepherosa Ziehau 
4021da2c9dbSSepherosa Ziehau static void
ip1000phy_load_dspcode(struct mii_softc * sc)4031da2c9dbSSepherosa Ziehau ip1000phy_load_dspcode(struct mii_softc *sc)
4041da2c9dbSSepherosa Ziehau {
4051da2c9dbSSepherosa Ziehau 
4061da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 31, 0x0001);
4071da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 27, 0x01e0);
4081da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 31, 0x0002);
4091da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 27, 0xeb8e);
4101da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 31, 0x0000);
4111da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 30, 0x005e);
4121da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, 9, 0x0700);
4131da2c9dbSSepherosa Ziehau 
4141da2c9dbSSepherosa Ziehau 	DELAY(50);
4151da2c9dbSSepherosa Ziehau }
4161da2c9dbSSepherosa Ziehau 
4171da2c9dbSSepherosa Ziehau static void
ip1000phy_reset(struct mii_softc * sc)4181da2c9dbSSepherosa Ziehau ip1000phy_reset(struct mii_softc *sc)
4191da2c9dbSSepherosa Ziehau {
4201da2c9dbSSepherosa Ziehau 	struct stge_softc *stge_sc;
4211da2c9dbSSepherosa Ziehau 	device_t parent;
4221da2c9dbSSepherosa Ziehau 	uint32_t reg;
4231da2c9dbSSepherosa Ziehau 
4241da2c9dbSSepherosa Ziehau 	mii_phy_reset(sc);
4251da2c9dbSSepherosa Ziehau 
4261da2c9dbSSepherosa Ziehau 	/* clear autoneg/full-duplex as we don't want it after reset */
4271da2c9dbSSepherosa Ziehau 	reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
4281da2c9dbSSepherosa Ziehau 	reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX);
4291da2c9dbSSepherosa Ziehau 	PHY_WRITE(sc, MII_BMCR, reg);
4301da2c9dbSSepherosa Ziehau 
4311da2c9dbSSepherosa Ziehau 	/*
4321da2c9dbSSepherosa Ziehau 	 * XXX There should be more general way to pass PHY specific
4331da2c9dbSSepherosa Ziehau 	 * data via mii interface.
4341da2c9dbSSepherosa Ziehau 	 */
4351da2c9dbSSepherosa Ziehau 	parent = device_get_parent(sc->mii_dev);
4361da2c9dbSSepherosa Ziehau 	if (strncmp(device_get_name(parent), "stge", 4) == 0) {
4371da2c9dbSSepherosa Ziehau 		stge_sc = device_get_softc(parent);
4381da2c9dbSSepherosa Ziehau 		if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e)
4391da2c9dbSSepherosa Ziehau 			ip1000phy_load_dspcode(sc);
4401da2c9dbSSepherosa Ziehau 	}
4411da2c9dbSSepherosa Ziehau }
442