f1bd50df99adb77e589508e891b9d9aec24d977b
[dragonfly.git] / sys / dev / netif / mii_layer / atphy.c
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  * $FreeBSD: src/sys/dev/mii/atphy.c,v 1.3 2008/10/25 06:39:17 yongari Exp $
28  */
29
30 /*
31  * Driver for the Attansic/Atheros F1 10/100/1000 PHY.
32  */
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37
38 #include <net/if.h>
39 #include <net/if_media.h>
40
41 #include <dev/netif/mii_layer/mii.h>
42 #include <dev/netif/mii_layer/miivar.h>
43 #include <dev/netif/mii_layer/miidevs.h>
44 #include <dev/netif/mii_layer/atphyreg.h>
45
46 #include "miibus_if.h"
47
48 static int      atphy_probe(device_t);
49 static int      atphy_attach(device_t);
50 static int      atphy_service(struct mii_softc *, struct mii_data *, int);
51 static void     atphy_status(struct mii_softc *);
52 static void     atphy_reset(struct mii_softc *);
53 static uint16_t atphy_anar(struct ifmedia_entry *);
54 static void     atphy_auto(struct mii_softc *);
55
56 static device_method_t atphy_methods[] = {
57         /* Device interface. */
58         DEVMETHOD(device_probe,         atphy_probe),
59         DEVMETHOD(device_attach,        atphy_attach),
60         DEVMETHOD(device_detach,        ukphy_detach),
61         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
62         { 0, 0 }
63 };
64
65 static const struct mii_phydesc atphys[] = {
66         MII_PHYDESC(ATHEROS,    F1),
67         MII_PHYDESC(ATHEROS,    F2),
68         MII_PHYDESC_NULL
69 };
70
71 static devclass_t atphy_devclass;
72
73 static driver_t atphy_driver = {
74         "atphy",
75         atphy_methods,
76         sizeof(struct mii_softc)
77 };
78
79 DRIVER_MODULE(atphy, miibus, atphy_driver, atphy_devclass, 0, 0);
80
81 static int
82 atphy_probe(device_t dev)
83 {
84         struct mii_attach_args *ma = device_get_ivars(dev);
85         const struct mii_phydesc *mpd;
86
87         mpd = mii_phy_match(ma, atphys);
88         if (mpd != NULL) {
89                 device_set_desc(dev, mpd->mpd_name);
90                 return 0;
91         }
92         return ENXIO;
93 }
94
95 static int
96 atphy_attach(device_t dev)
97 {
98         struct mii_softc *sc;
99         struct mii_attach_args *ma;
100         struct mii_data *mii;
101
102         sc = device_get_softc(dev);
103         ma = device_get_ivars(dev);
104
105         mii_softc_init(sc, ma);
106         sc->mii_dev = device_get_parent(dev);
107         mii = device_get_softc(sc->mii_dev);
108         LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
109
110         sc->mii_inst = mii->mii_instance;
111         sc->mii_service = atphy_service;
112         sc->mii_pdata = mii;
113         sc->mii_anegticks = MII_ANEGTICKS_GIGE;
114
115         mii->mii_instance++;
116
117         sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
118         if (sc->mii_capabilities & BMSR_EXTSTAT)
119                 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
120
121         device_printf(dev, " ");
122         if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 &&
123             (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0)
124                 kprintf("no media present");
125         else
126                 mii_phy_add_media(sc);
127         kprintf("\n");
128
129         atphy_reset(sc);
130
131         MIIBUS_MEDIAINIT(sc->mii_dev);
132         return 0;
133 }
134
135 static int
136 atphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
137 {
138         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
139         uint16_t anar, bmcr, bmsr;
140
141         switch (cmd) {
142         case MII_POLLSTAT:
143                 /*
144                  * If we're not polling our PHY instance, just return.
145                  */
146                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
147                         return 0;
148                 break;
149
150         case MII_MEDIACHG:
151                 /*
152                  * If the media indicates a different PHY instance,
153                  * isolate ourselves.
154                  */
155                 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
156                         bmcr = PHY_READ(sc, MII_BMCR);
157                         PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
158                         return 0;
159                 }
160
161                 /*
162                  * If the interface is not up, don't do anything.
163                  */
164                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
165                         break;
166
167                 if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO ||
168                     IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
169                         atphy_auto(sc);
170                         break;
171                 }
172
173                 bmcr = 0;
174                 switch (IFM_SUBTYPE(ife->ifm_media)) {
175                 case IFM_100_TX:
176                         bmcr = BMCR_S100;
177                         break;
178
179                 case IFM_10_T:
180                         bmcr = BMCR_S10;
181                         break;
182
183                 case IFM_NONE:
184                         bmcr = PHY_READ(sc, MII_BMCR);
185                         /*
186                          * XXX
187                          * Due to an unknown reason powering down PHY resulted
188                          * in unexpected results such as inaccessbility of
189                          * hardware of freshly rebooted system.  Disable
190                          * powering down PHY until I got more information for
191                          * Attansic/Atheros PHY hardwares.
192                          */
193                         PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO);
194                         goto done;
195
196                 default:
197                         return EINVAL;
198                 }
199
200                 anar = atphy_anar(ife);
201                 if ((ife->ifm_media & IFM_GMASK) & IFM_FDX) {
202                         bmcr |= BMCR_FDX;
203                         /* Enable pause. */
204                         anar |= (3 << 10);
205                 }
206
207                 if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX))
208                         PHY_WRITE(sc, MII_100T2CR, 0);
209                 PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA);
210
211                 /*
212                  * Reset the PHY so all changes take effect.
213                  */
214                 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_RESET | BMCR_AUTOEN |
215                     BMCR_STARTNEG);
216 done:
217                 break;
218
219         case MII_TICK:
220                 /*
221                  * If we're not currently selected, just return.
222                  */
223                 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
224                         return 0;
225
226                 /*
227                  * Is the interface even up?
228                  */
229                 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
230                         return 0;
231
232                 /*
233                  * Only used for autonegotiation.
234                  */
235                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
236                         sc->mii_ticks = 0;
237                         break;
238                 }
239
240                 /*
241                  * check for link.
242                  * Read the status register twice; BMSR_LINK is latch-low.
243                  */
244                 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
245                 if (bmsr & BMSR_LINK) {
246                         sc->mii_ticks = 0;
247                         break;
248                 }
249
250                 /* Announce link loss right after it happens. */
251                 if (sc->mii_ticks++ == 0)
252                         break;
253                 if (sc->mii_ticks <= sc->mii_anegticks)
254                         return 0;
255
256                 sc->mii_ticks = 0;
257                 atphy_auto(sc);
258                 break;
259         }
260
261         /* Update the media status. */
262         atphy_status(sc);
263
264         /* Callback if something changed. */
265         mii_phy_update(sc, cmd);
266         return 0;
267 }
268
269 static void
270 atphy_status(struct mii_softc *sc)
271 {
272         struct mii_data *mii = sc->mii_pdata;
273         uint32_t bmsr, bmcr, ssr;
274
275         mii->mii_media_status = IFM_AVALID;
276         mii->mii_media_active = IFM_ETHER;
277
278         bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
279         if (bmsr & BMSR_LINK)
280                 mii->mii_media_status |= IFM_ACTIVE;
281
282         bmcr = PHY_READ(sc, MII_BMCR);
283         if (bmcr & BMCR_ISO) {
284                 mii->mii_media_active |= IFM_NONE;
285                 mii->mii_media_status = 0;
286                 return;
287         }
288
289         if (bmcr & BMCR_LOOP)
290                 mii->mii_media_active |= IFM_LOOP;
291
292         ssr = PHY_READ(sc, ATPHY_SSR);
293         if ((ssr & ATPHY_SSR_SPD_DPLX_RESOLVED) == 0) {
294                 /* Erg, still trying, I guess... */
295                 mii->mii_media_active |= IFM_NONE;
296                 return;
297         }
298
299         switch (ssr & ATPHY_SSR_SPEED_MASK) {
300         case ATPHY_SSR_1000MBS:
301                 mii->mii_media_active |= IFM_1000_T;
302                 /*
303                  * atphy(4) got a valid link so reset mii_ticks.
304                  * Resetting mii_ticks is needed in order to
305                  * detect link loss after auto-negotiation.
306                  */
307                 sc->mii_ticks = 0;
308                 break;
309
310         case ATPHY_SSR_100MBS:
311                 mii->mii_media_active |= IFM_100_TX;
312                 sc->mii_ticks = 0;
313                 break;
314
315         case ATPHY_SSR_10MBS:
316                 mii->mii_media_active |= IFM_10_T;
317                 sc->mii_ticks = 0;
318                 break;
319
320         default:
321                 mii->mii_media_active |= IFM_NONE;
322                 return;
323         }
324
325         if (ssr & ATPHY_SSR_DUPLEX)
326                 mii->mii_media_active |= IFM_FDX;
327         else
328                 mii->mii_media_active |= IFM_HDX;
329
330         /* XXX Master/Slave, Flow-control */
331 }
332
333 static void
334 atphy_reset(struct mii_softc *sc)
335 {
336         uint32_t reg;
337         int i;
338
339         /* Take PHY out of power down mode. */
340         PHY_WRITE(sc, 29, 0x29);
341         PHY_WRITE(sc, 30, 0);
342
343         reg = PHY_READ(sc, ATPHY_SCR);
344         /* Enable automatic crossover. */
345         reg |= ATPHY_SCR_AUTO_X_MODE;
346         /* Disable power down. */
347         reg &= ~ATPHY_SCR_MAC_PDOWN;
348         /* Enable CRS on Tx. */
349         reg |= ATPHY_SCR_ASSERT_CRS_ON_TX;
350         /* Auto correction for reversed cable polarity. */
351         reg |= ATPHY_SCR_POLARITY_REVERSAL;
352         PHY_WRITE(sc, ATPHY_SCR, reg);
353
354         /* Workaround F1 bug to reset phy. */
355         atphy_auto(sc);
356
357         for (i = 0; i < 1000; i++) {
358                 DELAY(1);
359                 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0)
360                         break;
361         }
362 }
363
364 static uint16_t
365 atphy_anar(struct ifmedia_entry *ife)
366 {
367         uint16_t anar = 0;
368
369         switch (IFM_SUBTYPE(ife->ifm_media)) {
370         case IFM_AUTO:
371                 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10;
372                 return anar;
373
374         case IFM_1000_T:
375                 return anar;
376
377         case IFM_100_TX:
378                 anar |= ANAR_TX;
379                 break;
380
381         case IFM_10_T:
382                 anar |= ANAR_10;
383                 break;
384
385         default:
386                 return 0;
387         }
388
389         if ((ife->ifm_media & IFM_GMASK) & IFM_FDX) {
390                 if (IFM_SUBTYPE(ife->ifm_media) == IFM_100_TX)
391                         anar |= ANAR_TX_FD;
392                 else
393                         anar |= ANAR_10_FD;
394         }
395         return anar;
396 }
397
398 static void
399 atphy_auto(struct mii_softc *sc)
400 {
401         uint16_t anar;
402
403         anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities);
404         PHY_WRITE(sc, MII_ANAR, anar | (3 << 10) | ANAR_CSMA);
405
406         if (sc->mii_extcapabilities & (EXTSR_1000TFDX | EXTSR_1000THDX)) {
407                 PHY_WRITE(sc, MII_100T2CR, GTCR_ADV_1000TFDX |
408                     GTCR_ADV_1000THDX);
409         }
410         PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
411 }