1160638Syongari/*-
2160638Syongari * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org>
3160638Syongari * All rights reserved.
4160638Syongari *
5160638Syongari * Redistribution and use in source and binary forms, with or without
6160638Syongari * modification, are permitted provided that the following conditions
7160638Syongari * are met:
8160638Syongari * 1. Redistributions of source code must retain the above copyright
9160638Syongari *    notice unmodified, this list of conditions, and the following
10160638Syongari *    disclaimer.
11160638Syongari * 2. Redistributions in binary form must reproduce the above copyright
12160638Syongari *    notice, this list of conditions and the following disclaimer in the
13160638Syongari *    documentation and/or other materials provided with the distribution.
14160638Syongari *
15160638Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16160638Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17160638Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18160638Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19160638Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20160638Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21160638Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22160638Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23160638Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24160638Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25160638Syongari * SUCH DAMAGE.
26160638Syongari *
27160638Syongari */
28160638Syongari
29160638Syongari#include <sys/cdefs.h>
30160638Syongari__FBSDID("$FreeBSD$");
31160638Syongari
32160638Syongari/*
33177930Syongari * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY.
34160638Syongari */
35160638Syongari
36160638Syongari#include <sys/param.h>
37160638Syongari#include <sys/systm.h>
38160638Syongari#include <sys/kernel.h>
39160638Syongari#include <sys/module.h>
40160638Syongari#include <sys/socket.h>
41160638Syongari#include <sys/bus.h>
42160638Syongari
43160638Syongari#include <net/if.h>
44160638Syongari#include <net/if_media.h>
45160638Syongari
46160638Syongari#include <dev/mii/mii.h>
47160638Syongari#include <dev/mii/miivar.h>
48160638Syongari#include "miidevs.h"
49160638Syongari
50160638Syongari#include <dev/mii/ip1000phyreg.h>
51160638Syongari
52160638Syongari#include "miibus_if.h"
53160638Syongari
54160638Syongari#include <machine/bus.h>
55160638Syongari#include <dev/stge/if_stgereg.h>
56160638Syongari
57160638Syongaristatic int ip1000phy_probe(device_t);
58160638Syongaristatic int ip1000phy_attach(device_t);
59160638Syongari
60160638Syongaristatic device_method_t ip1000phy_methods[] = {
61160638Syongari	/* device interface */
62160638Syongari	DEVMETHOD(device_probe,		ip1000phy_probe),
63160638Syongari	DEVMETHOD(device_attach,	ip1000phy_attach),
64160638Syongari	DEVMETHOD(device_detach,	mii_phy_detach),
65160638Syongari	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
66229093Shselasky	DEVMETHOD_END
67160638Syongari};
68160638Syongari
69160638Syongaristatic devclass_t ip1000phy_devclass;
70160638Syongaristatic driver_t ip1000phy_driver = {
71160638Syongari	"ip1000phy",
72160638Syongari	ip1000phy_methods,
73221747Smarius	sizeof(struct mii_softc)
74160638Syongari};
75160638Syongari
76160638SyongariDRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0);
77160638Syongari
78160638Syongaristatic int	ip1000phy_service(struct mii_softc *, struct mii_data *, int);
79160638Syongaristatic void	ip1000phy_status(struct mii_softc *);
80160638Syongaristatic void	ip1000phy_reset(struct mii_softc *);
81215297Smariusstatic int	ip1000phy_mii_phy_auto(struct mii_softc *, int);
82160638Syongari
83160638Syongaristatic const struct mii_phydesc ip1000phys[] = {
84221407Smarius	MII_PHY_DESC(xxICPLUS, IP1000A),
85221407Smarius	MII_PHY_DESC(xxICPLUS, IP1001),
86160638Syongari	MII_PHY_END
87160638Syongari};
88160638Syongari
89221407Smariusstatic const struct mii_phy_funcs ip1000phy_funcs = {
90221407Smarius	ip1000phy_service,
91221407Smarius	ip1000phy_status,
92221407Smarius	ip1000phy_reset
93221407Smarius};
94221407Smarius
95160638Syongaristatic int
96160638Syongariip1000phy_probe(device_t dev)
97160638Syongari{
98160638Syongari
99164827Smarius	return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT));
100160638Syongari}
101160638Syongari
102160638Syongaristatic int
103160638Syongariip1000phy_attach(device_t dev)
104160638Syongari{
105160638Syongari	struct mii_attach_args *ma;
106221407Smarius	u_int flags;
107160638Syongari
108160638Syongari	ma = device_get_ivars(dev);
109221407Smarius	flags = MIIF_NOISOLATE | MIIF_NOMANPAUSE;
110221407Smarius	if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxICPLUS_IP1000A &&
111221407Smarius	     strcmp(ma->mii_data->mii_ifp->if_dname, "stge") == 0 &&
112221407Smarius	     (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0)
113221407Smarius		flags |= MIIF_PHYPRIV0;
114221746Smarius	mii_phy_dev_attach(dev, flags, &ip1000phy_funcs, 1);
115213364Smarius	return (0);
116160638Syongari}
117160638Syongari
118160638Syongaristatic int
119160638Syongariip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
120160638Syongari{
121160638Syongari	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
122160638Syongari	uint32_t gig, reg, speed;
123160638Syongari
124160638Syongari	switch (cmd) {
125160638Syongari	case MII_POLLSTAT:
126160638Syongari		break;
127160638Syongari
128160638Syongari	case MII_MEDIACHG:
129160638Syongari		/*
130160638Syongari		 * If the interface is not up, don't do anything.
131160638Syongari		 */
132160638Syongari		if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
133160638Syongari			break;
134160638Syongari		}
135160638Syongari
136221407Smarius		PHY_RESET(sc);
137160638Syongari		switch (IFM_SUBTYPE(ife->ifm_media)) {
138160638Syongari		case IFM_AUTO:
139215297Smarius			(void)ip1000phy_mii_phy_auto(sc, ife->ifm_media);
140160638Syongari			goto done;
141160638Syongari
142160638Syongari		case IFM_1000_T:
143160638Syongari			/*
144160638Syongari			 * XXX
145160638Syongari			 * Manual 1000baseT setting doesn't seem to work.
146160638Syongari			 */
147160638Syongari			speed = IP1000PHY_BMCR_1000;
148160638Syongari			break;
149160638Syongari
150160638Syongari		case IFM_100_TX:
151160638Syongari			speed = IP1000PHY_BMCR_100;
152160638Syongari			break;
153160638Syongari
154160638Syongari		case IFM_10_T:
155160638Syongari			speed = IP1000PHY_BMCR_10;
156160638Syongari			break;
157160638Syongari
158160638Syongari		default:
159160638Syongari			return (EINVAL);
160160638Syongari		}
161160638Syongari
162217412Smarius		if ((ife->ifm_media & IFM_FDX) != 0) {
163160638Syongari			speed |= IP1000PHY_BMCR_FDX;
164160638Syongari			gig = IP1000PHY_1000CR_1000T_FDX;
165160638Syongari		} else
166160638Syongari			gig = IP1000PHY_1000CR_1000T;
167160638Syongari
168217412Smarius		if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
169217412Smarius			gig |=
170217412Smarius			    IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL;
171217412Smarius			if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
172217412Smarius				gig |= IP1000PHY_1000CR_MMASTER;
173217412Smarius		} else
174217412Smarius			gig = 0;
175217412Smarius		PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig);
176160638Syongari		PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed);
177160638Syongari
178160638Syongaridone:
179160638Syongari		break;
180160638Syongari
181160638Syongari	case MII_TICK:
182160638Syongari		/*
183160638Syongari		 * Is the interface even up?
184160638Syongari		 */
185160638Syongari		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
186160638Syongari			return (0);
187160638Syongari
188160638Syongari		/*
189160638Syongari		 * Only used for autonegotiation.
190160638Syongari		 */
191160638Syongari		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
192160638Syongari			sc->mii_ticks = 0;
193160638Syongari			break;
194160638Syongari		}
195160638Syongari
196160638Syongari		/*
197160638Syongari		 * check for link.
198160638Syongari		 */
199160638Syongari		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
200160638Syongari		if (reg & BMSR_LINK) {
201160638Syongari			sc->mii_ticks = 0;
202160638Syongari			break;
203160638Syongari		}
204160638Syongari
205160638Syongari		/* Announce link loss right after it happens */
206160638Syongari		if (sc->mii_ticks++ == 0)
207160638Syongari			break;
208160638Syongari
209160638Syongari		/*
210160638Syongari		 * Only retry autonegotiation every mii_anegticks seconds.
211160638Syongari		 */
212160638Syongari		if (sc->mii_ticks <= sc->mii_anegticks)
213189564Syongari			break;
214160638Syongari
215160638Syongari		sc->mii_ticks = 0;
216215297Smarius		ip1000phy_mii_phy_auto(sc, ife->ifm_media);
217160638Syongari		break;
218160638Syongari	}
219160638Syongari
220160638Syongari	/* Update the media status. */
221221407Smarius	PHY_STATUS(sc);
222160638Syongari
223160638Syongari	/* Callback if something changed. */
224160638Syongari	mii_phy_update(sc, cmd);
225160638Syongari	return (0);
226160638Syongari}
227160638Syongari
228160638Syongaristatic void
229160638Syongariip1000phy_status(struct mii_softc *sc)
230160638Syongari{
231160638Syongari	struct mii_data *mii = sc->mii_pdata;
232160638Syongari	uint32_t bmsr, bmcr, stat;
233160638Syongari
234160638Syongari	mii->mii_media_status = IFM_AVALID;
235160638Syongari	mii->mii_media_active = IFM_ETHER;
236160638Syongari
237160638Syongari	bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) |
238160638Syongari	    PHY_READ(sc, IP1000PHY_MII_BMSR);
239160638Syongari	if ((bmsr & IP1000PHY_BMSR_LINK) != 0)
240160638Syongari		mii->mii_media_status |= IFM_ACTIVE;
241160638Syongari
242160638Syongari	bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR);
243160638Syongari	if ((bmcr & IP1000PHY_BMCR_LOOP) != 0)
244160638Syongari		mii->mii_media_active |= IFM_LOOP;
245160638Syongari
246160638Syongari	if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) {
247160638Syongari		if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) {
248160638Syongari			/* Erg, still trying, I guess... */
249160638Syongari			mii->mii_media_active |= IFM_NONE;
250160638Syongari			return;
251160638Syongari                }
252160638Syongari        }
253160638Syongari
254221407Smarius	if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) {
255177930Syongari		stat = PHY_READ(sc, IP1000PHY_LSR);
256177930Syongari		switch (stat & IP1000PHY_LSR_SPEED_MASK) {
257177930Syongari		case IP1000PHY_LSR_SPEED_10:
258177930Syongari			mii->mii_media_active |= IFM_10_T;
259177930Syongari			break;
260177930Syongari		case IP1000PHY_LSR_SPEED_100:
261177930Syongari			mii->mii_media_active |= IFM_100_TX;
262177930Syongari			break;
263177930Syongari		case IP1000PHY_LSR_SPEED_1000:
264177930Syongari			mii->mii_media_active |= IFM_1000_T;
265177930Syongari			break;
266189565Syongari		default:
267189565Syongari			mii->mii_media_active |= IFM_NONE;
268189565Syongari			return;
269177930Syongari		}
270177930Syongari		if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0)
271177930Syongari			mii->mii_media_active |= IFM_FDX;
272177930Syongari		else
273177930Syongari			mii->mii_media_active |= IFM_HDX;
274177930Syongari	} else {
275177930Syongari		stat = PHY_READ(sc, STGE_PhyCtrl);
276177930Syongari		switch (PC_LinkSpeed(stat)) {
277177930Syongari		case PC_LinkSpeed_Down:
278177930Syongari			mii->mii_media_active |= IFM_NONE;
279177930Syongari			return;
280177930Syongari		case PC_LinkSpeed_10:
281177930Syongari			mii->mii_media_active |= IFM_10_T;
282177930Syongari			break;
283177930Syongari		case PC_LinkSpeed_100:
284177930Syongari			mii->mii_media_active |= IFM_100_TX;
285177930Syongari			break;
286177930Syongari		case PC_LinkSpeed_1000:
287177930Syongari			mii->mii_media_active |= IFM_1000_T;
288177930Syongari			break;
289189565Syongari		default:
290189565Syongari			mii->mii_media_active |= IFM_NONE;
291189565Syongari			return;
292177930Syongari		}
293177930Syongari		if ((stat & PC_PhyDuplexStatus) != 0)
294177930Syongari			mii->mii_media_active |= IFM_FDX;
295177930Syongari		else
296177930Syongari			mii->mii_media_active |= IFM_HDX;
297160638Syongari	}
298160638Syongari
299215297Smarius	if ((mii->mii_media_active & IFM_FDX) != 0)
300215297Smarius		mii->mii_media_active |= mii_phy_flowstatus(sc);
301160638Syongari
302160638Syongari	if ((mii->mii_media_active & IFM_1000_T) != 0) {
303160638Syongari		stat = PHY_READ(sc, IP1000PHY_MII_1000SR);
304160638Syongari		if ((stat & IP1000PHY_1000SR_MASTER) != 0)
305215297Smarius			mii->mii_media_active |= IFM_ETH_MASTER;
306160638Syongari	}
307160638Syongari}
308160638Syongari
309160638Syongaristatic int
310215297Smariusip1000phy_mii_phy_auto(struct mii_softc *sc, int media)
311160638Syongari{
312160638Syongari	uint32_t reg;
313160638Syongari
314189567Syongari	reg = 0;
315221407Smarius	if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) {
316189567Syongari		reg = PHY_READ(sc, IP1000PHY_MII_ANAR);
317215923Smarius		reg &= ~(IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE);
318200693Syongari		reg |= IP1000PHY_ANAR_NP;
319200693Syongari	}
320189567Syongari	reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX |
321215297Smarius	    IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX;
322215297Smarius	if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
323215297Smarius		reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE;
324189567Syongari	PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA);
325189567Syongari
326160638Syongari	reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX;
327229543Syongari	if (sc->mii_mpd_model != MII_MODEL_xxICPLUS_IP1001)
328229543Syongari		reg |= IP1000PHY_1000CR_MASTER;
329189567Syongari	PHY_WRITE(sc, IP1000PHY_MII_1000CR, reg);
330189567Syongari	PHY_WRITE(sc, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX |
331160638Syongari	    IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG));
332160638Syongari
333160638Syongari	return (EJUSTRETURN);
334160638Syongari}
335160638Syongari
336160638Syongaristatic void
337160638Syongariip1000phy_load_dspcode(struct mii_softc *sc)
338160638Syongari{
339160638Syongari
340160638Syongari	PHY_WRITE(sc, 31, 0x0001);
341160638Syongari	PHY_WRITE(sc, 27, 0x01e0);
342160638Syongari	PHY_WRITE(sc, 31, 0x0002);
343160638Syongari	PHY_WRITE(sc, 27, 0xeb8e);
344160638Syongari	PHY_WRITE(sc, 31, 0x0000);
345160638Syongari	PHY_WRITE(sc, 30, 0x005e);
346160638Syongari	PHY_WRITE(sc, 9, 0x0700);
347160638Syongari
348160638Syongari	DELAY(50);
349160638Syongari}
350160638Syongari
351160638Syongaristatic void
352160638Syongariip1000phy_reset(struct mii_softc *sc)
353160638Syongari{
354160638Syongari	uint32_t reg;
355160638Syongari
356160638Syongari	mii_phy_reset(sc);
357160638Syongari
358160638Syongari	/* clear autoneg/full-duplex as we don't want it after reset */
359160638Syongari	reg = PHY_READ(sc, IP1000PHY_MII_BMCR);
360160638Syongari	reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX);
361160638Syongari	PHY_WRITE(sc, MII_BMCR, reg);
362160638Syongari
363213893Smarius	if ((sc->mii_flags & MIIF_PHYPRIV0) != 0)
364213893Smarius		ip1000phy_load_dspcode(sc);
365160638Syongari}
366