tdkphy.c revision 119418
143412Snewton/*
243412Snewton * Copyright (c) 2000,2001 Jonathan Chen.
343412Snewton * All rights reserved.
443412Snewton *
543412Snewton * Redistribution and use in source and binary forms, with or without
643412Snewton * modification, are permitted provided that the following conditions
743412Snewton * are met:
843412Snewton * 1. Redistributions of source code must retain the above copyright
943412Snewton *    notice, this list of conditions, and the following disclaimer,
1043412Snewton *    without modification, immediately at the beginning of the file.
1143412Snewton * 2. Redistributions in binary form must reproduce the above copyright
1243412Snewton *    notice, this list of conditions and the following disclaimer in
1343412Snewton *    the documentation and/or other materials provided with the
1443412Snewton *    distribution.
1543412Snewton *
1643412Snewton * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1743412Snewton * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1843412Snewton * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1997748Sschweikh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2043412Snewton * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2143412Snewton * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2243412Snewton * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2343412Snewton * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2443412Snewton * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2543412Snewton * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2643412Snewton * SUCH DAMAGE.
2743412Snewton */
2843412Snewton
2943412Snewton#include <sys/cdefs.h>
3043412Snewton__FBSDID("$FreeBSD: head/sys/dev/mii/tdkphy.c 119418 2003-08-24 17:55:58Z obrien $");
3143412Snewton
3243412Snewton/*
33116174Sobrien * Driver for the TDK 78Q2120 MII
34116174Sobrien *
35116174Sobrien * References:
3643412Snewton *   Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf
3743412Snewton *   Most of this code stolen from ukphy.c
3843412Snewton */
3943412Snewton
4043412Snewton/*
4143412Snewton * The TDK 78Q2120 is found on some Xircom X3201 based cardbus cards.  It's just
4243412Snewton * like any other normal phy, except it does auto negotiation in a different
4376166Smarkm * way.
4476166Smarkm */
4543412Snewton
4676166Smarkm#include <sys/cdefs.h>
4743412Snewton__FBSDID("$FreeBSD: head/sys/dev/mii/tdkphy.c 119418 2003-08-24 17:55:58Z obrien $");
4843412Snewton
4943412Snewton#include <sys/param.h>
5043412Snewton#include <sys/systm.h>
5143412Snewton#include <sys/kernel.h>
5243412Snewton#include <sys/socket.h>
5343412Snewton#include <sys/errno.h>
5443412Snewton#include <sys/module.h>
5543412Snewton#include <sys/bus.h>
5665302Sobrien
5743412Snewton#include <net/if.h>
5892761Salfred#include <net/if_media.h>
5943412Snewton
6043412Snewton#include <machine/clock.h>
6143412Snewton
6243412Snewton#include <dev/mii/mii.h>
6343412Snewton#include <dev/mii/miivar.h>
6443412Snewton#include "miidevs.h"
6543412Snewton
6643412Snewton#include <dev/mii/tdkphyreg.h>
6743412Snewton
6843412Snewton#include "miibus_if.h"
6943412Snewton
7043412Snewton#if 0
71101771Sjeff#if !defined(lint)
7243412Snewtonstatic const char rcsid[] =
7343412Snewton  "$Id: tdkphy.c,v 1.3 2000/10/14 06:20:56 jon Exp $";
7443412Snewton#endif
7543412Snewton#endif
7643412Snewton
7743412Snewtonstatic int tdkphy_probe(device_t);
7843412Snewtonstatic int tdkphy_attach(device_t);
7943412Snewton
8043412Snewtonstatic device_method_t tdkphy_methods[] = {
8143412Snewton	/* device interface */
8243412Snewton	DEVMETHOD(device_probe,		tdkphy_probe),
8343412Snewton	DEVMETHOD(device_attach,	tdkphy_attach),
8443412Snewton	DEVMETHOD(device_detach,	mii_phy_detach),
8543412Snewton	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
8643412Snewton	{ 0, 0 }
8743412Snewton};
8843412Snewton
8943412Snewtonstatic devclass_t tdkphy_devclass;
9043412Snewton
9143412Snewtonstatic driver_t tdkphy_driver = {
9243412Snewton	"tdkphy",
93131013Sobrien	tdkphy_methods,
9443412Snewton	sizeof(struct mii_softc)
9543412Snewton};
9643412Snewton
9743412SnewtonDRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0);
9843412Snewton
9943412Snewtonstatic int tdkphy_service(struct mii_softc *, struct mii_data *, int);
10043412Snewtonstatic void tdkphy_status(struct mii_softc *);
10143412Snewton
10243412Snewtonstatic int
10343412Snewtontdkphy_probe(device_t dev)
10443412Snewton{
10543412Snewton        struct mii_attach_args *ma;
10643412Snewton        ma = device_get_ivars(dev);
10743412Snewton 	if ((MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_TDK ||
10843412Snewton	     MII_MODEL(ma->mii_id2) != MII_MODEL_TDK_78Q2120))
10943412Snewton		return (ENXIO);
110125454Sjhb
11184783Sps	device_set_desc(dev, MII_STR_TDK_78Q2120);
112125454Sjhb	return (0);
113125454Sjhb}
11443412Snewton
115125454Sjhbstatic int
116125454Sjhbtdkphy_attach(device_t dev)
11743412Snewton{
118101771Sjeff	struct mii_softc *sc;
119101771Sjeff	struct mii_attach_args *ma;
12043412Snewton	struct mii_data *mii;
12143412Snewton	sc = device_get_softc(dev);
12243412Snewton	ma = device_get_ivars(dev);
123173361Skib	sc->mii_dev = device_get_parent(dev);
124173361Skib	mii = device_get_softc(sc->mii_dev);
125173361Skib	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
12643412Snewton
12743412Snewton	if (bootverbose)
12843412Snewton		device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
12943412Snewton		    MII_OUI(ma->mii_id1, ma->mii_id2),
13043412Snewton		    MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
13143412Snewton
13243412Snewton	sc->mii_inst = mii->mii_instance;
13343412Snewton	sc->mii_phy = ma->mii_phyno;
13443412Snewton	sc->mii_service = tdkphy_service;
13580114Sassar	sc->mii_pdata = mii;
13643412Snewton
13743412Snewton	mii->mii_instance++;
13843412Snewton
13943412Snewton	sc->mii_flags |= MIIF_NOISOLATE;
14043412Snewton
14143412Snewton#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
14243412Snewton
14343412Snewton	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
14443412Snewton	    BMCR_ISO);
145101771Sjeff#if 0
14643412Snewton	ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
14743412Snewton	    BMCR_LOOP|BMCR_S100);
14843412Snewton#endif
14943412Snewton
150144501Sjhb	mii_phy_reset(sc);
15143412Snewton
152101771Sjeff	sc->mii_capabilities =
15343412Snewton	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
15443412Snewton	device_printf(dev, " ");
15543412Snewton	mii_add_media(sc);
15643412Snewton	printf("\n");
15743412Snewton#undef ADD
15843412Snewton
15943412Snewton	MIIBUS_MEDIAINIT(sc->mii_dev);
16043412Snewton
161101771Sjeff	return(0);
16243412Snewton}
16343412Snewton
16443412Snewtonstatic int
16543412Snewtontdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
16643412Snewton{
16743412Snewton	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
16843412Snewton	int reg;
16943412Snewton
17043412Snewton	switch (cmd) {
17143412Snewton	case MII_POLLSTAT:
172101771Sjeff		/*
17343412Snewton		 * If we're not polling our PHY instance, just return.
17443412Snewton		 */
17543412Snewton		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
17680114Sassar			return (0);
17743412Snewton		break;
17843412Snewton
17943412Snewton	case MII_MEDIACHG:
18043412Snewton		/*
18143412Snewton		 * If the media indicates a different PHY instance,
18243412Snewton		 * isolate ourselves.
18343412Snewton		 */
18443412Snewton		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
18543412Snewton			reg = PHY_READ(sc, MII_BMCR);
18643412Snewton			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
187144501Sjhb			return (0);
18843412Snewton		}
189101771Sjeff
19043412Snewton		/*
19143412Snewton		 * If the interface is not up, don't do anything.
19280114Sassar		 */
193131013Sobrien		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
19443412Snewton			break;
19543412Snewton
19643412Snewton		switch (IFM_SUBTYPE(ife->ifm_media)) {
19743412Snewton		case IFM_AUTO:
19843412Snewton			/*
19943412Snewton			 * If we're already in auto mode, just return.
20043412Snewton			 */
20143412Snewton			if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
20243412Snewton				return (0);
20343412Snewton			(void) mii_phy_auto(sc);
204101771Sjeff			break;
20543412Snewton		case IFM_100_T4:
20643412Snewton			/*
20743412Snewton			 * Not supported on MII
20843412Snewton			 */
20943412Snewton			return (EINVAL);
21043412Snewton		default:
21143412Snewton			/*
21243412Snewton			 * BMCR data is stored in the ifmedia entry.
21343412Snewton			 */
214101771Sjeff			PHY_WRITE(sc, MII_ANAR,
21543412Snewton			    mii_anar(ife->ifm_media));
21680114Sassar			PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
21780114Sassar		}
21843412Snewton		break;
21943412Snewton
22043412Snewton	case MII_TICK:
22143412Snewton		/*
22243412Snewton		 * If we're not currently selected, just return.
22343412Snewton		 */
22443412Snewton		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
22543412Snewton			return (0);
22643412Snewton		if (mii_phy_tick(sc) == EJUSTRETURN)
22743412Snewton			return (0);
22843412Snewton		break;
22943412Snewton	}
23043412Snewton
23143412Snewton	/* Update the media status. */
23243412Snewton	tdkphy_status(sc);
233101771Sjeff	if (sc->mii_pdata->mii_media_active & IFM_FDX)
234175202Sattilio		PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX);
235101771Sjeff	else
23643412Snewton		PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX);
23743412Snewton
23843412Snewton	/* Callback if something changed. */
23943412Snewton	mii_phy_update(sc, cmd);
24043412Snewton	return (0);
24146889Speter}
24245738Speter
24343412Snewtonstatic void
244tdkphy_status(struct mii_softc *phy)
245{
246	struct mii_data *mii = phy->mii_pdata;
247	int bmsr, bmcr, anlpar, diag;
248
249	mii->mii_media_status = IFM_AVALID;
250	mii->mii_media_active = IFM_ETHER;
251
252	bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
253	if (bmsr & BMSR_LINK)
254		mii->mii_media_status |= IFM_ACTIVE;
255
256	bmcr = PHY_READ(phy, MII_BMCR);
257	if (bmcr & BMCR_ISO) {
258		mii->mii_media_active |= IFM_NONE;
259		mii->mii_media_status = 0;
260		return;
261	}
262
263	if (bmcr & BMCR_LOOP)
264		mii->mii_media_active |= IFM_LOOP;
265
266	if (bmcr & BMCR_AUTOEN) {
267		/*
268		 * NWay autonegotiation takes the highest-order common
269		 * bit of the ANAR and ANLPAR (i.e. best media advertised
270		 * both by us and our link partner).
271		 */
272		if ((bmsr & BMSR_ACOMP) == 0) {
273			/* Erg, still trying, I guess... */
274			mii->mii_media_active |= IFM_NONE;
275			return;
276		}
277
278		anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR);
279		/*
280		 * ANLPAR doesn't get set on my card, but we check it anyway,
281		 * since it is mentioned in the 78Q2120 specs.
282		 */
283		if (anlpar & ANLPAR_T4)
284			mii->mii_media_active |= IFM_100_T4;
285		else if (anlpar & ANLPAR_TX_FD)
286			mii->mii_media_active |= IFM_100_TX|IFM_FDX;
287		else if (anlpar & ANLPAR_TX)
288			mii->mii_media_active |= IFM_100_TX;
289		else if (anlpar & ANLPAR_10_FD)
290			mii->mii_media_active |= IFM_10_T|IFM_FDX;
291		else if (anlpar & ANLPAR_10)
292			mii->mii_media_active |= IFM_10_T;
293		else {
294			/*
295			 * ANLPAR isn't set, which leaves two possibilities:
296			 * 1) Auto negotiation failed
297			 * 2) Auto negotiation completed, but the card forgot
298			 *    to set ANLPAR.
299			 * So we check the MII_DIAG(18) register...
300			 */
301			diag = PHY_READ(phy, MII_DIAG);
302			if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */
303				mii->mii_media_active |= IFM_10_T;
304			else {
305				if (diag & DIAG_DUPLEX)
306					mii->mii_media_active |= IFM_FDX;
307				if (diag & DIAG_RATE_100)
308					mii->mii_media_active |= IFM_100_TX;
309				else
310					mii->mii_media_active |= IFM_10_T;
311			}
312		}
313	} else {
314		mii->mii_media_active = mii_media_from_bmcr(bmcr);
315	}
316}
317