406704a933e6dc688732255d081aab11fe518d5e
[dragonfly.git] / sys / dev / netif / mii_layer / brgphy.c
1 /*      $OpenBSD: brgphy.c,v 1.48 2006/05/20 23:03:53 brad Exp $        */
2
3 /*
4  * Copyright (c) 2000
5  *      Bill Paul <wpaul@ee.columbia.edu>.  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  * $FreeBSD: src/sys/dev/mii/brgphy.c,v 1.1.2.7 2003/05/11 18:00:55 ps Exp $
35  * $DragonFly: src/sys/dev/netif/mii_layer/brgphy.c,v 1.22 2008/10/22 14:24:24 sephe Exp $
36  */
37
38 /*
39  * Driver for the Broadcom BCR5400 1000baseT PHY. Speed is always
40  * 1000mbps; all we need to negotiate here is full or half duplex.
41  */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/socket.h>
47 #include <sys/bus.h>
48 #include <sys/sysctl.h>
49
50 #include <net/ethernet.h>
51 #include <net/if.h>
52 #include <net/if_media.h>
53 #include <net/if_arp.h>
54
55 #include "mii.h"
56 #include "miivar.h"
57 #include "miidevs.h"
58
59 #include "brgphyreg.h"
60 #include <dev/netif/bge/if_bgereg.h>
61
62 #include "miibus_if.h"
63
64 static int brgphy_probe(device_t);
65 static int brgphy_attach(device_t);
66
67 static const struct mii_phydesc brgphys[] = {
68         MII_PHYDESC(xxBROADCOM, BCM5400),
69         MII_PHYDESC(xxBROADCOM, BCM5401),
70         MII_PHYDESC(xxBROADCOM, BCM5411),
71         MII_PHYDESC(xxBROADCOM, BCM5421),
72         MII_PHYDESC(xxBROADCOM, BCM54K2),
73         MII_PHYDESC(xxBROADCOM, BCM5462),
74
75         MII_PHYDESC(xxBROADCOM, BCM5701),
76         MII_PHYDESC(xxBROADCOM, BCM5703),
77         MII_PHYDESC(xxBROADCOM, BCM5704),
78         MII_PHYDESC(xxBROADCOM, BCM5705),
79
80         MII_PHYDESC(xxBROADCOM, BCM5714),
81         MII_PHYDESC(xxBROADCOM, BCM5750),
82         MII_PHYDESC(xxBROADCOM, BCM5752),
83         MII_PHYDESC(xxBROADCOM2,BCM5755),
84         MII_PHYDESC(xxBROADCOM, BCM5780),
85         MII_PHYDESC(xxBROADCOM2,BCM5787),
86
87         MII_PHYDESC(xxBROADCOM, BCM5706C),
88         MII_PHYDESC(xxBROADCOM, BCM5708C),
89
90         MII_PHYDESC(BROADCOM2, BCM5906),
91
92         MII_PHYDESC_NULL
93 };
94
95 static device_method_t brgphy_methods[] = {
96         /* device interface */
97         DEVMETHOD(device_probe,         brgphy_probe),
98         DEVMETHOD(device_attach,        brgphy_attach),
99         DEVMETHOD(device_detach,        ukphy_detach),
100         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
101         { 0, 0 }
102 };
103
104 static devclass_t brgphy_devclass;
105
106 static driver_t brgphy_driver = {
107         "brgphy",
108         brgphy_methods,
109         sizeof(struct mii_softc)
110 };
111
112 DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
113
114 static int      brgphy_service(struct mii_softc *, struct mii_data *, int);
115 static void     brgphy_status(struct mii_softc *);
116 static void     brgphy_mii_phy_auto(struct mii_softc *);
117 static void     brgphy_reset(struct mii_softc *);
118 static void     brgphy_loop(struct mii_softc *);
119
120 static void     brgphy_bcm5401_dspcode(struct mii_softc *);
121 static void     brgphy_bcm5411_dspcode(struct mii_softc *);
122 static void     brgphy_bcm5421_dspcode(struct mii_softc *);
123 static void     brgphy_bcm54k2_dspcode(struct mii_softc *);
124
125 static void     brgphy_adc_bug(struct mii_softc *);
126 static void     brgphy_5704_a0_bug(struct mii_softc *);
127 static void     brgphy_ber_bug(struct mii_softc *);
128 static void     brgphy_crc_bug(struct mii_softc *);
129
130 static void     brgphy_jumbo_settings(struct mii_softc *, u_long);
131 static void     brgphy_eth_wirespeed(struct mii_softc *);
132
133 static int
134 brgphy_probe(device_t dev)
135 {
136         struct mii_attach_args *ma = device_get_ivars(dev);
137         const struct mii_phydesc *mpd;
138
139         mpd = mii_phy_match(ma, brgphys);
140         if (mpd != NULL) {
141                 device_set_desc(dev, mpd->mpd_name);
142                 return (0);
143         }
144         return(ENXIO);
145 }
146
147 static int
148 brgphy_attach(device_t dev)
149 {
150         struct mii_softc *sc;
151         struct mii_attach_args *ma;
152         struct mii_data *mii;
153
154         sc = device_get_softc(dev);
155         ma = device_get_ivars(dev);
156         mii_softc_init(sc, ma);
157         sc->mii_dev = device_get_parent(dev);
158         mii = device_get_softc(sc->mii_dev);
159         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
160
161         sc->mii_inst = mii->mii_instance;
162         sc->mii_service = brgphy_service;
163         sc->mii_reset = brgphy_reset;
164         sc->mii_pdata = mii;
165
166         sc->mii_flags |= MIIF_NOISOLATE;
167         mii->mii_instance++;
168
169         brgphy_reset(sc);
170
171 #define ADD(m, c)       ifmedia_add(&mii->mii_media, (m), (c), NULL)
172
173         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
174             MII_MEDIA_NONE);
175 #if 0
176         ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
177             MII_MEDIA_100_TX);
178 #endif
179
180 #undef ADD
181
182         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
183         if (sc->mii_capabilities & BMSR_EXTSTAT)
184                 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
185
186         device_printf(dev, " ");
187         if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
188             (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
189                 mii_phy_add_media(sc);
190         else
191                 kprintf("no media present");
192         kprintf("\n");
193
194         MIIBUS_MEDIAINIT(sc->mii_dev);
195         return(0);
196 }
197
198 static int
199 brgphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
200 {
201         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
202         int reg, speed, gig;
203
204         switch (cmd) {
205         case MII_POLLSTAT:
206                 /*
207                  * If we're not polling our PHY instance, just return.
208                  */
209                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
210                         return (0);
211                 break;
212
213         case MII_MEDIACHG:
214                 /*
215                  * If the media indicates a different PHY instance,
216                  * isolate ourselves.
217                  */
218                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
219                         reg = PHY_READ(sc, MII_BMCR);
220                         PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
221                         return (0);
222                 }
223
224                 /*
225                  * If the interface is not up, don't do anything.
226                  */
227                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
228                         break;
229
230                 brgphy_reset(sc);       /* XXX hardware bug work-around */
231
232                 switch (IFM_SUBTYPE(ife->ifm_media)) {
233                 case IFM_AUTO:
234 #ifdef foo
235                         /*
236                          * If we're already in auto mode, just return.
237                          */
238                         if (PHY_READ(sc, BRGPHY_MII_BMCR) & BRGPHY_BMCR_AUTOEN)
239                                 return (0);
240 #endif
241                         brgphy_mii_phy_auto(sc);
242                         break;
243                 case IFM_1000_T:
244                         speed = BRGPHY_S1000;
245                         goto setit;
246                 case IFM_100_TX:
247                         speed = BRGPHY_S100;
248                         goto setit;
249                 case IFM_10_T:
250                         speed = BRGPHY_S10;
251 setit:
252                         brgphy_loop(sc);
253                         if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
254                                 speed |= BRGPHY_BMCR_FDX;
255                                 gig = BRGPHY_1000CTL_AFD;
256                         } else {
257                                 gig = BRGPHY_1000CTL_AHD;
258                         }
259
260                         PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
261                         PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
262                         PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
263
264                         if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
265                                 break;
266
267                         PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
268                         PHY_WRITE(sc, BRGPHY_MII_BMCR,
269                             speed|BRGPHY_BMCR_AUTOEN|BRGPHY_BMCR_STARTNEG);
270
271                         if (sc->mii_model != MII_MODEL_xxBROADCOM_BCM5701)
272                                 break;
273
274                         /*
275                          * When settning the link manually, one side must
276                          * be the master and the other the slave. However
277                          * ifmedia doesn't give us a good way to specify
278                          * this, so we fake it by using one of the LINK
279                          * flags. If LINK0 is set, we program the PHY to
280                          * be a master, otherwise it's a slave.
281                          */
282                         if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
283                                 PHY_WRITE(sc, BRGPHY_MII_1000CTL,
284                                     gig|BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC);
285                         } else {
286                                 PHY_WRITE(sc, BRGPHY_MII_1000CTL,
287                                     gig|BRGPHY_1000CTL_MSE);
288                         }
289                         break;
290 #ifdef foo
291                 case IFM_NONE:
292                         PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
293                         break;
294 #endif
295                 case IFM_100_T4:
296                 default:
297                         return (EINVAL);
298                 }
299                 break;
300
301         case MII_TICK:
302                 /*
303                  * If we're not currently selected, just return.
304                  */
305                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
306                         return (0);
307
308                 /*
309                  * Is the interface even up?
310                  */
311                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
312                         return (0);
313
314                 /*
315                  * Only used for autonegotiation.
316                  */
317                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
318                         break;
319
320                 /*
321                  * Check to see if we have link.  If we do, we don't
322                  * need to restart the autonegotiation process.
323                  */
324                 reg = PHY_READ(sc, BRGPHY_MII_AUXSTS);
325                 if (reg & BRGPHY_AUXSTS_LINK) {
326                         sc->mii_ticks = 0;
327                         break;
328                 }
329
330                 /*
331                  * Only retry autonegotiation every 5 seconds.
332                  */
333                 if (++sc->mii_ticks <= sc->mii_anegticks)
334                         break;
335                 
336                 sc->mii_ticks = 0;
337                 brgphy_mii_phy_auto(sc);
338                 break;
339         }
340
341         /* Update the media status. */
342         brgphy_status(sc);
343
344         /*
345          * Callback if something changed. Note that we need to poke
346          * the DSP on the Broadcom PHYs if the media changes.
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                 switch (sc->mii_model) {
352                 case MII_MODEL_xxBROADCOM_BCM5400:
353                         brgphy_bcm5401_dspcode(sc);
354                         break;
355                 case MII_MODEL_xxBROADCOM_BCM5401:
356                         if (sc->mii_rev == 1 || sc->mii_rev == 3)
357                                 brgphy_bcm5401_dspcode(sc);
358                         break;
359                 case MII_MODEL_xxBROADCOM_BCM5411:
360                         brgphy_bcm5411_dspcode(sc);
361                         break;
362                 }
363         }
364         mii_phy_update(sc, cmd);
365         return (0);
366 }
367
368 static void
369 brgphy_status(struct mii_softc *sc)
370 {
371         struct mii_data *mii = sc->mii_pdata;
372         int bmcr, aux;
373
374         mii->mii_media_status = IFM_AVALID;
375         mii->mii_media_active = IFM_ETHER;
376
377         aux = PHY_READ(sc, BRGPHY_MII_AUXSTS);
378         if (aux & BRGPHY_AUXSTS_LINK)
379                 mii->mii_media_status |= IFM_ACTIVE;
380
381         bmcr = PHY_READ(sc, BRGPHY_MII_BMCR);
382         if (bmcr & BRGPHY_BMCR_LOOP)
383                 mii->mii_media_active |= IFM_LOOP;
384
385         if (bmcr & BRGPHY_BMCR_AUTOEN) {
386                 if ((PHY_READ(sc, BRGPHY_MII_BMSR) & BRGPHY_BMSR_ACOMP) == 0) {
387                         /* Erg, still trying, I guess... */
388                         mii->mii_media_active |= IFM_NONE;
389                         return;
390                 }
391
392                 switch (aux & BRGPHY_AUXSTS_AN_RES) {
393                 case BRGPHY_RES_1000FD:
394                         mii->mii_media_active |= IFM_1000_T | IFM_FDX;
395                         break;
396                 case BRGPHY_RES_1000HD:
397                         mii->mii_media_active |= IFM_1000_T | IFM_HDX;
398                         break;
399                 case BRGPHY_RES_100FD:
400                         mii->mii_media_active |= IFM_100_TX | IFM_FDX;
401                         break;
402                 case BRGPHY_RES_100T4:
403                         mii->mii_media_active |= IFM_100_T4;
404                         break;
405                 case BRGPHY_RES_100HD:
406                         mii->mii_media_active |= IFM_100_TX | IFM_HDX;
407                         break;
408                 case BRGPHY_RES_10FD:
409                         mii->mii_media_active |= IFM_10_T | IFM_FDX;
410                         break;
411                 case BRGPHY_RES_10HD:
412                         mii->mii_media_active |= IFM_10_T | IFM_HDX;
413                         break;
414                 default:
415                         mii->mii_media_active |= IFM_NONE;
416                         break;
417                 }
418         } else {
419                 mii->mii_media_active = mii->mii_media.ifm_cur->ifm_media;
420         }
421 }
422
423
424 static void
425 brgphy_mii_phy_auto(struct mii_softc *sc)
426 {
427         int ktcr = 0;
428
429         brgphy_loop(sc);
430         brgphy_reset(sc);
431         ktcr = BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD;
432         if (sc->mii_model == MII_MODEL_xxBROADCOM_BCM5701)
433                 ktcr |= BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC;
434         PHY_WRITE(sc, BRGPHY_MII_1000CTL, ktcr);
435         ktcr = PHY_READ(sc, BRGPHY_MII_1000CTL);
436         DELAY(1000);
437         PHY_WRITE(sc, BRGPHY_MII_ANAR,
438             BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA);
439         DELAY(1000);
440         PHY_WRITE(sc, BRGPHY_MII_BMCR,
441             BRGPHY_BMCR_AUTOEN | BRGPHY_BMCR_STARTNEG);
442         PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
443 }
444
445 static void
446 brgphy_loop(struct mii_softc *sc)
447 {
448         uint32_t bmsr;
449         int i;
450
451         PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
452         for (i = 0; i < 15000; i++) {
453                 bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
454                 if (!(bmsr & BRGPHY_BMSR_LINK))
455                         break;
456                 DELAY(10);
457         }
458 }
459
460 static void
461 brgphy_reset(struct mii_softc *sc)
462 {
463         struct ifnet *ifp;
464
465         mii_phy_reset(sc);
466
467         switch (sc->mii_model) {
468         case MII_MODEL_xxBROADCOM_BCM5400:
469                 brgphy_bcm5401_dspcode(sc);
470                         break;
471         case MII_MODEL_xxBROADCOM_BCM5401:
472                 if (sc->mii_rev == 1 || sc->mii_rev == 3)
473                         brgphy_bcm5401_dspcode(sc);
474                 break;
475         case MII_MODEL_xxBROADCOM_BCM5411:
476                 brgphy_bcm5411_dspcode(sc);
477                 break;
478         case MII_MODEL_xxBROADCOM_BCM5421:
479                 brgphy_bcm5421_dspcode(sc);
480                 break;
481         case MII_MODEL_xxBROADCOM_BCM54K2:
482                 brgphy_bcm54k2_dspcode(sc);
483                 break;
484         }
485
486         ifp = sc->mii_pdata->mii_ifp;
487         if (strncmp(ifp->if_xname, "bge", 3) == 0) {
488                 struct bge_softc *bge_sc = ifp->if_softc;
489
490                 if (bge_sc->bge_flags & BGE_FLAG_ADC_BUG)
491                         brgphy_adc_bug(sc);
492                 if (bge_sc->bge_flags & BGE_FLAG_5704_A0_BUG)
493                         brgphy_5704_a0_bug(sc);
494                 if (bge_sc->bge_flags & BGE_FLAG_BER_BUG) {
495                         brgphy_ber_bug(sc);
496                 } else if (bge_sc->bge_flags & BGE_FLAG_JITTER_BUG) {
497                         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0c00);
498                         PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x000a);
499
500                         if (bge_sc->bge_flags & BGE_FLAG_ADJUST_TRIM) {
501                                 PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, 0x110b);
502                                 PHY_WRITE(sc, BRGPHY_TEST1,
503                                     BRGPHY_TEST1_TRIM_EN | 0x4);
504                         } else {
505                                 PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, 0x010b);
506                         }
507
508                         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0400);
509                 }
510                 if (bge_sc->bge_flags & BGE_FLAG_CRC_BUG)
511                         brgphy_crc_bug(sc);
512
513                 /* Set Jumbo frame settings in the PHY. */
514                 brgphy_jumbo_settings(sc, ifp->if_mtu);
515
516                 /* Enable Ethernet@Wirespeed */
517                 if (bge_sc->bge_flags & BGE_FLAG_ETH_WIRESPEED)
518                         brgphy_eth_wirespeed(sc);
519
520                 /* Enable Link LED on Dell boxes */
521                 if (bge_sc->bge_flags & BGE_FLAG_NO_3LED) {
522                         PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL, 
523                         PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL)
524                                 & ~BRGPHY_PHY_EXTCTL_3_LED);
525                 }
526
527                 /* Adjust output voltage (From Linux driver) */
528                 if (bge_sc->bge_asicrev == BGE_ASICREV_BCM5906)
529                         PHY_WRITE(sc, BRGPHY_MII_EPHY_PTEST, 0x12);
530         } else if (strncmp(ifp->if_xname, "bce", 3) == 0) {
531                 brgphy_ber_bug(sc);
532                 brgphy_jumbo_settings(sc, ifp->if_mtu);
533                 brgphy_eth_wirespeed(sc);
534         }
535 }
536
537 /* Turn off tap power management on 5401. */
538 static void
539 brgphy_bcm5401_dspcode(struct mii_softc *sc)
540 {
541         static const struct {
542                 int             reg;
543                 uint16_t        val;
544         } dspcode[] = {
545                 { BRGPHY_MII_AUXCTL,            0x0c20 },
546                 { BRGPHY_MII_DSP_ADDR_REG,      0x0012 },
547                 { BRGPHY_MII_DSP_RW_PORT,       0x1804 },
548                 { BRGPHY_MII_DSP_ADDR_REG,      0x0013 },
549                 { BRGPHY_MII_DSP_RW_PORT,       0x1204 },
550                 { BRGPHY_MII_DSP_ADDR_REG,      0x8006 },
551                 { BRGPHY_MII_DSP_RW_PORT,       0x0132 },
552                 { BRGPHY_MII_DSP_ADDR_REG,      0x8006 },
553                 { BRGPHY_MII_DSP_RW_PORT,       0x0232 },
554                 { BRGPHY_MII_DSP_ADDR_REG,      0x201f },
555                 { BRGPHY_MII_DSP_RW_PORT,       0x0a20 },
556                 { 0,                            0 },
557         };
558         int i;
559
560         for (i = 0; dspcode[i].reg != 0; i++)
561                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
562         DELAY(40);
563 }
564
565 /* Setting some undocumented voltage */
566 static void
567 brgphy_bcm5411_dspcode(struct mii_softc *sc)
568 {
569         static const struct {
570                 int             reg;
571                 uint16_t        val;
572         } dspcode[] = {
573                 { 0x1c,                         0x8c23 },
574                 { 0x1c,                         0x8ca3 },
575                 { 0x1c,                         0x8c23 },
576                 { 0,                            0 },
577         };
578         int i;
579
580         for (i = 0; dspcode[i].reg != 0; i++)
581                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
582 }
583
584 static void
585 brgphy_bcm5421_dspcode(struct mii_softc *sc)
586 {
587         uint16_t data;
588
589         /* Set Class A mode */
590         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x1007);
591         data = PHY_READ(sc, BRGPHY_MII_AUXCTL);
592         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, data | 0x0400);
593
594         /* Set FFE gamma override to -0.125 */
595         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x0007);
596         data = PHY_READ(sc, BRGPHY_MII_AUXCTL);
597         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, data | 0x0800);
598         PHY_WRITE(sc, BRGPHY_MII_DSP_ADDR_REG, 0x000a);
599         data = PHY_READ(sc, BRGPHY_MII_DSP_RW_PORT);
600         PHY_WRITE(sc, BRGPHY_MII_DSP_RW_PORT, data | 0x0200);
601 }
602
603 static void
604 brgphy_bcm54k2_dspcode(struct mii_softc *sc)
605 {
606         static const struct {
607                 int             reg;
608                 uint16_t        val;
609         } dspcode[] = {
610                 { 4,                            0x01e1 },
611                 { 9,                            0x0300 },
612                 { 0,                            0 },
613         };
614         int i;
615
616         for (i = 0; dspcode[i].reg != 0; i++)
617                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
618 }
619
620 static void
621 brgphy_adc_bug(struct mii_softc *sc)
622 {
623         static const struct {
624                 int             reg;
625                 uint16_t        val;
626         } dspcode[] = {
627                 { BRGPHY_MII_AUXCTL,            0x0c00 },
628                 { BRGPHY_MII_DSP_ADDR_REG,      0x201f },
629                 { BRGPHY_MII_DSP_RW_PORT,       0x2aaa },
630                 { BRGPHY_MII_DSP_ADDR_REG,      0x000a },
631                 { BRGPHY_MII_DSP_RW_PORT,       0x0323 },
632                 { BRGPHY_MII_AUXCTL,            0x0400 },
633                 { 0,                            0 },
634         };
635         int i;
636
637         for (i = 0; dspcode[i].reg != 0; i++)
638                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
639 }
640
641 static void
642 brgphy_5704_a0_bug(struct mii_softc *sc)
643 {
644         static const struct {
645                 int             reg;
646                 u_int16_t       val;
647         } dspcode[] = {
648                 { 0x1c,                         0x8d68 },
649                 { 0x1c,                         0x8d68 },
650                 { 0,                            0 },
651         };
652         int i;
653
654         for (i = 0; dspcode[i].reg != 0; i++)
655                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
656 }
657
658 static void
659 brgphy_ber_bug(struct mii_softc *sc)
660 {
661         static const struct {
662                 int             reg;
663                 uint16_t        val;
664         } dspcode[] = {
665                 { BRGPHY_MII_AUXCTL,            0x0c00 },
666                 { BRGPHY_MII_DSP_ADDR_REG,      0x000a },
667                 { BRGPHY_MII_DSP_RW_PORT,       0x310b },
668                 { BRGPHY_MII_DSP_ADDR_REG,      0x201f },
669                 { BRGPHY_MII_DSP_RW_PORT,       0x9506 },
670                 { BRGPHY_MII_DSP_ADDR_REG,      0x401f },
671                 { BRGPHY_MII_DSP_RW_PORT,       0x14e2 },
672                 { BRGPHY_MII_AUXCTL,            0x0400 },
673                 { 0,                            0 },
674         };
675         int i;
676
677         for (i = 0; dspcode[i].reg != 0; i++)
678                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
679 }
680
681 static void
682 brgphy_crc_bug(struct mii_softc *sc)
683 {
684         static const struct {
685                 int             reg;
686                 uint16_t        val;
687         } dspcode[] = {
688                 { BRGPHY_MII_DSP_ADDR_REG,      0x0a75 },
689                 { 0x1c,                         0x8c68 },
690                 { 0x1c,                         0x8d68 },
691                 { 0x1c,                         0x8c68 },
692                 { 0,                            0 },
693         };
694         int i;
695
696         for (i = 0; dspcode[i].reg != 0; i++)
697                 PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
698 }
699
700 static void
701 brgphy_jumbo_settings(struct mii_softc *sc, u_long mtu)
702 {
703         uint32_t val;
704
705         /* Set or clear jumbo frame settings in the PHY. */
706         if (mtu > ETHER_MAX_LEN) {
707                 if (sc->mii_model == MII_MODEL_xxBROADCOM_BCM5401) {
708                         /* BCM5401 PHY cannot read-modify-write. */
709                         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x4c20);
710                 } else {
711                         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
712                         val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
713                         PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
714                             val | BRGPHY_AUXCTL_LONG_PKT);
715                 }
716
717                 val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
718                 PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
719                     val | BRGPHY_PHY_EXTCTL_HIGH_LA);
720         } else {
721                 PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7);
722                 val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
723                 PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
724                     val & ~(BRGPHY_AUXCTL_LONG_PKT | 0x7));
725
726                 val = PHY_READ(sc, BRGPHY_MII_PHY_EXTCTL);
727                 PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
728                     val & ~BRGPHY_PHY_EXTCTL_HIGH_LA);
729         }
730 }
731
732 static void
733 brgphy_eth_wirespeed(struct mii_softc *sc)
734 {
735         u_int32_t val;
736
737         /* Enable Ethernet@Wirespeed */
738         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
739         val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
740         PHY_WRITE(sc, BRGPHY_MII_AUXCTL, (val | (1 << 15) | (1 << 4)));
741 }