1 | /* $NetBSD: rgephy.c,v 1.40 2015/08/21 12:22:22 jmcneill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2003 |
5 | * Bill Paul <wpaul@windriver.com>. 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, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. All advertising materials mentioning features or use of this software |
16 | * must display the following acknowledgement: |
17 | * This product includes software developed by Bill Paul. |
18 | * 4. Neither the name of the author nor the names of any co-contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD |
26 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
32 | * THE POSSIBILITY OF SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: rgephy.c,v 1.40 2015/08/21 12:22:22 jmcneill Exp $" ); |
37 | |
38 | |
39 | /* |
40 | * Driver for the RealTek 8169S/8110S internal 10/100/1000 PHY. |
41 | */ |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/kernel.h> |
46 | #include <sys/device.h> |
47 | #include <sys/socket.h> |
48 | |
49 | |
50 | #include <net/if.h> |
51 | #include <net/if_media.h> |
52 | |
53 | #include <dev/mii/mii.h> |
54 | #include <dev/mii/miivar.h> |
55 | #include <dev/mii/miidevs.h> |
56 | |
57 | #include <dev/mii/rgephyreg.h> |
58 | |
59 | #include <dev/ic/rtl81x9reg.h> |
60 | |
61 | static int rgephy_match(device_t, cfdata_t, void *); |
62 | static void rgephy_attach(device_t, device_t, void *); |
63 | |
64 | struct rgephy_softc { |
65 | struct mii_softc mii_sc; |
66 | }; |
67 | |
68 | CFATTACH_DECL_NEW(rgephy, sizeof(struct rgephy_softc), |
69 | rgephy_match, rgephy_attach, mii_phy_detach, mii_phy_activate); |
70 | |
71 | |
72 | static int rgephy_service(struct mii_softc *, struct mii_data *, int); |
73 | static void rgephy_status(struct mii_softc *); |
74 | static int rgephy_mii_phy_auto(struct mii_softc *); |
75 | static void rgephy_reset(struct mii_softc *); |
76 | static void rgephy_loop(struct mii_softc *); |
77 | static void rgephy_load_dspcode(struct mii_softc *); |
78 | |
79 | static const struct mii_phy_funcs rgephy_funcs = { |
80 | rgephy_service, rgephy_status, rgephy_reset, |
81 | }; |
82 | |
83 | static const struct mii_phydesc rgephys[] = { |
84 | { MII_OUI_xxREALTEK, MII_MODEL_xxREALTEK_RTL8169S, |
85 | MII_STR_xxREALTEK_RTL8169S }, |
86 | |
87 | { MII_OUI_REALTEK, MII_MODEL_REALTEK_RTL8169S, |
88 | MII_STR_REALTEK_RTL8169S }, |
89 | |
90 | { MII_OUI_REALTEK, MII_MODEL_REALTEK_RTL8251, |
91 | MII_STR_REALTEK_RTL8251 }, |
92 | |
93 | { 0, 0, |
94 | NULL } |
95 | }; |
96 | |
97 | static int |
98 | rgephy_match(device_t parent, cfdata_t match, void *aux) |
99 | { |
100 | struct mii_attach_args *ma = aux; |
101 | |
102 | if (mii_phy_match(ma, rgephys) != NULL) |
103 | return 10; |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static void |
109 | rgephy_attach(device_t parent, device_t self, void *aux) |
110 | { |
111 | struct rgephy_softc *rsc = device_private(self); |
112 | struct mii_softc *sc = &rsc->mii_sc; |
113 | struct mii_attach_args *ma = aux; |
114 | struct mii_data *mii = ma->mii_data; |
115 | const struct mii_phydesc *mpd; |
116 | int rev; |
117 | const char *sep = "" ; |
118 | |
119 | ma = aux; |
120 | mii = ma->mii_data; |
121 | |
122 | rev = MII_REV(ma->mii_id2); |
123 | mpd = mii_phy_match(ma, rgephys); |
124 | aprint_naive(": Media interface\n" ); |
125 | aprint_normal(": %s, rev. %d\n" , mpd->mpd_name, rev); |
126 | |
127 | sc->mii_dev = self; |
128 | sc->mii_inst = mii->mii_instance; |
129 | sc->mii_phy = ma->mii_phyno; |
130 | sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); |
131 | sc->mii_mpd_model = MII_MODEL(ma->mii_id2); |
132 | sc->mii_mpd_rev = MII_REV(ma->mii_id2); |
133 | sc->mii_pdata = mii; |
134 | sc->mii_flags = mii->mii_flags; |
135 | sc->mii_anegticks = MII_ANEGTICKS_GIGE; |
136 | |
137 | sc->mii_funcs = &rgephy_funcs; |
138 | |
139 | #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) |
140 | #define PRINT(n) aprint_normal("%s%s", sep, (n)); sep = ", " |
141 | |
142 | #ifdef __FreeBSD__ |
143 | ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), |
144 | BMCR_LOOP|BMCR_S100); |
145 | #endif |
146 | |
147 | sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; |
148 | sc->mii_capabilities &= ~BMSR_ANEG; |
149 | |
150 | /* |
151 | * FreeBSD does not check EXSTAT, but instead adds gigabit |
152 | * media explicitly. Why? |
153 | */ |
154 | aprint_normal_dev(self, "" ); |
155 | if (sc->mii_capabilities & BMSR_EXTSTAT) { |
156 | sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); |
157 | } |
158 | mii_phy_add_media(sc); |
159 | |
160 | /* rtl8169S does not report auto-sense; add manually. */ |
161 | ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), MII_NMEDIA); |
162 | sep =", " ; |
163 | PRINT("auto" ); |
164 | |
165 | #undef ADD |
166 | #undef PRINT |
167 | |
168 | rgephy_reset(sc); |
169 | aprint_normal("\n" ); |
170 | } |
171 | |
172 | static int |
173 | rgephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) |
174 | { |
175 | struct ifmedia_entry *ife = mii->mii_media.ifm_cur; |
176 | int reg, speed, gig, anar; |
177 | |
178 | switch (cmd) { |
179 | case MII_POLLSTAT: |
180 | /* |
181 | * If we're not polling our PHY instance, just return. |
182 | */ |
183 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
184 | return 0; |
185 | break; |
186 | |
187 | case MII_MEDIACHG: |
188 | /* |
189 | * If the media indicates a different PHY instance, |
190 | * isolate ourselves. |
191 | */ |
192 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) { |
193 | reg = PHY_READ(sc, MII_BMCR); |
194 | PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); |
195 | return 0; |
196 | } |
197 | |
198 | /* |
199 | * If the interface is not up, don't do anything. |
200 | */ |
201 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) |
202 | break; |
203 | |
204 | rgephy_reset(sc); /* XXX hardware bug work-around */ |
205 | |
206 | anar = PHY_READ(sc, MII_ANAR); |
207 | anar &= ~(ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10); |
208 | |
209 | switch (IFM_SUBTYPE(ife->ifm_media)) { |
210 | case IFM_AUTO: |
211 | #ifdef foo |
212 | /* |
213 | * If we're already in auto mode, just return. |
214 | */ |
215 | if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) |
216 | return 0; |
217 | #endif |
218 | (void)rgephy_mii_phy_auto(sc); |
219 | break; |
220 | case IFM_1000_T: |
221 | speed = BMCR_S1000; |
222 | goto setit; |
223 | case IFM_100_TX: |
224 | speed = BMCR_S100; |
225 | anar |= ANAR_TX_FD | ANAR_TX; |
226 | goto setit; |
227 | case IFM_10_T: |
228 | speed = BMCR_S10; |
229 | anar |= ANAR_10_FD | ANAR_10; |
230 | setit: |
231 | rgephy_loop(sc); |
232 | if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { |
233 | speed |= BMCR_FDX; |
234 | gig = GTCR_ADV_1000TFDX; |
235 | anar &= ~(ANAR_TX | ANAR_10); |
236 | } else { |
237 | gig = GTCR_ADV_1000THDX; |
238 | anar &= ~(ANAR_TX_FD | ANAR_10_FD); |
239 | } |
240 | |
241 | if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) { |
242 | PHY_WRITE(sc, MII_100T2CR, 0); |
243 | PHY_WRITE(sc, MII_ANAR, anar); |
244 | PHY_WRITE(sc, MII_BMCR, speed | |
245 | BMCR_AUTOEN | BMCR_STARTNEG); |
246 | break; |
247 | } |
248 | |
249 | /* |
250 | * When setting the link manually, one side must |
251 | * be the master and the other the slave. However |
252 | * ifmedia doesn't give us a good way to specify |
253 | * this, so we fake it by using one of the LINK |
254 | * flags. If LINK0 is set, we program the PHY to |
255 | * be a master, otherwise it's a slave. |
256 | */ |
257 | if ((mii->mii_ifp->if_flags & IFF_LINK0)) { |
258 | PHY_WRITE(sc, MII_100T2CR, |
259 | gig|GTCR_MAN_MS|GTCR_ADV_MS); |
260 | } else { |
261 | PHY_WRITE(sc, MII_100T2CR, gig|GTCR_MAN_MS); |
262 | } |
263 | PHY_WRITE(sc, MII_BMCR, speed | |
264 | BMCR_AUTOEN | BMCR_STARTNEG); |
265 | break; |
266 | case IFM_NONE: |
267 | PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); |
268 | break; |
269 | case IFM_100_T4: |
270 | default: |
271 | return EINVAL; |
272 | } |
273 | break; |
274 | |
275 | case MII_TICK: |
276 | /* |
277 | * If we're not currently selected, just return. |
278 | */ |
279 | if (IFM_INST(ife->ifm_media) != sc->mii_inst) |
280 | return 0; |
281 | |
282 | /* |
283 | * Is the interface even up? |
284 | */ |
285 | if ((mii->mii_ifp->if_flags & IFF_UP) == 0) |
286 | return 0; |
287 | |
288 | /* |
289 | * Only used for autonegotiation. |
290 | */ |
291 | if ((IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) && |
292 | (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)) { |
293 | /* |
294 | * Reset autonegotiation timer to 0 to make sure |
295 | * the future autonegotiation start with 0. |
296 | */ |
297 | sc->mii_ticks = 0; |
298 | break; |
299 | } |
300 | |
301 | /* |
302 | * Check to see if we have link. If we do, we don't |
303 | * need to restart the autonegotiation process. Read |
304 | * the BMSR twice in case it's latched. |
305 | */ |
306 | if (sc->mii_mpd_rev >= 6) { |
307 | /* RTL8211F */ |
308 | reg = PHY_READ(sc, RGEPHY_MII_PHYSR); |
309 | if (reg & RGEPHY_PHYSR_LINK) { |
310 | sc->mii_ticks = 0; |
311 | break; |
312 | } |
313 | } else if (sc->mii_mpd_rev >= 2) { |
314 | /* RTL8211B(L) */ |
315 | reg = PHY_READ(sc, RGEPHY_MII_SSR); |
316 | if (reg & RGEPHY_SSR_LINK) { |
317 | sc->mii_ticks = 0; |
318 | break; |
319 | } |
320 | } else { |
321 | reg = PHY_READ(sc, RTK_GMEDIASTAT); |
322 | if ((reg & RTK_GMEDIASTAT_LINK) != 0) { |
323 | sc->mii_ticks = 0; |
324 | break; |
325 | } |
326 | } |
327 | |
328 | /* Announce link loss right after it happens. */ |
329 | if (sc->mii_ticks++ == 0) |
330 | break; |
331 | |
332 | /* Only retry autonegotiation every mii_anegticks seconds. */ |
333 | if (sc->mii_ticks <= sc->mii_anegticks) |
334 | return 0; |
335 | |
336 | rgephy_mii_phy_auto(sc); |
337 | break; |
338 | } |
339 | |
340 | /* Update the media status. */ |
341 | rgephy_status(sc); |
342 | |
343 | /* |
344 | * Callback if something changed. Note that we need to poke |
345 | * the DSP on the RealTek PHYs if the media changes. |
346 | * |
347 | */ |
348 | if (sc->mii_media_active != mii->mii_media_active || |
349 | sc->mii_media_status != mii->mii_media_status || |
350 | cmd == MII_MEDIACHG) { |
351 | rgephy_load_dspcode(sc); |
352 | } |
353 | mii_phy_update(sc, cmd); |
354 | return 0; |
355 | } |
356 | |
357 | static void |
358 | rgephy_status(struct mii_softc *sc) |
359 | { |
360 | struct mii_data *mii = sc->mii_pdata; |
361 | int gstat, bmsr, bmcr, physr; |
362 | uint16_t ssr; |
363 | |
364 | mii->mii_media_status = IFM_AVALID; |
365 | mii->mii_media_active = IFM_ETHER; |
366 | |
367 | if (sc->mii_mpd_rev >= 6) { |
368 | physr = PHY_READ(sc, RGEPHY_MII_PHYSR); |
369 | if (physr & RGEPHY_PHYSR_LINK) |
370 | mii->mii_media_status |= IFM_ACTIVE; |
371 | } else if (sc->mii_mpd_rev >= 2) { |
372 | ssr = PHY_READ(sc, RGEPHY_MII_SSR); |
373 | if (ssr & RGEPHY_SSR_LINK) |
374 | mii->mii_media_status |= IFM_ACTIVE; |
375 | } else { |
376 | gstat = PHY_READ(sc, RTK_GMEDIASTAT); |
377 | if ((gstat & RTK_GMEDIASTAT_LINK) != 0) |
378 | mii->mii_media_status |= IFM_ACTIVE; |
379 | } |
380 | |
381 | bmsr = PHY_READ(sc, MII_BMSR); |
382 | bmcr = PHY_READ(sc, MII_BMCR); |
383 | |
384 | if ((bmcr & BMCR_ISO) != 0) { |
385 | mii->mii_media_active |= IFM_NONE; |
386 | mii->mii_media_status = 0; |
387 | return; |
388 | } |
389 | |
390 | if ((bmcr & BMCR_LOOP) != 0) |
391 | mii->mii_media_active |= IFM_LOOP; |
392 | |
393 | if ((bmcr & BMCR_AUTOEN) != 0) { |
394 | if ((bmsr & BMSR_ACOMP) == 0) { |
395 | /* Erg, still trying, I guess... */ |
396 | mii->mii_media_active |= IFM_NONE; |
397 | return; |
398 | } |
399 | } |
400 | |
401 | if (sc->mii_mpd_rev >= 6) { |
402 | physr = PHY_READ(sc, RGEPHY_MII_PHYSR); |
403 | switch (__SHIFTOUT(physr, RGEPHY_PHYSR_SPEED)) { |
404 | case RGEPHY_PHYSR_SPEED_1000: |
405 | mii->mii_media_active |= IFM_1000_T; |
406 | break; |
407 | case RGEPHY_PHYSR_SPEED_100: |
408 | mii->mii_media_active |= IFM_100_TX; |
409 | break; |
410 | case RGEPHY_PHYSR_SPEED_10: |
411 | mii->mii_media_active |= IFM_10_T; |
412 | break; |
413 | default: |
414 | mii->mii_media_active |= IFM_NONE; |
415 | break; |
416 | } |
417 | if (physr & RGEPHY_PHYSR_DUPLEX) |
418 | mii->mii_media_active |= mii_phy_flowstatus(sc) | |
419 | IFM_FDX; |
420 | else |
421 | mii->mii_media_active |= IFM_HDX; |
422 | } else if (sc->mii_mpd_rev >= 2) { |
423 | ssr = PHY_READ(sc, RGEPHY_MII_SSR); |
424 | switch (ssr & RGEPHY_SSR_SPD_MASK) { |
425 | case RGEPHY_SSR_S1000: |
426 | mii->mii_media_active |= IFM_1000_T; |
427 | break; |
428 | case RGEPHY_SSR_S100: |
429 | mii->mii_media_active |= IFM_100_TX; |
430 | break; |
431 | case RGEPHY_SSR_S10: |
432 | mii->mii_media_active |= IFM_10_T; |
433 | break; |
434 | default: |
435 | mii->mii_media_active |= IFM_NONE; |
436 | break; |
437 | } |
438 | if (ssr & RGEPHY_SSR_FDX) |
439 | mii->mii_media_active |= mii_phy_flowstatus(sc) | |
440 | IFM_FDX; |
441 | else |
442 | mii->mii_media_active |= IFM_HDX; |
443 | } else { |
444 | gstat = PHY_READ(sc, RTK_GMEDIASTAT); |
445 | if ((gstat & RTK_GMEDIASTAT_1000MBPS) != 0) |
446 | mii->mii_media_active |= IFM_1000_T; |
447 | else if ((gstat & RTK_GMEDIASTAT_100MBPS) != 0) |
448 | mii->mii_media_active |= IFM_100_TX; |
449 | else if ((gstat & RTK_GMEDIASTAT_10MBPS) != 0) |
450 | mii->mii_media_active |= IFM_10_T; |
451 | else |
452 | mii->mii_media_active |= IFM_NONE; |
453 | if ((gstat & RTK_GMEDIASTAT_FDX) != 0) |
454 | mii->mii_media_active |= mii_phy_flowstatus(sc) | |
455 | IFM_FDX; |
456 | else |
457 | mii->mii_media_active |= IFM_HDX; |
458 | } |
459 | } |
460 | |
461 | |
462 | static int |
463 | rgephy_mii_phy_auto(struct mii_softc *mii) |
464 | { |
465 | int anar; |
466 | |
467 | mii->mii_ticks = 0; |
468 | rgephy_loop(mii); |
469 | rgephy_reset(mii); |
470 | |
471 | anar = BMSR_MEDIA_TO_ANAR(mii->mii_capabilities) | ANAR_CSMA; |
472 | if (mii->mii_flags & MIIF_DOPAUSE) |
473 | anar |= ANAR_FC | ANAR_PAUSE_ASYM; |
474 | |
475 | PHY_WRITE(mii, MII_ANAR, anar); |
476 | DELAY(1000); |
477 | PHY_WRITE(mii, MII_100T2CR, GTCR_ADV_1000THDX | GTCR_ADV_1000TFDX); |
478 | DELAY(1000); |
479 | PHY_WRITE(mii, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); |
480 | DELAY(100); |
481 | |
482 | return EJUSTRETURN; |
483 | } |
484 | |
485 | static void |
486 | rgephy_loop(struct mii_softc *sc) |
487 | { |
488 | uint32_t bmsr; |
489 | int i; |
490 | |
491 | if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 && |
492 | sc->mii_mpd_rev < 2) { |
493 | PHY_WRITE(sc, MII_BMCR, BMCR_PDOWN); |
494 | DELAY(1000); |
495 | } |
496 | |
497 | for (i = 0; i < 15000; i++) { |
498 | bmsr = PHY_READ(sc, MII_BMSR); |
499 | if ((bmsr & BMSR_LINK) == 0) { |
500 | #if 0 |
501 | device_printf(sc->mii_dev, "looped %d\n" , i); |
502 | #endif |
503 | break; |
504 | } |
505 | DELAY(10); |
506 | } |
507 | } |
508 | |
509 | #define PHY_SETBIT(x, y, z) \ |
510 | PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) |
511 | #define PHY_CLRBIT(x, y, z) \ |
512 | PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) |
513 | |
514 | /* |
515 | * Initialize RealTek PHY per the datasheet. The DSP in the PHYs of |
516 | * existing revisions of the 8169S/8110S chips need to be tuned in |
517 | * order to reliably negotiate a 1000Mbps link. This is only needed |
518 | * for rev 0 and rev 1 of the PHY. Later versions work without |
519 | * any fixups. |
520 | */ |
521 | static void |
522 | rgephy_load_dspcode(struct mii_softc *sc) |
523 | { |
524 | int val; |
525 | |
526 | if (sc->mii_mpd_model == MII_MODEL_REALTEK_RTL8251 || |
527 | sc->mii_mpd_rev >= 2) |
528 | return; |
529 | |
530 | #if 1 |
531 | PHY_WRITE(sc, 31, 0x0001); |
532 | PHY_WRITE(sc, 21, 0x1000); |
533 | PHY_WRITE(sc, 24, 0x65C7); |
534 | PHY_CLRBIT(sc, 4, 0x0800); |
535 | val = PHY_READ(sc, 4) & 0xFFF; |
536 | PHY_WRITE(sc, 4, val); |
537 | PHY_WRITE(sc, 3, 0x00A1); |
538 | PHY_WRITE(sc, 2, 0x0008); |
539 | PHY_WRITE(sc, 1, 0x1020); |
540 | PHY_WRITE(sc, 0, 0x1000); |
541 | PHY_SETBIT(sc, 4, 0x0800); |
542 | PHY_CLRBIT(sc, 4, 0x0800); |
543 | val = (PHY_READ(sc, 4) & 0xFFF) | 0x7000; |
544 | PHY_WRITE(sc, 4, val); |
545 | PHY_WRITE(sc, 3, 0xFF41); |
546 | PHY_WRITE(sc, 2, 0xDE60); |
547 | PHY_WRITE(sc, 1, 0x0140); |
548 | PHY_WRITE(sc, 0, 0x0077); |
549 | val = (PHY_READ(sc, 4) & 0xFFF) | 0xA000; |
550 | PHY_WRITE(sc, 4, val); |
551 | PHY_WRITE(sc, 3, 0xDF01); |
552 | PHY_WRITE(sc, 2, 0xDF20); |
553 | PHY_WRITE(sc, 1, 0xFF95); |
554 | PHY_WRITE(sc, 0, 0xFA00); |
555 | val = (PHY_READ(sc, 4) & 0xFFF) | 0xB000; |
556 | PHY_WRITE(sc, 4, val); |
557 | PHY_WRITE(sc, 3, 0xFF41); |
558 | PHY_WRITE(sc, 2, 0xDE20); |
559 | PHY_WRITE(sc, 1, 0x0140); |
560 | PHY_WRITE(sc, 0, 0x00BB); |
561 | val = (PHY_READ(sc, 4) & 0xFFF) | 0xF000; |
562 | PHY_WRITE(sc, 4, val); |
563 | PHY_WRITE(sc, 3, 0xDF01); |
564 | PHY_WRITE(sc, 2, 0xDF20); |
565 | PHY_WRITE(sc, 1, 0xFF95); |
566 | PHY_WRITE(sc, 0, 0xBF00); |
567 | PHY_SETBIT(sc, 4, 0x0800); |
568 | PHY_CLRBIT(sc, 4, 0x0800); |
569 | PHY_WRITE(sc, 31, 0x0000); |
570 | #else |
571 | (void)val; |
572 | PHY_WRITE(sc, 0x1f, 0x0001); |
573 | PHY_WRITE(sc, 0x15, 0x1000); |
574 | PHY_WRITE(sc, 0x18, 0x65c7); |
575 | PHY_WRITE(sc, 0x04, 0x0000); |
576 | PHY_WRITE(sc, 0x03, 0x00a1); |
577 | PHY_WRITE(sc, 0x02, 0x0008); |
578 | PHY_WRITE(sc, 0x01, 0x1020); |
579 | PHY_WRITE(sc, 0x00, 0x1000); |
580 | PHY_WRITE(sc, 0x04, 0x0800); |
581 | PHY_WRITE(sc, 0x04, 0x0000); |
582 | PHY_WRITE(sc, 0x04, 0x7000); |
583 | PHY_WRITE(sc, 0x03, 0xff41); |
584 | PHY_WRITE(sc, 0x02, 0xde60); |
585 | PHY_WRITE(sc, 0x01, 0x0140); |
586 | PHY_WRITE(sc, 0x00, 0x0077); |
587 | PHY_WRITE(sc, 0x04, 0x7800); |
588 | PHY_WRITE(sc, 0x04, 0x7000); |
589 | PHY_WRITE(sc, 0x04, 0xa000); |
590 | PHY_WRITE(sc, 0x03, 0xdf01); |
591 | PHY_WRITE(sc, 0x02, 0xdf20); |
592 | PHY_WRITE(sc, 0x01, 0xff95); |
593 | PHY_WRITE(sc, 0x00, 0xfa00); |
594 | PHY_WRITE(sc, 0x04, 0xa800); |
595 | PHY_WRITE(sc, 0x04, 0xa000); |
596 | PHY_WRITE(sc, 0x04, 0xb000); |
597 | PHY_WRITE(sc, 0x0e, 0xff41); |
598 | PHY_WRITE(sc, 0x02, 0xde20); |
599 | PHY_WRITE(sc, 0x01, 0x0140); |
600 | PHY_WRITE(sc, 0x00, 0x00bb); |
601 | PHY_WRITE(sc, 0x04, 0xb800); |
602 | PHY_WRITE(sc, 0x04, 0xb000); |
603 | PHY_WRITE(sc, 0x04, 0xf000); |
604 | PHY_WRITE(sc, 0x03, 0xdf01); |
605 | PHY_WRITE(sc, 0x02, 0xdf20); |
606 | PHY_WRITE(sc, 0x01, 0xff95); |
607 | PHY_WRITE(sc, 0x00, 0xbf00); |
608 | PHY_WRITE(sc, 0x04, 0xf800); |
609 | PHY_WRITE(sc, 0x04, 0xf000); |
610 | PHY_WRITE(sc, 0x04, 0x0000); |
611 | PHY_WRITE(sc, 0x1f, 0x0000); |
612 | PHY_WRITE(sc, 0x0b, 0x0000); |
613 | |
614 | #endif |
615 | |
616 | DELAY(40); |
617 | } |
618 | |
619 | static void |
620 | rgephy_reset(struct mii_softc *sc) |
621 | { |
622 | uint16_t ssr, phycr1; |
623 | |
624 | mii_phy_reset(sc); |
625 | DELAY(1000); |
626 | |
627 | if (sc->mii_mpd_model != MII_MODEL_REALTEK_RTL8251 && |
628 | sc->mii_mpd_rev < 2) { |
629 | rgephy_load_dspcode(sc); |
630 | } else if (sc->mii_mpd_rev == 3) { |
631 | /* RTL8211C(L) */ |
632 | ssr = PHY_READ(sc, RGEPHY_MII_SSR); |
633 | if ((ssr & RGEPHY_SSR_ALDPS) != 0) { |
634 | ssr &= ~RGEPHY_SSR_ALDPS; |
635 | PHY_WRITE(sc, RGEPHY_MII_SSR, ssr); |
636 | } |
637 | } else if (sc->mii_mpd_rev == 6) { |
638 | /* RTL8211F */ |
639 | phycr1 = PHY_READ(sc, RGEPHY_MII_PHYCR1); |
640 | phycr1 &= ~RGEPHY_PHYCR1_MDI_MMCE; |
641 | phycr1 &= ~RGEPHY_PHYCR1_ALDPS_EN; |
642 | PHY_WRITE(sc, RGEPHY_MII_PHYCR1, phycr1); |
643 | } else { |
644 | PHY_WRITE(sc, 0x1F, 0x0000); |
645 | PHY_WRITE(sc, 0x0e, 0x0000); |
646 | } |
647 | |
648 | /* Reset capabilities */ |
649 | /* Step1: write our capability */ |
650 | /* 10/100 capability */ |
651 | PHY_WRITE(sc, MII_ANAR, |
652 | ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); |
653 | /* 1000 capability */ |
654 | PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX); |
655 | |
656 | /* Step2: Restart NWay */ |
657 | /* NWay enable and Restart NWay */ |
658 | PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); |
659 | |
660 | if (sc->mii_mpd_rev == 6) { |
661 | /* RTL8211F */ |
662 | delay(10000); |
663 | /* disable EEE */ |
664 | PHY_WRITE(sc, RGEPHY_MII_MACR, 0x0007); |
665 | PHY_WRITE(sc, RGEPHY_MII_MAADR, 0x003c); |
666 | PHY_WRITE(sc, RGEPHY_MII_MACR, 0x4007); |
667 | PHY_WRITE(sc, RGEPHY_MII_MAADR, 0x0000); |
668 | } |
669 | } |
670 | |