inphy.c revision 76483
1251881Speter/*-
2251881Speter * Copyright (c) 2001 Jonathan Lemon
3251881Speter * All rights reserved.
4251881Speter *
5251881Speter * Redistribution and use in source and binary forms, with or without
6251881Speter * modification, are permitted provided that the following conditions
7251881Speter * are met:
8251881Speter * 1. Redistributions of source code must retain the above copyright
9251881Speter *    notice, this list of conditions and the following disclaimer.
10251881Speter * 2. Redistributions in binary form must reproduce the above copyright
11251881Speter *    notice, this list of conditions and the following disclaimer in the
12251881Speter *    documentation and/or other materials provided with the distribution.
13251881Speter * 3. Neither the name of the author nor the names of any co-contributors
14251881Speter *    may be used to endorse or promote products derived from this software
15251881Speter *    without specific prior written permission.
16251881Speter *
17251881Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20251881Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27251881Speter * SUCH DAMAGE.
28251881Speter *
29251881Speter *	$FreeBSD: head/sys/dev/mii/inphy.c 76483 2001-05-11 20:34:38Z jlemon $
30251881Speter */
31251881Speter
32251881Speter/*
33251881Speter * driver for Intel 82553 and 82555 PHYs
34251881Speter */
35251881Speter
36251881Speter#include <sys/param.h>
37251881Speter#include <sys/systm.h>
38251881Speter#include <sys/kernel.h>
39251881Speter#include <sys/malloc.h>
40251881Speter#include <sys/socket.h>
41251881Speter#include <sys/bus.h>
42251881Speter
43251881Speter#include <net/if.h>
44251881Speter#include <net/if_media.h>
45251881Speter
46251881Speter#include <dev/mii/mii.h>
47251881Speter#include <dev/mii/miivar.h>
48251881Speter#include <dev/mii/miidevs.h>
49251881Speter
50251881Speter#include <dev/mii/inphyreg.h>
51251881Speter
52251881Speter#include "miibus_if.h"
53251881Speter
54251881Speterstatic int 	inphy_probe(device_t dev);
55251881Speterstatic int 	inphy_attach(device_t dev);
56251881Speterstatic int 	inphy_detach(device_t dev);
57251881Speter
58251881Speterstatic device_method_t inphy_methods[] = {
59251881Speter	/* device interface */
60251881Speter	DEVMETHOD(device_probe,		inphy_probe),
61251881Speter	DEVMETHOD(device_attach,	inphy_attach),
62251881Speter	DEVMETHOD(device_detach,	inphy_detach),
63251881Speter	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
64251881Speter	{ 0, 0 }
65251881Speter};
66251881Speter
67251881Speterstatic devclass_t inphy_devclass;
68251881Speter
69251881Speterstatic driver_t inphy_driver = {
70251881Speter	"inphy",
71251881Speter	inphy_methods,
72251881Speter	sizeof(struct mii_softc)
73251881Speter};
74251881Speter
75251881SpeterDRIVER_MODULE(inphy, miibus, inphy_driver, inphy_devclass, 0, 0);
76251881Speter
77251881Speterint	inphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd);
78251881Spetervoid	inphy_status(struct mii_softc *sc);
79251881Speter
80251881Speter
81251881Speterstatic int
82251881Speterinphy_probe(device_t dev)
83251881Speter{
84251881Speter	struct mii_attach_args *ma;
85251881Speter	device_t parent;
86251881Speter
87251881Speter	ma = device_get_ivars(dev);
88251881Speter	parent = device_get_parent(device_get_parent(dev));
89251881Speter
90251881Speter	/* Intel 82553 A/B steppings */
91251881Speter	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxINTEL &&
92251881Speter	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxINTEL_I82553AB) {
93251881Speter		device_set_desc(dev, MII_STR_xxINTEL_I82553AB);
94251881Speter		return (0);
95251881Speter	}
96251881Speter
97251881Speter	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_INTEL) {
98251881Speter		switch (MII_MODEL(ma->mii_id2)) {
99251881Speter		case MII_MODEL_INTEL_I82555:
100251881Speter			device_set_desc(dev, MII_STR_INTEL_I82555);
101251881Speter			return (0);
102251881Speter		case MII_MODEL_INTEL_I82553C:
103251881Speter			device_set_desc(dev, MII_STR_INTEL_I82553C);
104251881Speter			return (0);
105251881Speter		case MII_MODEL_INTEL_I82562EM:
106251881Speter			device_set_desc(dev, MII_STR_INTEL_I82562EM);
107251881Speter			return (0);
108251881Speter		case MII_MODEL_INTEL_I82562ET:
109251881Speter			device_set_desc(dev, MII_STR_INTEL_I82562ET);
110251881Speter			return (0);
111251881Speter		}
112251881Speter	}
113251881Speter
114251881Speter	return (ENXIO);
115251881Speter}
116251881Speter
117251881Speterstatic int
118251881Speterinphy_attach(device_t dev)
119251881Speter{
120251881Speter	struct mii_softc *sc;
121251881Speter	struct mii_attach_args *ma;
122251881Speter	struct mii_data *mii;
123251881Speter
124251881Speter	sc = device_get_softc(dev);
125251881Speter	ma = device_get_ivars(dev);
126251881Speter	sc->mii_dev = device_get_parent(dev);
127251881Speter	mii = device_get_softc(sc->mii_dev);
128251881Speter	LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
129251881Speter
130251881Speter	sc->mii_inst = mii->mii_instance;
131251881Speter	sc->mii_phy = ma->mii_phyno;
132251881Speter	sc->mii_service = inphy_service;
133251881Speter	sc->mii_pdata = mii;
134251881Speter	mii->mii_instance++;
135251881Speter
136251881Speter#if 0
137251881Speter	sc->mii_flags |= MIIF_NOISOLATE;
138251881Speter#endif
139251881Speter
140251881Speter	ifmedia_add(&mii->mii_media,
141251881Speter	    IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
142251881Speter	    BMCR_LOOP|BMCR_S100, NULL);
143251881Speter
144251881Speter	mii_phy_reset(sc);
145251881Speter
146251881Speter	sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
147251881Speter	device_printf(dev, " ");
148251881Speter	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
149251881Speter		printf("no media present");
150251881Speter	else
151251881Speter		mii_add_media(mii, sc->mii_capabilities, sc->mii_inst);
152251881Speter	printf("\n");
153251881Speter
154251881Speter	MIIBUS_MEDIAINIT(sc->mii_dev);
155251881Speter
156251881Speter	return (0);
157251881Speter}
158251881Speter
159251881Speterstatic int
160251881Speterinphy_detach(device_t dev)
161251881Speter{
162251881Speter	struct mii_softc *sc;
163251881Speter	struct mii_data *mii;
164251881Speter
165251881Speter	sc = device_get_softc(dev);
166251881Speter	mii = device_get_softc(device_get_softc(dev));
167251881Speter	mii_phy_auto_stop(sc);
168251881Speter	sc->mii_dev = NULL;
169251881Speter	LIST_REMOVE(sc, mii_list);
170251881Speter
171251881Speter	return (0);
172251881Speter}
173251881Speter
174251881Speterint
175251881Speterinphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
176251881Speter{
177251881Speter	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178251881Speter	int reg;
179251881Speter
180251881Speter	switch (cmd) {
181251881Speter	case MII_POLLSTAT:
182251881Speter		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
183251881Speter			return (0);
184251881Speter		break;
185251881Speter
186251881Speter	case MII_MEDIACHG:
187251881Speter		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
188251881Speter			reg = PHY_READ(sc, MII_BMCR);
189251881Speter			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
190251881Speter			return (0);
191251881Speter		}
192251881Speter
193251881Speter		/*
194251881Speter		 * If the interface is not up, don't do anything.
195251881Speter		 */
196251881Speter		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
197251881Speter			break;
198251881Speter
199251881Speter		switch (IFM_SUBTYPE(ife->ifm_media)) {
200251881Speter		case IFM_AUTO:
201251881Speter			/*
202251881Speter			 * If we're already in auto mode, just return.
203251881Speter			 */
204251881Speter			if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
205251881Speter				return (0);
206251881Speter			(void) mii_phy_auto(sc, 0);
207251881Speter			break;
208251881Speter		case IFM_100_T4:
209251881Speter			/*
210251881Speter			 * XXX Not supported as a manual setting right now.
211251881Speter			 */
212251881Speter			return (EINVAL);
213251881Speter		default:
214251881Speter			/*
215251881Speter			 * BMCR data is stored in the ifmedia entry.
216251881Speter			 */
217251881Speter			PHY_WRITE(sc, MII_ANAR, mii_anar(ife->ifm_media));
218251881Speter			PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
219251881Speter		}
220251881Speter		break;
221251881Speter
222251881Speter	case MII_TICK:
223251881Speter		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
224251881Speter			return (0);
225251881Speter
226251881Speter		/*
227251881Speter		 * Is the interface even up?
228251881Speter		 */
229251881Speter		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
230251881Speter			return (0);
231251881Speter
232251881Speter		/*
233251881Speter		 * Only used for autonegotiation.
234251881Speter		 */
235251881Speter		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
236251881Speter			return (0);
237251881Speter
238251881Speter		/*
239251881Speter		 * check for link.
240251881Speter        	 * Read the status register twice; BMSR_LINK is latch-low.
241251881Speter		 */
242251881Speter        	reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
243251881Speter		if (reg & BMSR_LINK)
244251881Speter	                return (0);
245251881Speter
246251881Speter		/*
247251881Speter		 * Only retry autonegotiation every 5 seconds.
248251881Speter		 */
249251881Speter		if (++sc->mii_ticks != 5)
250251881Speter			return (0);
251251881Speter
252251881Speter		sc->mii_ticks = 0;
253251881Speter		mii_phy_reset(sc);
254251881Speter		if (mii_phy_auto(sc, 0) == EJUSTRETURN)
255251881Speter			return (0);
256251881Speter		break;
257251881Speter	}
258251881Speter
259251881Speter	/* Update the media status. */
260251881Speter	inphy_status(sc);
261251881Speter
262251881Speter	/* Callback if something changed. */
263251881Speter	if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
264251881Speter		MIIBUS_STATCHG(sc->mii_dev);
265251881Speter		sc->mii_active = mii->mii_media_active;
266251881Speter	}
267251881Speter	return (0);
268251881Speter}
269251881Speter
270251881Spetervoid
271251881Speterinphy_status(struct mii_softc *sc)
272251881Speter{
273251881Speter	struct mii_data *mii = sc->mii_pdata;
274251881Speter	int bmsr, bmcr, scr;
275251881Speter
276251881Speter	mii->mii_media_status = IFM_AVALID;
277251881Speter	mii->mii_media_active = IFM_ETHER;
278251881Speter
279251881Speter	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
280251881Speter	if (bmsr & BMSR_LINK)
281251881Speter		mii->mii_media_status |= IFM_ACTIVE;
282251881Speter
283251881Speter	bmcr = PHY_READ(sc, MII_BMCR);
284251881Speter	if (bmcr & BMCR_ISO) {
285251881Speter		mii->mii_media_active |= IFM_NONE;
286251881Speter		mii->mii_media_status = 0;
287251881Speter		return;
288251881Speter	}
289251881Speter
290251881Speter	if (bmcr & BMCR_LOOP)
291251881Speter		mii->mii_media_active |= IFM_LOOP;
292251881Speter
293251881Speter	if (bmcr & BMCR_AUTOEN) {
294251881Speter		if ((bmsr & BMSR_ACOMP) == 0) {
295251881Speter			mii->mii_media_active |= IFM_NONE;
296251881Speter			return;
297251881Speter		}
298251881Speter
299251881Speter		scr = PHY_READ(sc, MII_INPHY_SCR);
300251881Speter		if (scr & SCR_S100)
301251881Speter			mii->mii_media_active |= IFM_100_TX;
302251881Speter		else
303251881Speter			mii->mii_media_active |= IFM_10_T;
304251881Speter		if (scr & SCR_FDX)
305251881Speter			mii->mii_media_active |= IFM_FDX;
306251881Speter	} else
307251881Speter		mii->mii_media_active |= mii_media_from_bmcr(bmcr);
308251881Speter}
309251881Speter