jmphy.c revision 221407
144963Sjb/*-
244963Sjb * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
344963Sjb * All rights reserved.
444963Sjb *
544963Sjb * Redistribution and use in source and binary forms, with or without
644963Sjb * modification, are permitted provided that the following conditions
744963Sjb * are met:
844963Sjb * 1. Redistributions of source code must retain the above copyright
944963Sjb *    notice unmodified, this list of conditions, and the following
1044963Sjb *    disclaimer.
1144963Sjb * 2. Redistributions in binary form must reproduce the above copyright
1244963Sjb *    notice, this list of conditions and the following disclaimer in the
1344963Sjb *    documentation and/or other materials provided with the distribution.
1444963Sjb *
1544963Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1644963Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1744963Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1844963Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1944963Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2044963Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2144963Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2244963Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2344963Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2444963Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2544963Sjb * SUCH DAMAGE.
2644963Sjb */
2744963Sjb
2844963Sjb#include <sys/cdefs.h>
2944963Sjb__FBSDID("$FreeBSD: head/sys/dev/mii/jmphy.c 221407 2011-05-03 19:51:29Z marius $");
3044963Sjb
3144963Sjb/*
3250476Speter * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
3344963Sjb */
34174112Sdeischen
35174112Sdeischen#include <sys/param.h>
3644963Sjb#include <sys/systm.h>
3744963Sjb#include <sys/kernel.h>
3844963Sjb#include <sys/module.h>
3944963Sjb#include <sys/socket.h>
40174112Sdeischen#include <sys/bus.h>
41103388Smini
4244963Sjb#include <net/if.h>
4344963Sjb#include <net/if_media.h>
4444963Sjb
4544963Sjb#include <dev/mii/mii.h>
4648046Sjb#include <dev/mii/miivar.h>
4744963Sjb#include "miidevs.h"
48113658Sdeischen
4948046Sjb#include <dev/mii/jmphyreg.h>
50113658Sdeischen
51113658Sdeischen#include "miibus_if.h"
52113658Sdeischen
53113658Sdeischenstatic int	jmphy_probe(device_t);
5448046Sjbstatic int	jmphy_attach(device_t);
5548046Sjbstatic void	jmphy_reset(struct mii_softc *);
56113658Sdeischenstatic uint16_t	jmphy_anar(struct ifmedia_entry *);
57113658Sdeischenstatic int	jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
5848046Sjb
5948046Sjbstatic device_method_t jmphy_methods[] = {
60113658Sdeischen	/* Device interface. */
61113658Sdeischen	DEVMETHOD(device_probe,		jmphy_probe),
6248046Sjb	DEVMETHOD(device_attach,	jmphy_attach),
6348046Sjb	DEVMETHOD(device_detach,	mii_phy_detach),
64113658Sdeischen	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
65113658Sdeischen	{ NULL, NULL }
6648046Sjb};
6748046Sjb
68113658Sdeischenstatic devclass_t jmphy_devclass;
69113658Sdeischenstatic driver_t jmphy_driver = {
7048046Sjb	"jmphy",
7148046Sjb	jmphy_methods,
7248046Sjb	sizeof(struct mii_softc)
7348046Sjb};
7448046Sjb
75113658SdeischenDRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0);
76113658Sdeischen
77113658Sdeischenstatic int	jmphy_service(struct mii_softc *, struct mii_data *, int);
78113658Sdeischenstatic void	jmphy_status(struct mii_softc *);
79113658Sdeischen
80113658Sdeischenstatic const struct mii_phydesc jmphys[] = {
81113658Sdeischen	MII_PHY_DESC(JMICRON, JMP202),
8248046Sjb	MII_PHY_DESC(JMICRON, JMP211),
8348046Sjb	MII_PHY_END
8448046Sjb};
8544963Sjb
8648046Sjbstatic const struct mii_phy_funcs jmphy_funcs = {
8744963Sjb	jmphy_service,
8855194Sdeischen	jmphy_status,
8944963Sjb	jmphy_reset
9044963Sjb};
9144963Sjb
9244963Sjbstatic int
9344963Sjbjmphy_probe(device_t dev)
9444963Sjb{
9544963Sjb
9644963Sjb	return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
9744963Sjb}
9844963Sjb
9944963Sjbstatic int
10048046Sjbjmphy_attach(device_t dev)
10148046Sjb{
10248046Sjb	struct mii_attach_args *ma;
10348046Sjb	u_int flags;
10448046Sjb
10548046Sjb	ma = device_get_ivars(dev);
10648046Sjb	flags = 0;
107113661Sdeischen	if (strcmp(ma->mii_data->mii_ifp->if_dname, "jme") == 0 &&
108113661Sdeischen	    (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
109113661Sdeischen		flags |= MIIF_PHYPRIV0;
110113661Sdeischen	mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1);
111113661Sdeischen	return (0);
112113661Sdeischen}
113113661Sdeischen
11448046Sjbstatic int
11548046Sjbjmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
11648046Sjb{
11748046Sjb	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
11848046Sjb
11948046Sjb	switch (cmd) {
12048046Sjb	case MII_POLLSTAT:
12148046Sjb		break;
12248046Sjb
12344963Sjb	case MII_MEDIACHG:
12448046Sjb		/*
12544963Sjb		 * If the interface is not up, don't do anything.
12644963Sjb		 */
12744963Sjb		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
12844963Sjb			break;
12944963Sjb
13044963Sjb		if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
131113658Sdeischen			return (EINVAL);
132114187Sdeischen		break;
13344963Sjb
13444963Sjb	case MII_TICK:
13544963Sjb		/*
13644963Sjb		 * Is the interface even up?
13744963Sjb		 */
13844963Sjb		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
13944963Sjb			return (0);
14044963Sjb
14144963Sjb		/*
14248046Sjb		 * Only used for autonegotiation.
14348046Sjb		 */
14448046Sjb		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
145113658Sdeischen			sc->mii_ticks = 0;
146113658Sdeischen			break;
147113658Sdeischen		}
14848046Sjb
14948046Sjb		/* Check for link. */
15048046Sjb		if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
15148046Sjb			sc->mii_ticks = 0;
15248046Sjb			break;
15348046Sjb		}
15448046Sjb
15548046Sjb		/* Announce link loss right after it happens. */
15648046Sjb		if (sc->mii_ticks++ == 0)
15744963Sjb			break;
158114187Sdeischen		if (sc->mii_ticks <= sc->mii_anegticks)
15948046Sjb			return (0);
160113658Sdeischen
161114187Sdeischen		sc->mii_ticks = 0;
162113658Sdeischen		(void)jmphy_setmedia(sc, ife);
16344963Sjb		break;
16444963Sjb	}
16544963Sjb
16644963Sjb	/* Update the media status. */
16744963Sjb	PHY_STATUS(sc);
16844963Sjb
16997204Sdeischen	/* Callback if something changed. */
17044963Sjb	mii_phy_update(sc, cmd);
17148046Sjb	return (0);
172113658Sdeischen}
17348046Sjb
174113658Sdeischenstatic void
175113658Sdeischenjmphy_status(struct mii_softc *sc)
176113658Sdeischen{
177113658Sdeischen	struct mii_data *mii = sc->mii_pdata;
17848046Sjb	int bmcr, ssr;
179113658Sdeischen
180113658Sdeischen	mii->mii_media_status = IFM_AVALID;
181113658Sdeischen	mii->mii_media_active = IFM_ETHER;
182113658Sdeischen
183113658Sdeischen	ssr = PHY_READ(sc, JMPHY_SSR);
184114187Sdeischen	if ((ssr & JMPHY_SSR_LINK_UP) != 0)
185113658Sdeischen		mii->mii_media_status |= IFM_ACTIVE;
186113658Sdeischen
18748046Sjb	bmcr = PHY_READ(sc, MII_BMCR);
188113658Sdeischen	if ((bmcr & BMCR_ISO) != 0) {
18944963Sjb		mii->mii_media_active |= IFM_NONE;
19044963Sjb		mii->mii_media_status = 0;
19144963Sjb		return;
19244963Sjb	}
19344963Sjb
19444963Sjb	if ((bmcr & BMCR_LOOP) != 0)
19597204Sdeischen		mii->mii_media_active |= IFM_LOOP;
19644963Sjb
19748046Sjb	if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
198113658Sdeischen		/* Erg, still trying, I guess... */
19948046Sjb		mii->mii_media_active |= IFM_NONE;
200113658Sdeischen		return;
201113658Sdeischen	}
202113658Sdeischen
203113658Sdeischen	switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
20448046Sjb	case JMPHY_SSR_SPEED_1000:
205113658Sdeischen		mii->mii_media_active |= IFM_1000_T;
206113658Sdeischen		/*
207113658Sdeischen		 * jmphy(4) got a valid link so reset mii_ticks.
208113658Sdeischen		 * Resetting mii_ticks is needed in order to
209113658Sdeischen		 * detect link loss after auto-negotiation.
210114187Sdeischen		 */
211113658Sdeischen		sc->mii_ticks = 0;
212113658Sdeischen		break;
21348046Sjb	case JMPHY_SSR_SPEED_100:
214113658Sdeischen		mii->mii_media_active |= IFM_100_TX;
21544963Sjb		sc->mii_ticks = 0;
21644963Sjb		break;
21744963Sjb	case JMPHY_SSR_SPEED_10:
21844963Sjb		mii->mii_media_active |= IFM_10_T;
21944963Sjb		sc->mii_ticks = 0;
22044963Sjb		break;
22144963Sjb	default:
22244963Sjb		mii->mii_media_active |= IFM_NONE;
22344963Sjb		return;
22448046Sjb	}
22548046Sjb
22648046Sjb	if ((ssr & JMPHY_SSR_DUPLEX) != 0)
227113658Sdeischen		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
228113658Sdeischen	else
22948046Sjb		mii->mii_media_active |= IFM_HDX;
23044963Sjb
23144963Sjb	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
23244963Sjb		if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
23344963Sjb			mii->mii_media_active |= IFM_ETH_MASTER;
23444963Sjb	}
23544963Sjb}
23644963Sjb
23744963Sjbstatic void
23844963Sjbjmphy_reset(struct mii_softc *sc)
23944963Sjb{
24044963Sjb	uint16_t t2cr, val;
24144963Sjb	int i;
24244963Sjb
24348046Sjb	/* Disable sleep mode. */
244113658Sdeischen	PHY_WRITE(sc, JMPHY_TMCTL,
24544963Sjb	    PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
24644963Sjb	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
24744963Sjb
248132120Sdavidxu	for (i = 0; i < 1000; i++) {
249132120Sdavidxu		DELAY(1);
250132120Sdavidxu		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
251132120Sdavidxu			break;
252132120Sdavidxu	}
253132120Sdavidxu	/* Perform vendor recommended PHY calibration. */
254132120Sdavidxu	if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
255132120Sdavidxu		/* Select PHY test mode 1. */
256132120Sdavidxu		t2cr = PHY_READ(sc, MII_100T2CR);
257132120Sdavidxu		t2cr &= ~GTCR_TEST_MASK;
25844963Sjb		t2cr |= 0x2000;
259132120Sdavidxu		PHY_WRITE(sc, MII_100T2CR, t2cr);
260132120Sdavidxu		/* Apply calibration patch. */
261132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
262132120Sdavidxu		    JMPHY_EXT_COMM_2);
263132120Sdavidxu		val = PHY_READ(sc, JMPHY_SPEC_DATA);
264132120Sdavidxu		val &= ~0x0002;
265132120Sdavidxu		val |= 0x0010 | 0x0001;
266132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
267132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
268132120Sdavidxu		    JMPHY_EXT_COMM_2);
269132120Sdavidxu
270132120Sdavidxu		/* XXX 20ms to complete recalibration. */
271132120Sdavidxu		DELAY(20 * 1000);
272132120Sdavidxu
273132120Sdavidxu		PHY_READ(sc, MII_100T2CR);
274132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
275132120Sdavidxu		    JMPHY_EXT_COMM_2);
276132120Sdavidxu		val = PHY_READ(sc, JMPHY_SPEC_DATA);
277132120Sdavidxu		val &= ~(0x0001 | 0x0002 | 0x0010);
278132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
279132120Sdavidxu		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
280133047Sdavidxu		    JMPHY_EXT_COMM_2);
281132120Sdavidxu		/* Disable PHY test mode. */
282132120Sdavidxu		PHY_READ(sc, MII_100T2CR);
283132120Sdavidxu		t2cr &= ~GTCR_TEST_MASK;
284132120Sdavidxu		PHY_WRITE(sc, MII_100T2CR, t2cr);
285132120Sdavidxu	}
286132120Sdavidxu}
287132120Sdavidxu
288132120Sdavidxustatic uint16_t
289132120Sdavidxujmphy_anar(struct ifmedia_entry *ife)
290132120Sdavidxu{
291132120Sdavidxu	uint16_t anar;
292132120Sdavidxu
293132120Sdavidxu	anar = 0;
294132120Sdavidxu	switch (IFM_SUBTYPE(ife->ifm_media)) {
295132120Sdavidxu	case IFM_AUTO:
296132120Sdavidxu		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
297132120Sdavidxu		break;
298132120Sdavidxu	case IFM_1000_T:
299132120Sdavidxu		break;
30044963Sjb	case IFM_100_TX:
30144963Sjb		anar |= ANAR_TX | ANAR_TX_FD;
30244963Sjb		break;
30344963Sjb	case IFM_10_T:
30444963Sjb		anar |= ANAR_10 | ANAR_10_FD;
30544963Sjb		break;
30648046Sjb	default:
30748046Sjb		break;
308113658Sdeischen	}
30948046Sjb
31048046Sjb	return (anar);
31144963Sjb}
31244963Sjb
31348046Sjbstatic int
31444963Sjbjmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
31544963Sjb{
31644963Sjb	uint16_t anar, bmcr, gig;
31744963Sjb
31844963Sjb	gig = 0;
31944963Sjb	bmcr = PHY_READ(sc, MII_BMCR);
32044963Sjb	switch (IFM_SUBTYPE(ife->ifm_media)) {
32144963Sjb	case IFM_AUTO:
32244963Sjb		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
32344963Sjb		break;
32444963Sjb	case IFM_1000_T:
32544963Sjb		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
32644963Sjb		break;
32744963Sjb	case IFM_100_TX:
328	case IFM_10_T:
329		break;
330	case IFM_NONE:
331		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
332		return (EJUSTRETURN);
333	default:
334		return (EINVAL);
335	}
336
337	anar = jmphy_anar(ife);
338	if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
339	    (ife->ifm_media & IFM_FDX) != 0) &&
340	    ((ife->ifm_media & IFM_FLOW) != 0 ||
341	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
342		anar |= ANAR_PAUSE_TOWARDS;
343
344	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
345		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
346			gig |= GTCR_MAN_MS;
347			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
348				gig |= GTCR_ADV_MS;
349		}
350		PHY_WRITE(sc, MII_100T2CR, gig);
351	}
352	PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
353	PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
354
355	return (EJUSTRETURN);
356}
357