1179098Syongari/*-
2179098Syongari * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3179098Syongari * All rights reserved.
4179098Syongari *
5179098Syongari * Redistribution and use in source and binary forms, with or without
6179098Syongari * modification, are permitted provided that the following conditions
7179098Syongari * are met:
8179098Syongari * 1. Redistributions of source code must retain the above copyright
9179098Syongari *    notice unmodified, this list of conditions, and the following
10179098Syongari *    disclaimer.
11179098Syongari * 2. Redistributions in binary form must reproduce the above copyright
12179098Syongari *    notice, this list of conditions and the following disclaimer in the
13179098Syongari *    documentation and/or other materials provided with the distribution.
14179098Syongari *
15179098Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16179098Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17179098Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18179098Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19179098Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20179098Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21179098Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22179098Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23179098Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24179098Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25179098Syongari * SUCH DAMAGE.
26179098Syongari */
27179098Syongari
28179098Syongari#include <sys/cdefs.h>
29179098Syongari__FBSDID("$FreeBSD$");
30179098Syongari
31179098Syongari/*
32179098Syongari * Driver for the Attansic/Atheros F1 10/100/1000 PHY.
33179098Syongari */
34179098Syongari
35179098Syongari#include <sys/param.h>
36179098Syongari#include <sys/systm.h>
37179098Syongari#include <sys/kernel.h>
38179098Syongari#include <sys/module.h>
39179098Syongari#include <sys/socket.h>
40179098Syongari#include <sys/bus.h>
41179098Syongari
42179098Syongari#include <net/if.h>
43179098Syongari#include <net/if_media.h>
44179098Syongari
45179098Syongari#include <dev/mii/mii.h>
46179098Syongari#include <dev/mii/miivar.h>
47179098Syongari#include "miidevs.h"
48179098Syongari
49179098Syongari#include <dev/mii/atphyreg.h>
50179098Syongari
51179098Syongari#include "miibus_if.h"
52179098Syongari
53179098Syongaristatic int atphy_probe(device_t);
54179098Syongaristatic int atphy_attach(device_t);
55179098Syongari
56179098Syongaristatic device_method_t atphy_methods[] = {
57179098Syongari	/* Device interface. */
58179098Syongari	DEVMETHOD(device_probe,		atphy_probe),
59179098Syongari	DEVMETHOD(device_attach,	atphy_attach),
60179098Syongari	DEVMETHOD(device_detach,	mii_phy_detach),
61179098Syongari	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
62227908Smarius	DEVMETHOD_END
63179098Syongari};
64179098Syongari
65179098Syongaristatic devclass_t atphy_devclass;
66179098Syongaristatic driver_t atphy_driver = {
67179098Syongari	"atphy",
68179098Syongari	atphy_methods,
69221407Smarius	sizeof(struct mii_softc)
70179098Syongari};
71179098Syongari
72179098SyongariDRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, 0, 0);
73179098Syongari
74179098Syongaristatic int	atphy_service(struct mii_softc *, struct mii_data *, int);
75179098Syongaristatic void	atphy_status(struct mii_softc *);
76179098Syongaristatic void	atphy_reset(struct mii_softc *);
77179098Syongaristatic uint16_t	atphy_anar(struct ifmedia_entry *);
78215298Smariusstatic int	atphy_setmedia(struct mii_softc *, int);
79179098Syongari
80179098Syongaristatic const struct mii_phydesc atphys[] = {
81221407Smarius	MII_PHY_DESC(xxATHEROS, F1),
82221407Smarius	MII_PHY_DESC(xxATHEROS, F1_7),
83257751Snwhitehorn	MII_PHY_DESC(xxATHEROS, AR8021),
84221407Smarius	MII_PHY_DESC(xxATHEROS, F2),
85179098Syongari	MII_PHY_END
86179098Syongari};
87179098Syongari
88221407Smariusstatic const struct mii_phy_funcs atphy_funcs = {
89221407Smarius	atphy_service,
90221407Smarius	atphy_status,
91221407Smarius	atphy_reset
92221407Smarius};
93221407Smarius
94179098Syongaristatic int
95179098Syongariatphy_probe(device_t dev)
96179098Syongari{
97179098Syongari
98179098Syongari	return (mii_phy_dev_probe(dev, atphys, BUS_PROBE_DEFAULT));
99179098Syongari}
100179098Syongari
101179098Syongaristatic int
102179098Syongariatphy_attach(device_t dev)
103179098Syongari{
104179098Syongari
105221407Smarius	mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &atphy_funcs, 1);
106213364Smarius	return (0);
107179098Syongari}
108179098Syongari
109179098Syongaristatic int
110179098Syongariatphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
111179098Syongari{
112179098Syongari	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
113179098Syongari	uint16_t anar, bmcr, bmsr;
114179098Syongari
115179098Syongari	switch (cmd) {
116179098Syongari	case MII_POLLSTAT:
117179098Syongari		break;
118179098Syongari
119179098Syongari	case MII_MEDIACHG:
120179098Syongari		if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
121179098Syongari		    IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
122215298Smarius			atphy_setmedia(sc, ife->ifm_media);
123179098Syongari			break;
124179098Syongari		}
125179098Syongari
126179098Syongari		bmcr = 0;
127179098Syongari		switch (IFM_SUBTYPE(ife->ifm_media)) {
128179098Syongari		case IFM_100_TX:
129179098Syongari			bmcr = BMCR_S100;
130179098Syongari			break;
131179098Syongari		case IFM_10_T:
132179098Syongari			bmcr = BMCR_S10;
133179098Syongari			break;
134179098Syongari		case IFM_NONE:
135179098Syongari			bmcr = PHY_READ(sc, MII_BMCR);
136179098Syongari			/*
137179098Syongari			 * XXX
138179098Syongari			 * Due to an unknown reason powering down PHY resulted
139215298Smarius			 * in unexpected results such as inaccessibility of
140179098Syongari			 * hardware of freshly rebooted system. Disable
141179098Syongari			 * powering down PHY until I got more information for
142179098Syongari			 * Attansic/Atheros PHY hardwares.
143179098Syongari			 */
144179098Syongari			PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
145179098Syongari			goto done;
146179098Syongari		default:
147179098Syongari			return (EINVAL);
148179098Syongari		}
149179098Syongari
150179098Syongari		anar = atphy_anar(ife);
151217412Smarius		if ((ife->ifm_media & IFM_FDX) != 0) {
152179098Syongari			bmcr |= BMCR_FDX;
153217412Smarius			if ((ife->ifm_media & IFM_FLOW) != 0 ||
154215298Smarius			    (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
155215298Smarius				anar |= ANAR_PAUSE_TOWARDS;
156179098Syongari		}
157179098Syongari
158179098Syongari		if ((sc->mii_extcapabilities & (EXTSR_1000TFDX |
159179098Syongari		    EXTSR_1000THDX)) != 0)
160179098Syongari			PHY_WRITE(sc, MII_100T2CR, 0);
161179098Syongari		PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
162179098Syongari
163179098Syongari		/*
164179098Syongari		 * Reset the PHY so all changes take effect.
165179098Syongari		 */
166184253Syongari		PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN |
167184253Syongari		    BMCR_STARTNEG);
168179098Syongaridone:
169179098Syongari		break;
170179098Syongari
171179098Syongari	case MII_TICK:
172179098Syongari		/*
173179098Syongari		 * Only used for autonegotiation.
174179098Syongari		 */
175179098Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
176179098Syongari			sc->mii_ticks = 0;
177179098Syongari			break;
178179098Syongari		}
179179098Syongari
180179098Syongari		/*
181215298Smarius		 * Check for link.
182179098Syongari		 * Read the status register twice; BMSR_LINK is latch-low.
183179098Syongari		 */
184179098Syongari		bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
185179098Syongari		if (bmsr & BMSR_LINK) {
186179098Syongari			sc->mii_ticks = 0;
187179098Syongari			break;
188179098Syongari		}
189179098Syongari
190179098Syongari		/* Announce link loss right after it happens. */
191179098Syongari		if (sc->mii_ticks++ == 0)
192179098Syongari			break;
193179098Syongari		if (sc->mii_ticks <= sc->mii_anegticks)
194179098Syongari			return (0);
195179098Syongari
196179098Syongari		sc->mii_ticks = 0;
197215298Smarius		atphy_setmedia(sc, ife->ifm_media);
198179098Syongari		break;
199179098Syongari	}
200179098Syongari
201179098Syongari	/* Update the media status. */
202221407Smarius	PHY_STATUS(sc);
203179098Syongari
204179098Syongari	/* Callback if something changed. */
205179098Syongari	mii_phy_update(sc, cmd);
206179098Syongari	return (0);
207179098Syongari}
208179098Syongari
209179098Syongaristatic void
210179098Syongariatphy_status(struct mii_softc *sc)
211179098Syongari{
212179098Syongari	struct mii_data *mii = sc->mii_pdata;
213179098Syongari	uint32_t bmsr, bmcr, ssr;
214179098Syongari
215179098Syongari	mii->mii_media_status = IFM_AVALID;
216179098Syongari	mii->mii_media_active = IFM_ETHER;
217179098Syongari
218179098Syongari	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
219179098Syongari	if ((bmsr & BMSR_LINK) != 0)
220179098Syongari		mii->mii_media_status |= IFM_ACTIVE;
221179098Syongari
222179098Syongari	bmcr = PHY_READ(sc, MII_BMCR);
223179098Syongari	if ((bmcr & BMCR_ISO) != 0) {
224179098Syongari		mii->mii_media_active |= IFM_NONE;
225179098Syongari		mii->mii_media_status = 0;
226179098Syongari		return;
227179098Syongari	}
228179098Syongari
229179098Syongari	if ((bmcr & BMCR_LOOP) != 0)
230179098Syongari		mii->mii_media_active |= IFM_LOOP;
231179098Syongari
232179098Syongari	ssr = PHY_READ(sc, ATPHY_SSR);
233179098Syongari	if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
234179098Syongari		/* Erg, still trying, I guess... */
235179098Syongari		mii->mii_media_active |= IFM_NONE;
236179098Syongari		return;
237179098Syongari	}
238179098Syongari
239179098Syongari	switch (ssr & ATPHY_SSR_SPEED_MASK) {
240179098Syongari	case ATPHY_SSR_1000MBS:
241179098Syongari		mii->mii_media_active |= IFM_1000_T;
242179098Syongari		/*
243215298Smarius		 * atphy(4) has a valid link so reset mii_ticks.
244179098Syongari		 * Resetting mii_ticks is needed in order to
245179098Syongari		 * detect link loss after auto-negotiation.
246179098Syongari		 */
247179098Syongari		sc->mii_ticks = 0;
248179098Syongari		break;
249179098Syongari	case ATPHY_SSR_100MBS:
250179098Syongari		mii->mii_media_active |= IFM_100_TX;
251179098Syongari		sc->mii_ticks = 0;
252179098Syongari		break;
253179098Syongari	case ATPHY_SSR_10MBS:
254179098Syongari		mii->mii_media_active |= IFM_10_T;
255179098Syongari		sc->mii_ticks = 0;
256179098Syongari		break;
257179098Syongari	default:
258179098Syongari		mii->mii_media_active |= IFM_NONE;
259179098Syongari		return;
260179098Syongari	}
261179098Syongari
262179098Syongari	if ((ssr & ATPHY_SSR_DUPLEX) != 0)
263215298Smarius		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
264179098Syongari	else
265179098Syongari		mii->mii_media_active |= IFM_HDX;
266179098Syongari
267215298Smarius	if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
268215298Smarius	    (PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
269215298Smarius		mii->mii_media_active |= IFM_ETH_MASTER;
270179098Syongari}
271179098Syongari
272179098Syongaristatic void
273179098Syongariatphy_reset(struct mii_softc *sc)
274179098Syongari{
275215459Smarius	struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
276179098Syongari	uint32_t reg;
277179098Syongari	int i;
278179098Syongari
279179098Syongari	/* Take PHY out of power down mode. */
280179098Syongari	PHY_WRITE(sc, 29, 0x29);
281179098Syongari	PHY_WRITE(sc, 30, 0);
282179098Syongari
283179098Syongari	reg = PHY_READ(sc, ATPHY_SCR);
284179098Syongari	/* Enable automatic crossover. */
285179098Syongari	reg |= ATPHY_SCR_AUTO_X_MODE;
286179098Syongari	/* Disable power down. */
287179098Syongari	reg &= ~ATPHY_SCR_MAC_PDOWN;
288179098Syongari	/* Enable CRS on Tx. */
289179098Syongari	reg |= ATPHY_SCR_ASSERT_CRS_ON_TX;
290179098Syongari	/* Auto correction for reversed cable polarity. */
291179098Syongari	reg |= ATPHY_SCR_POLARITY_REVERSAL;
292179098Syongari	PHY_WRITE(sc, ATPHY_SCR, reg);
293179098Syongari
294179098Syongari	/* Workaround F1 bug to reset phy. */
295215459Smarius	atphy_setmedia(sc, ife == NULL ? IFM_AUTO : ife->ifm_media);
296179098Syongari
297179098Syongari	for (i = 0; i < 1000; i++) {
298179098Syongari		DELAY(1);
299179098Syongari		if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
300179098Syongari			break;
301179098Syongari	}
302179098Syongari}
303179098Syongari
304179098Syongaristatic uint16_t
305179098Syongariatphy_anar(struct ifmedia_entry *ife)
306179098Syongari{
307179098Syongari	uint16_t anar;
308179098Syongari
309179098Syongari	anar = 0;
310179098Syongari	switch (IFM_SUBTYPE(ife->ifm_media)) {
311179098Syongari	case IFM_AUTO:
312179098Syongari		anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
313179098Syongari		return (anar);
314179098Syongari	case IFM_1000_T:
315179098Syongari		return (anar);
316179098Syongari	case IFM_100_TX:
317179098Syongari		anar |= ANAR_TX;
318179098Syongari		break;
319179098Syongari	case IFM_10_T:
320179098Syongari		anar |= ANAR_10;
321179098Syongari		break;
322179098Syongari	default:
323179098Syongari		return (0);
324179098Syongari	}
325179098Syongari
326217412Smarius	if ((ife->ifm_media & IFM_FDX) != 0) {
327179098Syongari		if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX)
328179098Syongari			anar |= ANAR_TX_FD;
329179098Syongari		else
330179098Syongari			anar |= ANAR_10_FD;
331179098Syongari	}
332179098Syongari
333179098Syongari	return (anar);
334179098Syongari}
335179098Syongari
336179098Syongaristatic int
337215298Smariusatphy_setmedia(struct mii_softc *sc, int media)
338179098Syongari{
339179098Syongari	uint16_t anar;
340179098Syongari
341215298Smarius	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
342217412Smarius	if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) &&
343217412Smarius	    ((media & IFM_FLOW) != 0 ||
344217412Smarius	    (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
345215298Smarius		anar |= ANAR_PAUSE_TOWARDS;
346215298Smarius	PHY_WRITE(sc, MII_ANAR, anar);
347217412Smarius	if ((sc->mii_extcapabilities &
348217412Smarius	     (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
349179098Syongari		PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX |
350179098Syongari		    GTCR_ADV_1000THDX);
351221817Syongari	else if (sc->mii_mpd_model == MII_MODEL_xxATHEROS_F1) {
352221817Syongari		/*
353221817Syongari		 * AR8132 has 10/100 PHY and the PHY uses the same
354221817Syongari		 * model number of F1 gigabit PHY.  The PHY has no
355221817Syongari		 * ability to establish gigabit link so explicitly
356221817Syongari		 * disable 1000baseT configuration for the PHY.
357221817Syongari		 * Otherwise, there is a case that atphy(4) could
358221817Syongari		 * not establish a link against gigabit link partner
359221817Syongari		 * unless the link partner supports down-shifting.
360221817Syongari		 */
361221817Syongari		PHY_WRITE(sc, MII_100T2CR, 0);
362221817Syongari	}
363179098Syongari	PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
364179098Syongari
365179098Syongari	return (EJUSTRETURN);
366179098Syongari}
367