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$");
35119418Sobrien
3659477Swpaul/*
3759477Swpaul * driver for the XaQti XMAC II's internal PHY. This is sort of
3859477Swpaul * like a 10/100 PHY, except the only thing we're really autoselecting
3959477Swpaul * here is full/half duplex. Speed is always 1000mbps.
4059477Swpaul */
4159477Swpaul
4259477Swpaul#include <sys/param.h>
4359477Swpaul#include <sys/systm.h>
4459477Swpaul#include <sys/kernel.h>
45129876Sphk#include <sys/module.h>
4659477Swpaul#include <sys/socket.h>
4759477Swpaul#include <sys/bus.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>
54109514Sobrien#include "miidevs.h"
5559477Swpaul
5659477Swpaul#include <dev/mii/xmphyreg.h>
5759477Swpaul
5859477Swpaul#include "miibus_if.h"
5959477Swpaul
60105135Salfredstatic int xmphy_probe(device_t);
61105135Salfredstatic int xmphy_attach(device_t);
6259477Swpaul
6359477Swpaulstatic device_method_t xmphy_methods[] = {
6459477Swpaul	/* device interface */
6559477Swpaul	DEVMETHOD(device_probe,		xmphy_probe),
6659477Swpaul	DEVMETHOD(device_attach,	xmphy_attach),
6795722Sphk	DEVMETHOD(device_detach,	mii_phy_detach),
6859477Swpaul	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
69227908Smarius	DEVMETHOD_END
7059477Swpaul};
7159477Swpaul
7259477Swpaulstatic devclass_t xmphy_devclass;
7359477Swpaul
7459477Swpaulstatic driver_t xmphy_driver = {
7559477Swpaul	"xmphy",
7659477Swpaul	xmphy_methods,
7759477Swpaul	sizeof(struct mii_softc)
7859477Swpaul};
7959477Swpaul
8059477SwpaulDRIVER_MODULE(xmphy, miibus, xmphy_driver, xmphy_devclass, 0, 0);
8159477Swpaul
8292739Salfredstatic int	xmphy_service(struct mii_softc *, struct mii_data *, int);
8392739Salfredstatic void	xmphy_status(struct mii_softc *);
8496026Sphkstatic int	xmphy_mii_phy_auto(struct mii_softc *);
8559477Swpaul
86164827Smariusstatic const struct mii_phydesc xmphys[] = {
87221407Smarius	MII_PHY_DESC(xxJATO, BASEX),
88221407Smarius	MII_PHY_DESC(xxXAQTI, XMACII),
89164827Smarius	MII_PHY_END
90164827Smarius};
91164827Smarius
92221407Smariusstatic const struct mii_phy_funcs xmphy_funcs = {
93221407Smarius	xmphy_service,
94221407Smarius	xmphy_status,
95221407Smarius	mii_phy_reset
96221407Smarius};
97221407Smarius
98105135Salfredstatic int
99150763Simpxmphy_probe(device_t dev)
10059477Swpaul{
10159477Swpaul
102164827Smarius	return (mii_phy_dev_probe(dev, xmphys, BUS_PROBE_DEFAULT));
10359477Swpaul}
10459477Swpaul
105105135Salfredstatic int
106150763Simpxmphy_attach(device_t dev)
10759477Swpaul{
10859477Swpaul	struct mii_softc *sc;
10959477Swpaul	const char *sep = "";
11059477Swpaul
11159477Swpaul	sc = device_get_softc(dev);
11259477Swpaul
113221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
114221407Smarius	    &xmphy_funcs, 0);
115213364Smarius	sc->mii_anegticks = MII_ANEGTICKS;
11659477Swpaul
117221407Smarius	PHY_RESET(sc);
118213364Smarius
119221407Smarius#define	ADD(m, c)	ifmedia_add(&sc->mii_pdata->mii_media, (m), (c), NULL)
12059477Swpaul#define PRINT(s)	printf("%s%s", sep, s); sep = ", "
12159477Swpaul
12259477Swpaul	device_printf(dev, " ");
12359477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst),
12459477Swpaul	    XMPHY_BMCR_FDX);
12559477Swpaul	PRINT("1000baseSX");
12659477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 0);
12759477Swpaul	PRINT("1000baseSX-FDX");
12859477Swpaul	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
12959477Swpaul	PRINT("auto");
13059477Swpaul
13159477Swpaul	printf("\n");
132221407Smarius
13359477Swpaul#undef ADD
13459477Swpaul#undef PRINT
13559477Swpaul
13659477Swpaul	MIIBUS_MEDIAINIT(sc->mii_dev);
137164830Smarius	return (0);
13859477Swpaul}
13959477Swpaul
14084145Sjlemonstatic int
141150763Simpxmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
14259477Swpaul{
14359477Swpaul	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
14459477Swpaul	int reg;
14559477Swpaul
14659477Swpaul	switch (cmd) {
14759477Swpaul	case MII_POLLSTAT:
14859477Swpaul		break;
14959477Swpaul
15059477Swpaul	case MII_MEDIACHG:
15159477Swpaul		/*
15259477Swpaul		 * If the interface is not up, don't do anything.
15359477Swpaul		 */
15459477Swpaul		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
15559477Swpaul			break;
15659477Swpaul
15759477Swpaul		switch (IFM_SUBTYPE(ife->ifm_media)) {
15859477Swpaul		case IFM_AUTO:
15977078Swpaul#ifdef foo
16059477Swpaul			/*
16159477Swpaul			 * If we're already in auto mode, just return.
16259477Swpaul			 */
16359477Swpaul			if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN)
16459477Swpaul				return (0);
16577078Swpaul#endif
166215300Smarius			(void)xmphy_mii_phy_auto(sc);
16759477Swpaul			break;
16859477Swpaul		case IFM_1000_SX:
169221407Smarius			PHY_RESET(sc);
170217413Smarius			if ((ife->ifm_media & IFM_FDX) != 0) {
17159477Swpaul				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX);
17259477Swpaul				PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX);
17359477Swpaul			} else {
17459477Swpaul				PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX);
17559477Swpaul				PHY_WRITE(sc, XMPHY_MII_BMCR, 0);
17659477Swpaul			}
17759477Swpaul			break;
17859477Swpaul		default:
17959477Swpaul			return (EINVAL);
18059477Swpaul		}
18159477Swpaul		break;
18259477Swpaul
18359477Swpaul	case MII_TICK:
18459477Swpaul		/*
18584145Sjlemon		 * Is the interface even up?
18684145Sjlemon		 */
18784145Sjlemon		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
18884145Sjlemon			return (0);
18984145Sjlemon
19084145Sjlemon		/*
19159477Swpaul		 * Only used for autonegotiation.
19259477Swpaul		 */
19359477Swpaul		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
19484145Sjlemon			break;
19559477Swpaul
19659477Swpaul		/*
19784145Sjlemon		 * Check to see if we have link.  If we do, we don't
19884145Sjlemon		 * need to restart the autonegotiation process.  Read
19984145Sjlemon		 * the BMSR twice in case it's latched.
20059477Swpaul		 */
20184145Sjlemon		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
20284145Sjlemon		if (reg & BMSR_LINK)
20384145Sjlemon			break;
20459477Swpaul
205213364Smarius		/* Only retry autonegotiation every mii_anegticks seconds. */
206213364Smarius		if (sc->mii_ticks <= sc->mii_anegticks)
207128870Sandre			break;
208164830Smarius
20959477Swpaul		sc->mii_ticks = 0;
21059477Swpaul
211221407Smarius		PHY_RESET(sc);
21296026Sphk		xmphy_mii_phy_auto(sc);
213164830Smarius		return (0);
21459477Swpaul	}
21559477Swpaul
21659477Swpaul	/* Update the media status. */
21759477Swpaul	xmphy_status(sc);
21859477Swpaul
21959477Swpaul	/* Callback if something changed. */
22084145Sjlemon	mii_phy_update(sc, cmd);
22159477Swpaul	return (0);
22259477Swpaul}
22359477Swpaul
22484145Sjlemonstatic void
225150763Simpxmphy_status(struct mii_softc *sc)
22659477Swpaul{
22759477Swpaul	struct mii_data *mii = sc->mii_pdata;
22859477Swpaul	int bmsr, bmcr, anlpar;
22959477Swpaul
23059477Swpaul	mii->mii_media_status = IFM_AVALID;
23159477Swpaul	mii->mii_media_active = IFM_ETHER;
23259477Swpaul
23359477Swpaul	bmsr = PHY_READ(sc, XMPHY_MII_BMSR) |
23459477Swpaul	    PHY_READ(sc, XMPHY_MII_BMSR);
23559477Swpaul	if (bmsr & XMPHY_BMSR_LINK)
23659477Swpaul		mii->mii_media_status |= IFM_ACTIVE;
23759477Swpaul
23859477Swpaul	/* Do dummy read of extended status register. */
23959477Swpaul	bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS);
24059477Swpaul
24159477Swpaul	bmcr = PHY_READ(sc, XMPHY_MII_BMCR);
24259477Swpaul
24359477Swpaul	if (bmcr & XMPHY_BMCR_LOOP)
24459477Swpaul		mii->mii_media_active |= IFM_LOOP;
24559477Swpaul
24659477Swpaul	if (bmcr & XMPHY_BMCR_AUTOEN) {
24759477Swpaul		if ((bmsr & XMPHY_BMSR_ACOMP) == 0) {
24859477Swpaul			if (bmsr & XMPHY_BMSR_LINK) {
24959477Swpaul				mii->mii_media_active |= IFM_1000_SX|IFM_HDX;
25059477Swpaul				return;
25159477Swpaul			}
25259477Swpaul			/* Erg, still trying, I guess... */
25359477Swpaul			mii->mii_media_active |= IFM_NONE;
25459477Swpaul			return;
25559477Swpaul		}
25659477Swpaul
25759477Swpaul		mii->mii_media_active |= IFM_1000_SX;
25859477Swpaul		anlpar = PHY_READ(sc, XMPHY_MII_ANAR) &
25959477Swpaul		    PHY_READ(sc, XMPHY_MII_ANLPAR);
26059477Swpaul		if (anlpar & XMPHY_ANLPAR_FDX)
26159477Swpaul			mii->mii_media_active |= IFM_FDX;
26259477Swpaul		else
26359477Swpaul			mii->mii_media_active |= IFM_HDX;
26459477Swpaul		return;
26559477Swpaul	}
26659477Swpaul
26759477Swpaul	mii->mii_media_active |= IFM_1000_SX;
26859477Swpaul	if (bmcr & XMPHY_BMCR_FDX)
26959477Swpaul		mii->mii_media_active |= IFM_FDX;
27059477Swpaul	else
27159477Swpaul		mii->mii_media_active |= IFM_HDX;
27259477Swpaul}
27359477Swpaul
27459477Swpaulstatic int
275150763Simpxmphy_mii_phy_auto(struct mii_softc *mii)
27659477Swpaul{
27796026Sphk	int anar = 0;
27859477Swpaul
27996026Sphk	anar = PHY_READ(mii, XMPHY_MII_ANAR);
28096026Sphk	anar |= XMPHY_ANAR_FDX|XMPHY_ANAR_HDX;
28196026Sphk	PHY_WRITE(mii, XMPHY_MII_ANAR, anar);
28296026Sphk	DELAY(1000);
28396026Sphk	PHY_WRITE(mii, XMPHY_MII_BMCR,
28496026Sphk	    XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG);
28559477Swpaul
28659477Swpaul	return (EJUSTRETURN);
28759477Swpaul}
288