jmphy.c revision 221407
1179335Syongari/*-
2179335Syongari * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3179335Syongari * All rights reserved.
4179335Syongari *
5179335Syongari * Redistribution and use in source and binary forms, with or without
6179335Syongari * modification, are permitted provided that the following conditions
7179335Syongari * are met:
8179335Syongari * 1. Redistributions of source code must retain the above copyright
9179335Syongari *    notice unmodified, this list of conditions, and the following
10179335Syongari *    disclaimer.
11179335Syongari * 2. Redistributions in binary form must reproduce the above copyright
12179335Syongari *    notice, this list of conditions and the following disclaimer in the
13179335Syongari *    documentation and/or other materials provided with the distribution.
14179335Syongari *
15179335Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16179335Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17179335Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18179335Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19179335Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20179335Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21179335Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22179335Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23179335Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24179335Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25179335Syongari * SUCH DAMAGE.
26179335Syongari */
27179335Syongari
28179335Syongari#include <sys/cdefs.h>
29179335Syongari__FBSDID("$FreeBSD: head/sys/dev/mii/jmphy.c 221407 2011-05-03 19:51:29Z marius $");
30179335Syongari
31179335Syongari/*
32179335Syongari * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY.
33179335Syongari */
34179335Syongari
35179335Syongari#include <sys/param.h>
36179335Syongari#include <sys/systm.h>
37179335Syongari#include <sys/kernel.h>
38179335Syongari#include <sys/module.h>
39179335Syongari#include <sys/socket.h>
40179335Syongari#include <sys/bus.h>
41179335Syongari
42179335Syongari#include <net/if.h>
43179335Syongari#include <net/if_media.h>
44179335Syongari
45179335Syongari#include <dev/mii/mii.h>
46179335Syongari#include <dev/mii/miivar.h>
47179335Syongari#include "miidevs.h"
48179335Syongari
49179335Syongari#include <dev/mii/jmphyreg.h>
50179335Syongari
51179335Syongari#include "miibus_if.h"
52179335Syongari
53215298Smariusstatic int	jmphy_probe(device_t);
54215298Smariusstatic int	jmphy_attach(device_t);
55179335Syongaristatic void	jmphy_reset(struct mii_softc *);
56179335Syongaristatic uint16_t	jmphy_anar(struct ifmedia_entry *);
57215298Smariusstatic int	jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
58179335Syongari
59179335Syongaristatic device_method_t jmphy_methods[] = {
60179335Syongari	/* Device interface. */
61179335Syongari	DEVMETHOD(device_probe,		jmphy_probe),
62179335Syongari	DEVMETHOD(device_attach,	jmphy_attach),
63179335Syongari	DEVMETHOD(device_detach,	mii_phy_detach),
64179335Syongari	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
65179335Syongari	{ NULL, NULL }
66179335Syongari};
67179335Syongari
68179335Syongaristatic devclass_t jmphy_devclass;
69179335Syongaristatic driver_t jmphy_driver = {
70179335Syongari	"jmphy",
71179335Syongari	jmphy_methods,
72221407Smarius	sizeof(struct mii_softc)
73179335Syongari};
74179335Syongari
75179335SyongariDRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0);
76179335Syongari
77179335Syongaristatic int	jmphy_service(struct mii_softc *, struct mii_data *, int);
78179335Syongaristatic void	jmphy_status(struct mii_softc *);
79179335Syongari
80179335Syongaristatic const struct mii_phydesc jmphys[] = {
81179335Syongari	MII_PHY_DESC(JMICRON, JMP202),
82179335Syongari	MII_PHY_DESC(JMICRON, JMP211),
83179335Syongari	MII_PHY_END
84179335Syongari};
85179335Syongari
86221407Smariusstatic const struct mii_phy_funcs jmphy_funcs = {
87221407Smarius	jmphy_service,
88221407Smarius	jmphy_status,
89221407Smarius	jmphy_reset
90221407Smarius};
91221407Smarius
92179335Syongaristatic int
93179335Syongarijmphy_probe(device_t dev)
94179335Syongari{
95179335Syongari
96179335Syongari	return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
97179335Syongari}
98179335Syongari
99179335Syongaristatic int
100179335Syongarijmphy_attach(device_t dev)
101179335Syongari{
102179335Syongari	struct mii_attach_args *ma;
103221407Smarius	u_int flags;
104179335Syongari
105179335Syongari	ma = device_get_ivars(dev);
106221407Smarius	flags = 0;
107221407Smarius	if (strcmp(ma->mii_data->mii_ifp->if_dname, "jme") == 0 &&
108221407Smarius	    (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
109221407Smarius		flags |= MIIF_PHYPRIV0;
110221407Smarius	mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1);
111213364Smarius	return (0);
112179335Syongari}
113179335Syongari
114179335Syongaristatic int
115179335Syongarijmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
116179335Syongari{
117179335Syongari	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
118179335Syongari
119179335Syongari	switch (cmd) {
120179335Syongari	case MII_POLLSTAT:
121179335Syongari		break;
122179335Syongari
123179335Syongari	case MII_MEDIACHG:
124179335Syongari		/*
125179335Syongari		 * If the interface is not up, don't do anything.
126179335Syongari		 */
127179335Syongari		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
128179335Syongari			break;
129179335Syongari
130215298Smarius		if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
131179335Syongari			return (EINVAL);
132179335Syongari		break;
133179335Syongari
134179335Syongari	case MII_TICK:
135179335Syongari		/*
136179335Syongari		 * Is the interface even up?
137179335Syongari		 */
138179335Syongari		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
139179335Syongari			return (0);
140179335Syongari
141179335Syongari		/*
142179335Syongari		 * Only used for autonegotiation.
143179335Syongari		 */
144179335Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
145179335Syongari			sc->mii_ticks = 0;
146179335Syongari			break;
147179335Syongari		}
148179335Syongari
149179335Syongari		/* Check for link. */
150179335Syongari		if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
151179335Syongari			sc->mii_ticks = 0;
152179335Syongari			break;
153179335Syongari		}
154179335Syongari
155179335Syongari		/* Announce link loss right after it happens. */
156179335Syongari		if (sc->mii_ticks++ == 0)
157179335Syongari			break;
158179335Syongari		if (sc->mii_ticks <= sc->mii_anegticks)
159179335Syongari			return (0);
160179335Syongari
161179335Syongari		sc->mii_ticks = 0;
162215298Smarius		(void)jmphy_setmedia(sc, ife);
163179335Syongari		break;
164179335Syongari	}
165179335Syongari
166179335Syongari	/* Update the media status. */
167221407Smarius	PHY_STATUS(sc);
168179335Syongari
169179335Syongari	/* Callback if something changed. */
170179335Syongari	mii_phy_update(sc, cmd);
171179335Syongari	return (0);
172179335Syongari}
173179335Syongari
174179335Syongaristatic void
175179335Syongarijmphy_status(struct mii_softc *sc)
176179335Syongari{
177179335Syongari	struct mii_data *mii = sc->mii_pdata;
178179335Syongari	int bmcr, ssr;
179179335Syongari
180179335Syongari	mii->mii_media_status = IFM_AVALID;
181179335Syongari	mii->mii_media_active = IFM_ETHER;
182179335Syongari
183179335Syongari	ssr = PHY_READ(sc, JMPHY_SSR);
184179335Syongari	if ((ssr & JMPHY_SSR_LINK_UP) != 0)
185179335Syongari		mii->mii_media_status |= IFM_ACTIVE;
186179335Syongari
187179335Syongari	bmcr = PHY_READ(sc, MII_BMCR);
188179335Syongari	if ((bmcr & BMCR_ISO) != 0) {
189179335Syongari		mii->mii_media_active |= IFM_NONE;
190179335Syongari		mii->mii_media_status = 0;
191179335Syongari		return;
192179335Syongari	}
193179335Syongari
194179335Syongari	if ((bmcr & BMCR_LOOP) != 0)
195179335Syongari		mii->mii_media_active |= IFM_LOOP;
196179335Syongari
197179335Syongari	if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
198179335Syongari		/* Erg, still trying, I guess... */
199179335Syongari		mii->mii_media_active |= IFM_NONE;
200179335Syongari		return;
201179335Syongari	}
202179335Syongari
203179335Syongari	switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
204179335Syongari	case JMPHY_SSR_SPEED_1000:
205179335Syongari		mii->mii_media_active |= IFM_1000_T;
206179335Syongari		/*
207179335Syongari		 * jmphy(4) got a valid link so reset mii_ticks.
208179335Syongari		 * Resetting mii_ticks is needed in order to
209179335Syongari		 * detect link loss after auto-negotiation.
210179335Syongari		 */
211179335Syongari		sc->mii_ticks = 0;
212179335Syongari		break;
213179335Syongari	case JMPHY_SSR_SPEED_100:
214179335Syongari		mii->mii_media_active |= IFM_100_TX;
215179335Syongari		sc->mii_ticks = 0;
216179335Syongari		break;
217179335Syongari	case JMPHY_SSR_SPEED_10:
218179335Syongari		mii->mii_media_active |= IFM_10_T;
219179335Syongari		sc->mii_ticks = 0;
220179335Syongari		break;
221179335Syongari	default:
222179335Syongari		mii->mii_media_active |= IFM_NONE;
223179335Syongari		return;
224179335Syongari	}
225179335Syongari
226179335Syongari	if ((ssr & JMPHY_SSR_DUPLEX) != 0)
227215298Smarius		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
228179335Syongari	else
229179335Syongari		mii->mii_media_active |= IFM_HDX;
230215298Smarius
231179335Syongari	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
232179335Syongari		if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
233179335Syongari			mii->mii_media_active |= IFM_ETH_MASTER;
234179335Syongari	}
235179335Syongari}
236179335Syongari
237179335Syongaristatic void
238179335Syongarijmphy_reset(struct mii_softc *sc)
239179335Syongari{
240216551Syongari	uint16_t t2cr, val;
241179335Syongari	int i;
242179335Syongari
243179335Syongari	/* Disable sleep mode. */
244179335Syongari	PHY_WRITE(sc, JMPHY_TMCTL,
245179335Syongari	    PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
246179335Syongari	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
247179335Syongari
248179335Syongari	for (i = 0; i < 1000; i++) {
249179335Syongari		DELAY(1);
250179335Syongari		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
251179335Syongari			break;
252179335Syongari	}
253216551Syongari	/* Perform vendor recommended PHY calibration. */
254216551Syongari	if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
255216551Syongari		/* Select PHY test mode 1. */
256216551Syongari		t2cr = PHY_READ(sc, MII_100T2CR);
257216551Syongari		t2cr &= ~GTCR_TEST_MASK;
258216551Syongari		t2cr |= 0x2000;
259216551Syongari		PHY_WRITE(sc, MII_100T2CR, t2cr);
260216551Syongari		/* Apply calibration patch. */
261216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
262216551Syongari		    JMPHY_EXT_COMM_2);
263216551Syongari		val = PHY_READ(sc, JMPHY_SPEC_DATA);
264216551Syongari		val &= ~0x0002;
265216551Syongari		val |= 0x0010 | 0x0001;
266216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
267216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
268216551Syongari		    JMPHY_EXT_COMM_2);
269216551Syongari
270216551Syongari		/* XXX 20ms to complete recalibration. */
271216551Syongari		DELAY(20 * 1000);
272216551Syongari
273216551Syongari		PHY_READ(sc, MII_100T2CR);
274216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
275216551Syongari		    JMPHY_EXT_COMM_2);
276216551Syongari		val = PHY_READ(sc, JMPHY_SPEC_DATA);
277216551Syongari		val &= ~(0x0001 | 0x0002 | 0x0010);
278216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
279216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
280216551Syongari		    JMPHY_EXT_COMM_2);
281216551Syongari		/* Disable PHY test mode. */
282216551Syongari		PHY_READ(sc, MII_100T2CR);
283216551Syongari		t2cr &= ~GTCR_TEST_MASK;
284216551Syongari		PHY_WRITE(sc, MII_100T2CR, t2cr);
285216551Syongari	}
286179335Syongari}
287179335Syongari
288179335Syongaristatic uint16_t
289179335Syongarijmphy_anar(struct ifmedia_entry *ife)
290179335Syongari{
291179335Syongari	uint16_t anar;
292179335Syongari
293179335Syongari	anar = 0;
294179335Syongari	switch (IFM_SUBTYPE(ife->ifm_media)) {
295179335Syongari	case IFM_AUTO:
296179335Syongari		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
297179335Syongari		break;
298179335Syongari	case IFM_1000_T:
299179335Syongari		break;
300179335Syongari	case IFM_100_TX:
301179335Syongari		anar |= ANAR_TX | ANAR_TX_FD;
302179335Syongari		break;
303179335Syongari	case IFM_10_T:
304179335Syongari		anar |= ANAR_10 | ANAR_10_FD;
305179335Syongari		break;
306179335Syongari	default:
307179335Syongari		break;
308179335Syongari	}
309179335Syongari
310179335Syongari	return (anar);
311179335Syongari}
312179335Syongari
313179335Syongaristatic int
314215298Smariusjmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
315179335Syongari{
316179335Syongari	uint16_t anar, bmcr, gig;
317179335Syongari
318179335Syongari	gig = 0;
319179335Syongari	bmcr = PHY_READ(sc, MII_BMCR);
320179335Syongari	switch (IFM_SUBTYPE(ife->ifm_media)) {
321179335Syongari	case IFM_AUTO:
322179335Syongari		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
323179335Syongari		break;
324179335Syongari	case IFM_1000_T:
325179335Syongari		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
326179335Syongari		break;
327179335Syongari	case IFM_100_TX:
328179335Syongari	case IFM_10_T:
329179335Syongari		break;
330179335Syongari	case IFM_NONE:
331179335Syongari		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
332179335Syongari		return (EJUSTRETURN);
333179335Syongari	default:
334179335Syongari		return (EINVAL);
335179335Syongari	}
336179335Syongari
337179335Syongari	anar = jmphy_anar(ife);
338217414Smarius	if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
339215298Smarius	    (ife->ifm_media & IFM_FDX) != 0) &&
340217414Smarius	    ((ife->ifm_media & IFM_FLOW) != 0 ||
341217414Smarius	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
342215298Smarius		anar |= ANAR_PAUSE_TOWARDS;
343179335Syongari
344179335Syongari	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
345215298Smarius		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
346215298Smarius			gig |= GTCR_MAN_MS;
347215298Smarius			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
348215298Smarius				gig |= GTCR_ADV_MS;
349215298Smarius		}
350179335Syongari		PHY_WRITE(sc, MII_100T2CR, gig);
351179335Syongari	}
352179335Syongari	PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
353179335Syongari	PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
354179335Syongari
355179335Syongari	return (EJUSTRETURN);
356179335Syongari}
357