xmphy.c revision 267654
112891Swpaul/*-
212891Swpaul * Copyright (c) 2000
312891Swpaul *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
412891Swpaul *
512891Swpaul * Redistribution and use in source and binary forms, with or without
612891Swpaul * modification, are permitted provided that the following conditions
712891Swpaul * are met:
812891Swpaul * 1. Redistributions of source code must retain the above copyright
912891Swpaul *    notice, this list of conditions and the following disclaimer.
1012891Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1112891Swpaul *    notice, this list of conditions and the following disclaimer in the
1212891Swpaul *    documentation and/or other materials provided with the distribution.
1312891Swpaul * 3. All advertising materials mentioning features or use of this software
1412891Swpaul *    must display the following acknowledgement:
1512891Swpaul *	This product includes software developed by Bill Paul.
1612891Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1712891Swpaul *    may be used to endorse or promote products derived from this software
1812891Swpaul *    without specific prior written permission.
1912891Swpaul *
2012891Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2112891Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2212891Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2312891Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2412891Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2512891Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2612891Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2712891Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2812891Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2912891Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3012891Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3112891Swpaul */
3212891Swpaul
3312891Swpaul#include <sys/cdefs.h>
3430827Scharnier__FBSDID("$FreeBSD: releng/9.3/sys/dev/mii/xmphy.c 229093 2011-12-31 14:12:12Z hselasky $");
3530827Scharnier
3650479Speter/*
3730827Scharnier * driver for the XaQti XMAC II's internal PHY. This is sort of
3830827Scharnier * like a 10/100 PHY, except the only thing we're really autoselecting
3920818Swpaul * here is full/half duplex. Speed is always 1000mbps.
4012891Swpaul */
4130827Scharnier
4230827Scharnier#include <sys/param.h>
4312891Swpaul#include <sys/systm.h>
4412891Swpaul#include <sys/kernel.h>
4512891Swpaul#include <sys/module.h>
4612891Swpaul#include <sys/socket.h>
4712891Swpaul#include <sys/bus.h>
4812891Swpaul
4912891Swpaul#include <net/if.h>
5012997Swpaul#include <net/if_media.h>
5112891Swpaul
5212891Swpaul#include <dev/mii/mii.h>
5312891Swpaul#include <dev/mii/miivar.h>
5428042Swpaul#include "miidevs.h"
5528042Swpaul
5628042Swpaul#include <dev/mii/xmphyreg.h>
5728042Swpaul
5828042Swpaul#include "miibus_if.h"
5946186Swpaul
6046186Swpaulstatic int xmphy_probe(device_t);
6146186Swpaulstatic int xmphy_attach(device_t);
6246186Swpaul
6346186Swpaulstatic device_method_t xmphy_methods[] = {
6446186Swpaul	/* device interface */
6546186Swpaul	DEVMETHOD(device_probe,		xmphy_probe),
6646186Swpaul	DEVMETHOD(device_attach,	xmphy_attach),
6746186Swpaul	DEVMETHOD(device_detach,	mii_phy_detach),
6846186Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
6946186Swpaul	DEVMETHOD_END
7014262Swpaul};
7114262Swpaul
7214262Swpaulstatic devclass_t xmphy_devclass;
7314262Swpaul
7412891Swpaulstatic driver_t xmphy_driver = {
7512891Swpaul	"xmphy",
7612891Swpaul	xmphy_methods,
7712891Swpaul	sizeof(struct mii_softc)
7812891Swpaul};
7912891Swpaul
8019161SwpaulDRIVER_MODULE(xmphy, miibus, xmphy_driver, xmphy_devclass, 0, 0);
8119161Swpaul
8219161Swpaulstatic int	xmphy_service(struct mii_softc *, struct mii_data *, int);
8312891Swpaulstatic void	xmphy_status(struct mii_softc *);
8419161Swpaulstatic int	xmphy_mii_phy_auto(struct mii_softc *);
8512891Swpaul
8612891Swpaulstatic const struct mii_phydesc xmphys[] = {
8712891Swpaul	MII_PHY_DESC(xxJATO, BASEX),
8812891Swpaul	MII_PHY_DESC(xxXAQTI, XMACII),
8912891Swpaul	MII_PHY_END
9012891Swpaul};
9112891Swpaul
9212891Swpaulstatic const struct mii_phy_funcs xmphy_funcs = {
9312891Swpaul	xmphy_service,
9412891Swpaul	xmphy_status,
9512891Swpaul	mii_phy_reset
9612891Swpaul};
9719161Swpaul
9819161Swpaulstatic int
9919161Swpaulxmphy_probe(device_t dev)
10012891Swpaul{
10119161Swpaul
10212891Swpaul	return (mii_phy_dev_probe(dev, xmphys, BUS_PROBE_DEFAULT));
10312891Swpaul}
10412891Swpaul
10512891Swpaulstatic int
10612891Swpaulxmphy_attach(device_t dev)
10712891Swpaul{
10812891Swpaul	struct mii_softc *sc;
10912891Swpaul	const char *sep = "";
11012891Swpaul
11112891Swpaul	sc = device_get_softc(dev);
11212891Swpaul
11312891Swpaul	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
11412891Swpaul	    &xmphy_funcs, 0);
11512891Swpaul	sc->mii_anegticks = MII_ANEGTICKS;
11612891Swpaul
11712891Swpaul	PHY_RESET(sc);
11812891Swpaul
11919161Swpaul#define	ADD(m, c)	ifmedia_add(&sc->mii_pdata->mii_media, (m), (c), NULL)
12019161Swpaul#define PRINT(s)	printf("%s%s", sep, s); sep = ", "
12119161Swpaul
12212891Swpaul	device_printf(dev, " ");
12319161Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst),
12412891Swpaul	    XMPHY_BMCR_FDX);
12512891Swpaul	PRINT("1000baseSX");
12612891Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 0);
12712891Swpaul	PRINT("1000baseSX-FDX");
12812891Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
12912891Swpaul	PRINT("auto");
13012891Swpaul
13112891Swpaul	printf("\n");
13212891Swpaul
13312891Swpaul#undef ADD
13412891Swpaul#undef PRINT
13512891Swpaul
13612891Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
13712891Swpaul	return (0);
13812891Swpaul}
13914304Swpaul
14014304Swpaulstatic int
14119161Swpaulxmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
14219161Swpaul{
14319161Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
14419161Swpaul	int reg;
14512891Swpaul
14619161Swpaul	switch (cmd) {
14712891Swpaul	case MII_POLLSTAT:
14812891Swpaul		break;
14912891Swpaul
15012891Swpaul	case MII_MEDIACHG:
15112891Swpaul		/*
15212891Swpaul		 * If the interface is not up, don't do anything.
15312891Swpaul		 */
15412891Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
15512891Swpaul			break;
15633250Swpaul
15720818Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
15820818Swpaul		case IFM_AUTO:
15912891Swpaul#ifdef foo
16012891Swpaul			/*
16120818Swpaul			 * If we're already in auto mode, just return.
16220818Swpaul			 */
16312891Swpaul			if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN)
16412891Swpaul				return (0);
16512891Swpaul#endif
16612891Swpaul			(void)xmphy_mii_phy_auto(sc);
16719161Swpaul			break;
16819161Swpaul		case IFM_1000_SX:
16919161Swpaul			PHY_RESET(sc);
17019161Swpaul			if ((ife->ifm_media & IFM_FDX) != 0) {
17119161Swpaul				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX);
17212891Swpaul				PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX);
17319161Swpaul			} else {
17427589Swpaul				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX);
17520818Swpaul				PHY_WRITE(sc, XMPHY_MII_BMCR, 0);
17620818Swpaul			}
17727589Swpaul			break;
17827589Swpaul		default:
17912891Swpaul			return (EINVAL);
18012891Swpaul		}
18130827Scharnier		break;
18212891Swpaul
18312891Swpaul	case MII_TICK:
18427589Swpaul		/*
18512891Swpaul		 * Is the interface even up?
18627589Swpaul		 */
18712891Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
18820818Swpaul			return (0);
18920818Swpaul
19012891Swpaul		/*
19112891Swpaul		 * Only used for autonegotiation.
19212891Swpaul		 */
19312891Swpaul		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
19412891Swpaul			break;
19512891Swpaul
19612891Swpaul		/*
19712891Swpaul		 * Check to see if we have link.  If we do, we don't
19812891Swpaul		 * need to restart the autonegotiation process.  Read
19912891Swpaul		 * the BMSR twice in case it's latched.
20014304Swpaul		 */
20114304Swpaul		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
20219161Swpaul		if (reg & BMSR_LINK)
20319161Swpaul			break;
20419161Swpaul
20519161Swpaul		/* Only retry autonegotiation every mii_anegticks seconds. */
20612891Swpaul		if (sc->mii_ticks <= sc->mii_anegticks)
20719161Swpaul			break;
20812891Swpaul
20912891Swpaul		sc->mii_ticks = 0;
21012891Swpaul
21112891Swpaul		PHY_RESET(sc);
21212891Swpaul		xmphy_mii_phy_auto(sc);
21312891Swpaul		return (0);
21412891Swpaul	}
21512891Swpaul
21612891Swpaul	/* Update the media status. */
21733250Swpaul	xmphy_status(sc);
21812891Swpaul
21912891Swpaul	/* Callback if something changed. */
22012891Swpaul	mii_phy_update(sc, cmd);
22112891Swpaul	return (0);
22220818Swpaul}
22312891Swpaul
22412891Swpaulstatic void
22512891Swpaulxmphy_status(struct mii_softc *sc)
22612891Swpaul{
22712891Swpaul	struct mii_data *mii = sc->mii_pdata;
22812891Swpaul	int bmsr, bmcr, anlpar;
22912891Swpaul
23012891Swpaul	mii->mii_media_status = IFM_AVALID;
23112891Swpaul	mii->mii_media_active = IFM_ETHER;
23214304Swpaul
23314304Swpaul	bmsr = PHY_READ(sc, XMPHY_MII_BMSR) |
23415426Swpaul	    PHY_READ(sc, XMPHY_MII_BMSR);
23519161Swpaul	if (bmsr & XMPHY_BMSR_LINK)
23619161Swpaul		mii->mii_media_status |= IFM_ACTIVE;
23719161Swpaul
23812891Swpaul	/* Do dummy read of extended status register. */
23919161Swpaul	bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS);
24012891Swpaul
24112891Swpaul	bmcr = PHY_READ(sc, XMPHY_MII_BMCR);
24212891Swpaul
24312891Swpaul	if (bmcr & XMPHY_BMCR_LOOP)
24412891Swpaul		mii->mii_media_active |= IFM_LOOP;
24512891Swpaul
24612891Swpaul	if (bmcr & XMPHY_BMCR_AUTOEN) {
24712891Swpaul		if ((bmsr & XMPHY_BMSR_ACOMP) == 0) {
24812891Swpaul			if (bmsr & XMPHY_BMSR_LINK) {
24920818Swpaul				mii->mii_media_active |= IFM_1000_SX|IFM_HDX;
25012891Swpaul				return;
25112891Swpaul			}
25212891Swpaul			/* Erg, still trying, I guess... */
25312891Swpaul			mii->mii_media_active |= IFM_NONE;
25420818Swpaul			return;
25520818Swpaul		}
25612891Swpaul
25720818Swpaul		mii->mii_media_active |= IFM_1000_SX;
25820818Swpaul		anlpar = PHY_READ(sc, XMPHY_MII_ANAR) &
25912891Swpaul		    PHY_READ(sc, XMPHY_MII_ANLPAR);
26012891Swpaul		if (anlpar & XMPHY_ANLPAR_FDX)
26112891Swpaul			mii->mii_media_active |= IFM_FDX;
26212997Swpaul		else
26312997Swpaul			mii->mii_media_active |= IFM_HDX;
26412997Swpaul		return;
26512997Swpaul	}
26612997Swpaul
26712997Swpaul	mii->mii_media_active |= IFM_1000_SX;
26812997Swpaul	if (bmcr & XMPHY_BMCR_FDX)
26912997Swpaul		mii->mii_media_active |= IFM_FDX;
27012997Swpaul	else
27112997Swpaul		mii->mii_media_active |= IFM_HDX;
27212997Swpaul}
27313375Swpaul
27412997Swpaulstatic int
27513375Swpaulxmphy_mii_phy_auto(struct mii_softc *mii)
27612997Swpaul{
27712997Swpaul	int anar = 0;
27812997Swpaul
27919131Swpaul	anar = PHY_READ(mii, XMPHY_MII_ANAR);
28019131Swpaul	anar |= XMPHY_ANAR_FDX|XMPHY_ANAR_HDX;
28119161Swpaul	PHY_WRITE(mii, XMPHY_MII_ANAR, anar);
28219131Swpaul	DELAY(1000);
28319131Swpaul	PHY_WRITE(mii, XMPHY_MII_BMCR,
28412997Swpaul	    XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG);
28512997Swpaul
28612997Swpaul	return (EJUSTRETURN);
28712997Swpaul}
28813375Swpaul