FreeBSD/Linux Kernel Cross Reference
sys/dev/mii/e1000phy.c
1 /* $FreeBSD: releng/5.0/sys/dev/mii/e1000phy.c 106107 2002-10-29 00:20:47Z semenu $ */
2 /*
3 * Principal Author: Parag Patel
4 * Copyright (c) 2001
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Additonal Copyright (c) 2001 by Traakan Software under same licence.
30 * Secondary Author: Matthew Jacob
31 */
32
33 /*
34 * driver for the Marvell 88E1000 series external 1000/100/10-BT PHY.
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/socket.h>
41 #include <sys/bus.h>
42
43 #include <machine/clock.h>
44
45 #include <net/if.h>
46 #include <net/if_media.h>
47
48 #include <dev/mii/mii.h>
49 #include <dev/mii/miivar.h>
50 #include <dev/mii/miidevs.h>
51
52 #include <dev/mii/e1000phyreg.h>
53
54 #include "miibus_if.h"
55
56 static int e1000phy_probe(device_t);
57 static int e1000phy_attach(device_t);
58
59 static device_method_t e1000phy_methods[] = {
60 /* device interface */
61 DEVMETHOD(device_probe, e1000phy_probe),
62 DEVMETHOD(device_attach, e1000phy_attach),
63 DEVMETHOD(device_detach, mii_phy_detach),
64 DEVMETHOD(device_shutdown, bus_generic_shutdown),
65 { 0, 0 }
66 };
67
68 static devclass_t e1000phy_devclass;
69 static driver_t e1000phy_driver = {
70 "e1000phy", e1000phy_methods, sizeof (struct mii_softc)
71 };
72 DRIVER_MODULE(e1000phy, miibus, e1000phy_driver, e1000phy_devclass, 0, 0);
73
74 static int e1000phy_service(struct mii_softc *, struct mii_data *, int);
75 static void e1000phy_status(struct mii_softc *);
76 static void e1000phy_reset(struct mii_softc *);
77 static int e1000phy_mii_phy_auto(struct mii_softc *);
78
79 static int e1000phy_debug = 0;
80
81 static int
82 e1000phy_probe(device_t dev)
83 {
84 struct mii_attach_args *ma;
85 u_int32_t id;
86
87 ma = device_get_ivars(dev);
88 id = ((ma->mii_id1 << 16) | ma->mii_id2) & E1000_ID_MASK;
89
90 if (id != E1000_ID_88E1000 && id != E1000_ID_88E1000S) {
91 return ENXIO;
92 }
93
94 device_set_desc(dev, MII_STR_MARVELL_E1000);
95 return 0;
96 }
97
98 static int
99 e1000phy_attach(device_t dev)
100 {
101 struct mii_softc *sc;
102 struct mii_attach_args *ma;
103 struct mii_data *mii;
104
105 getenv_int("e1000phy_debug", &e1000phy_debug);
106
107 sc = device_get_softc(dev);
108 ma = device_get_ivars(dev);
109 sc->mii_dev = device_get_parent(dev);
110 mii = device_get_softc(sc->mii_dev);
111 LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
112
113 sc->mii_inst = mii->mii_instance;
114 sc->mii_phy = ma->mii_phyno;
115 sc->mii_service = e1000phy_service;
116 sc->mii_pdata = mii;
117
118 sc->mii_flags |= MIIF_NOISOLATE;
119 mii->mii_instance++;
120 e1000phy_reset(sc);
121
122 device_printf(dev, " ");
123
124 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
125 /*
126 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
127 E1000_CR_ISOLATE);
128 */
129 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst),
130 E1000_CR_SPEED_10);
131 printf("10baseT, ");
132 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst),
133 E1000_CR_SPEED_10 | E1000_CR_FULL_DUPLEX);
134 printf("10baseT-FDX, ");
135 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst),
136 E1000_CR_SPEED_100);
137 printf("100baseTX, ");
138 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst),
139 E1000_CR_SPEED_100 | E1000_CR_FULL_DUPLEX);
140 printf("100baseTX-FDX, ");
141 /*
142 * 1000BT-simplex not supported; driver must ignore this entry,
143 * but it must be present in order to manually set full-duplex.
144 */
145 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst),
146 E1000_CR_SPEED_1000);
147 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst),
148 E1000_CR_SPEED_1000 | E1000_CR_FULL_DUPLEX);
149 printf("1000baseTX-FDX, ");
150 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0);
151 printf("auto\n");
152 #undef ADD
153
154 MIIBUS_MEDIAINIT(sc->mii_dev);
155 return(0);
156 }
157
158 static void
159 e1000phy_reset(struct mii_softc *sc)
160 {
161 u_int32_t reg;
162 int i;
163
164 /* initialize custom E1000 registers to magic values */
165 reg = PHY_READ(sc, E1000_SCR);
166 reg &= ~E1000_SCR_AUTO_X_MODE;
167 PHY_WRITE(sc, E1000_SCR, reg);
168
169 /* normal PHY reset */
170 /*mii_phy_reset(sc);*/
171 reg = PHY_READ(sc, E1000_CR);
172 reg |= E1000_CR_RESET;
173 PHY_WRITE(sc, E1000_CR, reg);
174
175 for (i = 0; i < 500; i++) {
176 DELAY(1);
177 reg = PHY_READ(sc, E1000_CR);
178 if (!(reg & E1000_CR_RESET))
179 break;
180 }
181
182 /* set more custom E1000 registers to magic values */
183 reg = PHY_READ(sc, E1000_SCR);
184 reg |= E1000_SCR_ASSERT_CRS_ON_TX;
185 PHY_WRITE(sc, E1000_SCR, reg);
186
187 reg = PHY_READ(sc, E1000_ESCR);
188 reg |= E1000_ESCR_TX_CLK_25;
189 PHY_WRITE(sc, E1000_ESCR, reg);
190
191 /* even more magic to reset DSP? */
192 PHY_WRITE(sc, 29, 0x1d);
193 PHY_WRITE(sc, 30, 0xc1);
194 PHY_WRITE(sc, 30, 0x00);
195 }
196
197 static int
198 e1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
199 {
200 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
201 int reg;
202
203 switch (cmd) {
204 case MII_POLLSTAT:
205 /*
206 * If we're not polling our PHY instance, just return.
207 */
208 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
209 return (0);
210 break;
211
212 case MII_MEDIACHG:
213 /*
214 * If the media indicates a different PHY instance,
215 * isolate ourselves.
216 */
217 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
218 reg = PHY_READ(sc, E1000_CR);
219 PHY_WRITE(sc, E1000_CR, reg | E1000_CR_ISOLATE);
220 return (0);
221 }
222
223 /*
224 * If the interface is not up, don't do anything.
225 */
226 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
227 break;
228 }
229
230 switch (IFM_SUBTYPE(ife->ifm_media)) {
231 case IFM_AUTO:
232 e1000phy_reset(sc);
233 (void)e1000phy_mii_phy_auto(sc);
234 break;
235
236 case IFM_1000_T:
237 e1000phy_reset(sc);
238
239 /* TODO - any other way to force 1000BT? */
240 (void)e1000phy_mii_phy_auto(sc);
241 break;
242
243 case IFM_100_TX:
244 e1000phy_reset(sc);
245
246 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
247 PHY_WRITE(sc, E1000_CR,
248 E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_100);
249 PHY_WRITE(sc, E1000_AR, E1000_AR_100TX_FD);
250 } else {
251 PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_100);
252 PHY_WRITE(sc, E1000_AR, E1000_AR_100TX);
253 }
254 break;
255
256 case IFM_10_T:
257 e1000phy_reset(sc);
258
259 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
260 PHY_WRITE(sc, E1000_CR,
261 E1000_CR_FULL_DUPLEX | E1000_CR_SPEED_10);
262 PHY_WRITE(sc, E1000_AR, E1000_AR_10T_FD);
263 } else {
264 PHY_WRITE(sc, E1000_CR, E1000_CR_SPEED_10);
265 PHY_WRITE(sc, E1000_AR, E1000_AR_10T);
266 }
267
268 break;
269
270 default:
271 return (EINVAL);
272 }
273
274 break;
275
276 case MII_TICK:
277 /*
278 * If we're not currently selected, just return.
279 */
280 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
281 return (0);
282 }
283
284 /*
285 * Is the interface even up?
286 */
287 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
288 return (0);
289
290 /*
291 * Only used for autonegotiation.
292 */
293 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
294 break;
295
296 /*
297 * check for link.
298 * Read the status register twice; BMSR_LINK is latch-low.
299 */
300 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
301 if (reg & BMSR_LINK)
302 break;
303
304 /*
305 * Only retry autonegotiation every 5 seconds.
306 */
307 if (++sc->mii_ticks != 5)
308 return (0);
309
310 sc->mii_ticks = 0;
311 e1000phy_reset(sc);
312 e1000phy_mii_phy_auto(sc);
313 return (0);
314 }
315
316 /* Update the media status. */
317 e1000phy_status(sc);
318
319 /* Callback if something changed. */
320 mii_phy_update(sc, cmd);
321 return (0);
322 }
323
324 static void
325 e1000phy_status(struct mii_softc *sc)
326 {
327 struct mii_data *mii = sc->mii_pdata;
328 int bmsr, bmcr, esr, ssr, isr, ar, lpar;
329
330 mii->mii_media_status = IFM_AVALID;
331 mii->mii_media_active = IFM_ETHER;
332
333 bmsr = PHY_READ(sc, E1000_SR) | PHY_READ(sc, E1000_SR);
334 esr = PHY_READ(sc, E1000_ESR);
335 bmcr = PHY_READ(sc, E1000_CR);
336 ssr = PHY_READ(sc, E1000_SSR);
337 isr = PHY_READ(sc, E1000_ISR);
338 ar = PHY_READ(sc, E1000_AR);
339 lpar = PHY_READ(sc, E1000_LPAR);
340
341 if (bmsr & E1000_SR_LINK_STATUS)
342 mii->mii_media_status |= IFM_ACTIVE;
343
344 if (bmcr & E1000_CR_LOOPBACK)
345 mii->mii_media_active |= IFM_LOOP;
346
347 if ((!(bmsr & E1000_SR_AUTO_NEG_COMPLETE) || !(ssr & E1000_SSR_LINK) ||
348 !(ssr & E1000_SSR_SPD_DPLX_RESOLVED))) {
349 /* Erg, still trying, I guess... */
350 mii->mii_media_active |= IFM_NONE;
351 return;
352 }
353
354 if (ssr & E1000_SSR_1000MBS)
355 mii->mii_media_active |= IFM_1000_T;
356 else if (ssr & E1000_SSR_100MBS)
357 mii->mii_media_active |= IFM_100_TX;
358 else
359 mii->mii_media_active |= IFM_10_T;
360
361 if (ssr & E1000_SSR_DUPLEX)
362 mii->mii_media_active |= IFM_FDX;
363 else
364 mii->mii_media_active |= IFM_HDX;
365
366 /* FLAG0==rx-flow-control FLAG1==tx-flow-control */
367 if ((ar & E1000_AR_PAUSE) && (lpar & E1000_LPAR_PAUSE)) {
368 mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1;
369 } else if (!(ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
370 (lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
371 mii->mii_media_active |= IFM_FLAG1;
372 } else if ((ar & E1000_AR_PAUSE) && (ar & E1000_AR_ASM_DIR) &&
373 !(lpar & E1000_LPAR_PAUSE) && (lpar & E1000_LPAR_ASM_DIR)) {
374 mii->mii_media_active |= IFM_FLAG0;
375 }
376 }
377
378 static int
379 e1000phy_mii_phy_auto(struct mii_softc *mii)
380 {
381
382 PHY_WRITE(mii, E1000_AR, E1000_AR_10T | E1000_AR_10T_FD |
383 E1000_AR_100TX | E1000_AR_100TX_FD |
384 E1000_AR_PAUSE | E1000_AR_ASM_DIR);
385 PHY_WRITE(mii, E1000_1GCR, E1000_1GCR_1000T_FD);
386 PHY_WRITE(mii, E1000_CR,
387 E1000_CR_AUTO_NEG_ENABLE | E1000_CR_RESTART_AUTO_NEG);
388
389 return (EJUSTRETURN);
390 }
Cache object: a6841dcf583c0be4ff87a6dc49f13c46
|