xref: /dragonfly/sys/dev/netif/mii_layer/atphy.c (revision 502d982c7d2fa8a5f352ab7ee72e877d7b541538)
1 /*-
2  * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * Driver for the Attansic/Atheros F1 10/100/1000 PHY.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/socket.h>
38 
39 #include <net/if.h>
40 #include <net/if_media.h>
41 
42 #include <dev/netif/mii_layer/mii.h>
43 #include <dev/netif/mii_layer/miivar.h>
44 #include <dev/netif/mii_layer/atphyreg.h>
45 
46 #include "miibus_if.h"
47 #include "miidevs.h"
48 
49 static int          atphy_probe(device_t);
50 static int          atphy_attach(device_t);
51 static int          atphy_service(struct mii_softc *, struct mii_data *, int);
52 static void         atphy_status(struct mii_softc *);
53 static void         atphy_reset(struct mii_softc *);
54 static uint16_t     atphy_anar(struct ifmedia_entry *);
55 static int          atphy_setmedia(struct mii_softc *sc, int media);
56 
57 static device_method_t atphy_methods[] = {
58           /* Device interface. */
59           DEVMETHOD(device_probe,                 atphy_probe),
60           DEVMETHOD(device_attach,      atphy_attach),
61           DEVMETHOD(device_detach,        ukphy_detach),
62           DEVMETHOD(device_shutdown,    bus_generic_shutdown),
63           DEVMETHOD_END
64 };
65 
66 static devclass_t atphy_devclass;
67 static driver_t atphy_driver = {
68           "atphy",
69           atphy_methods,
70           sizeof(struct mii_softc)
71 };
72 
73 DRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, NULL, NULL);
74 
75 static const struct mii_phydesc atphys[] = {
76 #if 0
77           MII_PHYDESC(xxATHEROS,        F1),
78           MII_PHYDESC(xxATHEROS,        F1_7),
79           MII_PHYDESC(xxATHEROS,        AR8021),
80           MII_PHYDESC(xxATHEROS,        F2),
81 #endif
82           MII_PHYDESC(ATHEROS,          F1),
83           MII_PHYDESC(ATHEROS,    F1_7),
84           MII_PHYDESC(ATHEROS,    F2),
85           MII_PHYDESC_NULL
86 };
87 
88 #if 0
89 static const struct mii_phy_funcs atphy_funcs = {
90           atphy_service,
91           atphy_status,
92           atphy_reset
93 };
94 #endif
95 
96 static int
atphy_probe(device_t dev)97 atphy_probe(device_t dev)
98 {
99           /*return (mii_phy_dev_probe(dev, atphys, BUS_PROBE_DEFAULT));*/
100           struct mii_attach_args *ma = device_get_ivars(dev);
101           const struct mii_phydesc *mpd;
102 
103           mpd = mii_phy_match(ma, atphys);
104           if (mpd != NULL) {
105                     device_set_desc(dev, mpd->mpd_name);
106                     return 0;
107           }
108           return ENXIO;
109 }
110 
111 static int
atphy_attach(device_t dev)112 atphy_attach(device_t dev)
113 {
114           /*mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &atphy_funcs, 1);*/
115 
116           struct mii_softc *sc;
117           struct mii_attach_args *ma;
118           struct mii_data *mii;
119 
120           sc = device_get_softc(dev);
121           ma = device_get_ivars(dev);
122 
123           mii_softc_init(sc, ma);
124           sc->mii_dev = device_get_parent(dev);
125           mii = device_get_softc(sc->mii_dev);
126           LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
127 
128           sc->mii_inst = mii->mii_instance;
129           sc->mii_service = atphy_service;
130           sc->mii_pdata = mii;
131           sc->mii_anegticks = MII_ANEGTICKS_GIGE;
132 
133           mii->mii_instance++;
134 
135           sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
136           if (sc->mii_capabilities & BMSR_EXTSTAT)
137                     sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
138 
139           device_printf(dev, " ");
140           if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
141               (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
142                     kprintf("no media present");
143           else
144                     mii_phy_add_media(sc);
145           kprintf("\n");
146 
147           atphy_reset(sc);
148 
149           MIIBUS_MEDIAINIT(sc->mii_dev);
150           return (0);
151 }
152 
153 static int
atphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)154 atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
155 {
156           struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
157           uint16_t anar, bmcr, bmsr;
158 
159           switch (cmd) {
160           case MII_POLLSTAT:
161                     break;
162 
163           case MII_MEDIACHG:
164                     if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
165                         IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
166                               atphy_setmedia(sc, ife->ifm_media);
167                               break;
168                     }
169 
170                     bmcr = 0;
171                     switch (IFM_SUBTYPE(ife->ifm_media)) {
172                     case IFM_100_TX:
173                               bmcr = BMCR_S100;
174                               break;
175                     case IFM_10_T:
176                               bmcr = BMCR_S10;
177                               break;
178                     case IFM_NONE:
179                               bmcr = PHY_READ(sc, MII_BMCR);
180                               /*
181                                * XXX
182                                * Due to an unknown reason powering down PHY resulted
183                                * in unexpected results such as inaccessibility of
184                                * hardware of freshly rebooted system. Disable
185                                * powering down PHY until I got more information for
186                                * Attansic/Atheros PHY hardwares.
187                                */
188                               PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
189                               goto done;
190                     default:
191                               return (EINVAL);
192                     }
193 
194                     anar = atphy_anar(ife);
195                     if ((ife->ifm_media & IFM_FDX) != 0) {
196                               bmcr |= BMCR_FDX;
197 #if defined(__FreeBSD__)
198                               if ((ife->ifm_media & IFM_FLOW) != 0 ||
199                                   (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
200                                         anar |= ANAR_PAUSE_TOWARDS;
201 #else
202                               if ((ife->ifm_media & IFM_FLOW) != 0)
203                                         anar |= ANAR_PAUSE_TOWARDS;
204 #endif
205                     }
206 
207                     if ((sc->mii_extcapabilities & (EXTSR_1000TFDX |
208                         EXTSR_1000THDX)) != 0)
209                               PHY_WRITE(sc, MII_100T2CR, 0);
210                     PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
211 
212                     /*
213                      * Reset the PHY so all changes take effect.
214                      */
215                     PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN |
216                         BMCR_STARTNEG);
217 done:
218                     break;
219 
220           case MII_TICK:
221                     /*
222                      * Only used for autonegotiation.
223                      */
224                     if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
225                               sc->mii_ticks = 0;
226                               break;
227                     }
228 
229                     /*
230                      * Check for link.
231                      * Read the status register twice; BMSR_LINK is latch-low.
232                      */
233                     bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
234                     if (bmsr & BMSR_LINK) {
235                               sc->mii_ticks = 0;
236                               break;
237                     }
238 
239                     /* Announce link loss right after it happens. */
240                     if (sc->mii_ticks++ == 0)
241                               break;
242                     if (sc->mii_ticks <= sc->mii_anegticks)
243                               return (0);
244 
245                     sc->mii_ticks = 0;
246                     atphy_setmedia(sc, ife->ifm_media);
247                     break;
248           }
249 
250           /* Update the media status. */
251           atphy_status(sc);
252 
253           /* Callback if something changed. */
254           mii_phy_update(sc, cmd);
255           return (0);
256 }
257 
258 static void
atphy_status(struct mii_softc * sc)259 atphy_status(struct mii_softc *sc)
260 {
261           struct mii_data *mii = sc->mii_pdata;
262           uint32_t bmsr, bmcr, ssr;
263 
264           mii->mii_media_status = IFM_AVALID;
265           mii->mii_media_active = IFM_ETHER;
266 
267           bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
268           if ((bmsr & BMSR_LINK) != 0)
269                     mii->mii_media_status |= IFM_ACTIVE;
270 
271           bmcr = PHY_READ(sc, MII_BMCR);
272           if ((bmcr & BMCR_ISO) != 0) {
273                     mii->mii_media_active |= IFM_NONE;
274                     mii->mii_media_status = 0;
275                     return;
276           }
277 
278           if ((bmcr & BMCR_LOOP) != 0)
279                     mii->mii_media_active |= IFM_LOOP;
280 
281           ssr = PHY_READ(sc, ATPHY_SSR);
282           if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
283                     /* Erg, still trying, I guess... */
284                     mii->mii_media_active |= IFM_NONE;
285                     return;
286           }
287 
288           switch (ssr & ATPHY_SSR_SPEED_MASK) {
289           case ATPHY_SSR_1000MBS:
290                     mii->mii_media_active |= IFM_1000_T;
291                     /*
292                      * atphy(4) has a valid link so reset mii_ticks.
293                      * Resetting mii_ticks is needed in order to
294                      * detect link loss after auto-negotiation.
295                      */
296                     sc->mii_ticks = 0;
297                     break;
298           case ATPHY_SSR_100MBS:
299                     mii->mii_media_active |= IFM_100_TX;
300                     sc->mii_ticks = 0;
301                     break;
302           case ATPHY_SSR_10MBS:
303                     mii->mii_media_active |= IFM_10_T;
304                     sc->mii_ticks = 0;
305                     break;
306           default:
307                     mii->mii_media_active |= IFM_NONE;
308                     return;
309           }
310 
311           if ((ssr & ATPHY_SSR_DUPLEX) != 0)
312                     mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
313           else
314                     mii->mii_media_active |= IFM_HDX;
315 
316           if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
317               (PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0)
318                     mii->mii_media_active |= IFM_ETH_MASTER;
319 }
320 
321 static void
atphy_reset(struct mii_softc * sc)322 atphy_reset(struct mii_softc *sc)
323 {
324           struct ifmedia_entry *ife = sc->mii_pdata->mii_media.ifm_cur;
325           uint32_t reg;
326           int i;
327 
328           /* Take PHY out of power down mode. */
329           PHY_WRITE(sc, 29, 0x29);
330           PHY_WRITE(sc, 30, 0);
331 
332           reg = PHY_READ(sc, ATPHY_SCR);
333           /* Enable automatic crossover. */
334           reg |= ATPHY_SCR_AUTO_X_MODE;
335           /* Disable power down. */
336           reg &= ~ATPHY_SCR_MAC_PDOWN;
337           /* Enable CRS on Tx. */
338           reg |= ATPHY_SCR_ASSERT_CRS_ON_TX;
339           /* Auto correction for reversed cable polarity. */
340           reg |= ATPHY_SCR_POLARITY_REVERSAL;
341           PHY_WRITE(sc, ATPHY_SCR, reg);
342 
343           /* Workaround F1 bug to reset phy. */
344           atphy_setmedia(sc, ife == NULL ? IFM_AUTO : ife->ifm_media);
345 
346           for (i = 0; i < 1000; i++) {
347                     DELAY(1);
348                     if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
349                               break;
350           }
351 }
352 
353 static uint16_t
atphy_anar(struct ifmedia_entry * ife)354 atphy_anar(struct ifmedia_entry *ife)
355 {
356           uint16_t anar;
357 
358           anar = 0;
359           switch (IFM_SUBTYPE(ife->ifm_media)) {
360           case IFM_AUTO:
361                     anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
362                     return (anar);
363           case IFM_1000_T:
364                     return (anar);
365           case IFM_100_TX:
366                     anar |= ANAR_TX;
367                     break;
368           case IFM_10_T:
369                     anar |= ANAR_10;
370                     break;
371           default:
372                     return (0);
373           }
374 
375           if ((ife->ifm_media & IFM_FDX) != 0) {
376                     if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX)
377                               anar |= ANAR_TX_FD;
378                     else
379                               anar |= ANAR_10_FD;
380           }
381 
382           return (anar);
383 }
384 
385 static int
atphy_setmedia(struct mii_softc * sc,int media)386 atphy_setmedia(struct mii_softc *sc, int media)
387 {
388           uint16_t anar;
389 
390           anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
391 #if defined(__FreeBSD__)
392           if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) &&
393               ((media & IFM_FLOW) != 0 ||
394               (sc->mii_flags & MIIF_FORCEPAUSE) != 0))
395                     anar |= ANAR_PAUSE_TOWARDS;
396 #else
397           if ((IFM_SUBTYPE(media) == IFM_AUTO || (media & IFM_FDX) != 0) &&
398               ((media & IFM_FLOW) != 0))
399                     anar |= ANAR_PAUSE_TOWARDS;
400 #endif
401           PHY_WRITE(sc, MII_ANAR, anar);
402           if ((sc->mii_extcapabilities &
403                (EXTSR_1000TFDX | EXTSR_1000THDX)) != 0)
404                     PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX |
405                         GTCR_ADV_1000THDX);
406           else if (sc->mii_model == MII_MODEL_ATHEROS_F1) {
407                     /*
408                      * AR8132 has 10/100 PHY and the PHY uses the same
409                      * model number of F1 gigabit PHY.  The PHY has no
410                      * ability to establish gigabit link so explicitly
411                      * disable 1000baseT configuration for the PHY.
412                      * Otherwise, there is a case that atphy(4) could
413                      * not establish a link against gigabit link partner
414                      * unless the link partner supports down-shifting.
415                      */
416                     PHY_WRITE(sc, MII_100T2CR, 0);
417           }
418           PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
419 
420           return (EJUSTRETURN);
421 }
422