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$");
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>
43257184Sglebius#include <net/if_var.h>
44179335Syongari#include <net/if_media.h>
45179335Syongari
46179335Syongari#include <dev/mii/mii.h>
47179335Syongari#include <dev/mii/miivar.h>
48179335Syongari#include "miidevs.h"
49179335Syongari
50179335Syongari#include <dev/mii/jmphyreg.h>
51179335Syongari
52179335Syongari#include "miibus_if.h"
53179335Syongari
54215298Smariusstatic int	jmphy_probe(device_t);
55215298Smariusstatic int	jmphy_attach(device_t);
56179335Syongaristatic void	jmphy_reset(struct mii_softc *);
57179335Syongaristatic uint16_t	jmphy_anar(struct ifmedia_entry *);
58215298Smariusstatic int	jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *);
59179335Syongari
60179335Syongaristatic device_method_t jmphy_methods[] = {
61179335Syongari	/* Device interface. */
62179335Syongari	DEVMETHOD(device_probe,		jmphy_probe),
63179335Syongari	DEVMETHOD(device_attach,	jmphy_attach),
64179335Syongari	DEVMETHOD(device_detach,	mii_phy_detach),
65179335Syongari	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
66227908Smarius	DEVMETHOD_END
67179335Syongari};
68179335Syongari
69179335Syongaristatic devclass_t jmphy_devclass;
70179335Syongaristatic driver_t jmphy_driver = {
71179335Syongari	"jmphy",
72179335Syongari	jmphy_methods,
73221407Smarius	sizeof(struct mii_softc)
74179335Syongari};
75179335Syongari
76179335SyongariDRIVER_MODULE(jmphy, miibus, jmphy_driver, jmphy_devclass, 0, 0);
77179335Syongari
78179335Syongaristatic int	jmphy_service(struct mii_softc *, struct mii_data *, int);
79179335Syongaristatic void	jmphy_status(struct mii_softc *);
80179335Syongari
81179335Syongaristatic const struct mii_phydesc jmphys[] = {
82179335Syongari	MII_PHY_DESC(JMICRON, JMP202),
83179335Syongari	MII_PHY_DESC(JMICRON, JMP211),
84179335Syongari	MII_PHY_END
85179335Syongari};
86179335Syongari
87221407Smariusstatic const struct mii_phy_funcs jmphy_funcs = {
88221407Smarius	jmphy_service,
89221407Smarius	jmphy_status,
90221407Smarius	jmphy_reset
91221407Smarius};
92221407Smarius
93179335Syongaristatic int
94179335Syongarijmphy_probe(device_t dev)
95179335Syongari{
96179335Syongari
97179335Syongari	return (mii_phy_dev_probe(dev, jmphys, BUS_PROBE_DEFAULT));
98179335Syongari}
99179335Syongari
100179335Syongaristatic int
101179335Syongarijmphy_attach(device_t dev)
102179335Syongari{
103221407Smarius	u_int flags;
104179335Syongari
105221407Smarius	flags = 0;
106277093Sglebius	if (mii_dev_mac_match(dev, "jme") &&
107221407Smarius	    (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
108221407Smarius		flags |= MIIF_PHYPRIV0;
109221407Smarius	mii_phy_dev_attach(dev, flags, &jmphy_funcs, 1);
110213364Smarius	return (0);
111179335Syongari}
112179335Syongari
113179335Syongaristatic int
114179335Syongarijmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
115179335Syongari{
116179335Syongari	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
117179335Syongari
118179335Syongari	switch (cmd) {
119179335Syongari	case MII_POLLSTAT:
120179335Syongari		break;
121179335Syongari
122179335Syongari	case MII_MEDIACHG:
123215298Smarius		if (jmphy_setmedia(sc, ife) != EJUSTRETURN)
124179335Syongari			return (EINVAL);
125179335Syongari		break;
126179335Syongari
127179335Syongari	case MII_TICK:
128179335Syongari		/*
129179335Syongari		 * Only used for autonegotiation.
130179335Syongari		 */
131179335Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
132179335Syongari			sc->mii_ticks = 0;
133179335Syongari			break;
134179335Syongari		}
135179335Syongari
136179335Syongari		/* Check for link. */
137179335Syongari		if ((PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) != 0) {
138179335Syongari			sc->mii_ticks = 0;
139179335Syongari			break;
140179335Syongari		}
141179335Syongari
142179335Syongari		/* Announce link loss right after it happens. */
143179335Syongari		if (sc->mii_ticks++ == 0)
144179335Syongari			break;
145179335Syongari		if (sc->mii_ticks <= sc->mii_anegticks)
146179335Syongari			return (0);
147179335Syongari
148179335Syongari		sc->mii_ticks = 0;
149215298Smarius		(void)jmphy_setmedia(sc, ife);
150179335Syongari		break;
151179335Syongari	}
152179335Syongari
153179335Syongari	/* Update the media status. */
154221407Smarius	PHY_STATUS(sc);
155179335Syongari
156179335Syongari	/* Callback if something changed. */
157179335Syongari	mii_phy_update(sc, cmd);
158179335Syongari	return (0);
159179335Syongari}
160179335Syongari
161179335Syongaristatic void
162179335Syongarijmphy_status(struct mii_softc *sc)
163179335Syongari{
164179335Syongari	struct mii_data *mii = sc->mii_pdata;
165179335Syongari	int bmcr, ssr;
166179335Syongari
167179335Syongari	mii->mii_media_status = IFM_AVALID;
168179335Syongari	mii->mii_media_active = IFM_ETHER;
169179335Syongari
170179335Syongari	ssr = PHY_READ(sc, JMPHY_SSR);
171179335Syongari	if ((ssr & JMPHY_SSR_LINK_UP) != 0)
172179335Syongari		mii->mii_media_status |= IFM_ACTIVE;
173179335Syongari
174179335Syongari	bmcr = PHY_READ(sc, MII_BMCR);
175179335Syongari	if ((bmcr & BMCR_ISO) != 0) {
176179335Syongari		mii->mii_media_active |= IFM_NONE;
177179335Syongari		mii->mii_media_status = 0;
178179335Syongari		return;
179179335Syongari	}
180179335Syongari
181179335Syongari	if ((bmcr & BMCR_LOOP) != 0)
182179335Syongari		mii->mii_media_active |= IFM_LOOP;
183179335Syongari
184179335Syongari	if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
185179335Syongari		/* Erg, still trying, I guess... */
186179335Syongari		mii->mii_media_active |= IFM_NONE;
187179335Syongari		return;
188179335Syongari	}
189179335Syongari
190179335Syongari	switch ((ssr & JMPHY_SSR_SPEED_MASK)) {
191179335Syongari	case JMPHY_SSR_SPEED_1000:
192179335Syongari		mii->mii_media_active |= IFM_1000_T;
193179335Syongari		/*
194179335Syongari		 * jmphy(4) got a valid link so reset mii_ticks.
195179335Syongari		 * Resetting mii_ticks is needed in order to
196179335Syongari		 * detect link loss after auto-negotiation.
197179335Syongari		 */
198179335Syongari		sc->mii_ticks = 0;
199179335Syongari		break;
200179335Syongari	case JMPHY_SSR_SPEED_100:
201179335Syongari		mii->mii_media_active |= IFM_100_TX;
202179335Syongari		sc->mii_ticks = 0;
203179335Syongari		break;
204179335Syongari	case JMPHY_SSR_SPEED_10:
205179335Syongari		mii->mii_media_active |= IFM_10_T;
206179335Syongari		sc->mii_ticks = 0;
207179335Syongari		break;
208179335Syongari	default:
209179335Syongari		mii->mii_media_active |= IFM_NONE;
210179335Syongari		return;
211179335Syongari	}
212179335Syongari
213179335Syongari	if ((ssr & JMPHY_SSR_DUPLEX) != 0)
214215298Smarius		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
215179335Syongari	else
216179335Syongari		mii->mii_media_active |= IFM_HDX;
217215298Smarius
218179335Syongari	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
219179335Syongari		if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
220179335Syongari			mii->mii_media_active |= IFM_ETH_MASTER;
221179335Syongari	}
222179335Syongari}
223179335Syongari
224179335Syongaristatic void
225179335Syongarijmphy_reset(struct mii_softc *sc)
226179335Syongari{
227216551Syongari	uint16_t t2cr, val;
228179335Syongari	int i;
229179335Syongari
230179335Syongari	/* Disable sleep mode. */
231179335Syongari	PHY_WRITE(sc, JMPHY_TMCTL,
232179335Syongari	    PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB);
233179335Syongari	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN);
234179335Syongari
235179335Syongari	for (i = 0; i < 1000; i++) {
236179335Syongari		DELAY(1);
237179335Syongari		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
238179335Syongari			break;
239179335Syongari	}
240216551Syongari	/* Perform vendor recommended PHY calibration. */
241216551Syongari	if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) {
242216551Syongari		/* Select PHY test mode 1. */
243216551Syongari		t2cr = PHY_READ(sc, MII_100T2CR);
244216551Syongari		t2cr &= ~GTCR_TEST_MASK;
245216551Syongari		t2cr |= 0x2000;
246216551Syongari		PHY_WRITE(sc, MII_100T2CR, t2cr);
247216551Syongari		/* Apply calibration patch. */
248216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_READ |
249216551Syongari		    JMPHY_EXT_COMM_2);
250216551Syongari		val = PHY_READ(sc, JMPHY_SPEC_DATA);
251216551Syongari		val &= ~0x0002;
252216551Syongari		val |= 0x0010 | 0x0001;
253216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
254216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
255216551Syongari		    JMPHY_EXT_COMM_2);
256216551Syongari
257216551Syongari		/* XXX 20ms to complete recalibration. */
258216551Syongari		DELAY(20 * 1000);
259216551Syongari
260216551Syongari		PHY_READ(sc, MII_100T2CR);
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 &= ~(0x0001 | 0x0002 | 0x0010);
265216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_DATA, val);
266216551Syongari		PHY_WRITE(sc, JMPHY_SPEC_ADDR, JMPHY_SPEC_ADDR_WRITE |
267216551Syongari		    JMPHY_EXT_COMM_2);
268216551Syongari		/* Disable PHY test mode. */
269216551Syongari		PHY_READ(sc, MII_100T2CR);
270216551Syongari		t2cr &= ~GTCR_TEST_MASK;
271216551Syongari		PHY_WRITE(sc, MII_100T2CR, t2cr);
272216551Syongari	}
273179335Syongari}
274179335Syongari
275179335Syongaristatic uint16_t
276179335Syongarijmphy_anar(struct ifmedia_entry *ife)
277179335Syongari{
278179335Syongari	uint16_t anar;
279179335Syongari
280179335Syongari	anar = 0;
281179335Syongari	switch (IFM_SUBTYPE(ife->ifm_media)) {
282179335Syongari	case IFM_AUTO:
283179335Syongari		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
284179335Syongari		break;
285179335Syongari	case IFM_1000_T:
286179335Syongari		break;
287179335Syongari	case IFM_100_TX:
288179335Syongari		anar |= ANAR_TX | ANAR_TX_FD;
289179335Syongari		break;
290179335Syongari	case IFM_10_T:
291179335Syongari		anar |= ANAR_10 | ANAR_10_FD;
292179335Syongari		break;
293179335Syongari	default:
294179335Syongari		break;
295179335Syongari	}
296179335Syongari
297179335Syongari	return (anar);
298179335Syongari}
299179335Syongari
300179335Syongaristatic int
301215298Smariusjmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife)
302179335Syongari{
303179335Syongari	uint16_t anar, bmcr, gig;
304179335Syongari
305179335Syongari	gig = 0;
306179335Syongari	bmcr = PHY_READ(sc, MII_BMCR);
307179335Syongari	switch (IFM_SUBTYPE(ife->ifm_media)) {
308179335Syongari	case IFM_AUTO:
309179335Syongari		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
310179335Syongari		break;
311179335Syongari	case IFM_1000_T:
312179335Syongari		gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX;
313179335Syongari		break;
314179335Syongari	case IFM_100_TX:
315179335Syongari	case IFM_10_T:
316179335Syongari		break;
317179335Syongari	case IFM_NONE:
318179335Syongari		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN);
319179335Syongari		return (EJUSTRETURN);
320179335Syongari	default:
321179335Syongari		return (EINVAL);
322179335Syongari	}
323179335Syongari
324179335Syongari	anar = jmphy_anar(ife);
325217414Smarius	if ((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
326215298Smarius	    (ife->ifm_media & IFM_FDX) != 0) &&
327217414Smarius	    ((ife->ifm_media & IFM_FLOW) != 0 ||
328217414Smarius	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
329215298Smarius		anar |= ANAR_PAUSE_TOWARDS;
330179335Syongari
331179335Syongari	if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) {
332215298Smarius		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
333215298Smarius			gig |= GTCR_MAN_MS;
334215298Smarius			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
335215298Smarius				gig |= GTCR_ADV_MS;
336215298Smarius		}
337179335Syongari		PHY_WRITE(sc, MII_100T2CR, gig);
338179335Syongari	}
339179335Syongari	PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
340179335Syongari	PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG);
341179335Syongari
342179335Syongari	return (EJUSTRETURN);
343179335Syongari}
344