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