]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mii/ciphy.c
MFV r354582: file 5.37.
[FreeBSD/FreeBSD.git] / sys / dev / mii / ciphy.c
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 2004
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 __FBSDID("$FreeBSD$");
37
38 /*
39  * Driver for the Cicada/Vitesse CS/VSC8xxx 10/100/1000 copper PHY.
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/module.h>
46 #include <sys/socket.h>
47 #include <sys/bus.h>
48
49 #include <net/if.h>
50 #include <net/if_arp.h>
51 #include <net/if_media.h>
52
53 #include <dev/mii/mii.h>
54 #include <dev/mii/miivar.h>
55 #include "miidevs.h"
56
57 #include <dev/mii/ciphyreg.h>
58
59 #include "miibus_if.h"
60
61 #include <machine/bus.h>
62
63 static int ciphy_probe(device_t);
64 static int ciphy_attach(device_t);
65
66 static device_method_t ciphy_methods[] = {
67         /* device interface */
68         DEVMETHOD(device_probe,         ciphy_probe),
69         DEVMETHOD(device_attach,        ciphy_attach),
70         DEVMETHOD(device_detach,        mii_phy_detach),
71         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
72         DEVMETHOD_END
73 };
74
75 static devclass_t ciphy_devclass;
76
77 static driver_t ciphy_driver = {
78         "ciphy",
79         ciphy_methods,
80         sizeof(struct mii_softc)
81 };
82
83 DRIVER_MODULE(ciphy, miibus, ciphy_driver, ciphy_devclass, 0, 0);
84
85 static int      ciphy_service(struct mii_softc *, struct mii_data *, int);
86 static void     ciphy_status(struct mii_softc *);
87 static void     ciphy_reset(struct mii_softc *);
88 static void     ciphy_fixup(struct mii_softc *);
89
90 static const struct mii_phydesc ciphys[] = {
91         MII_PHY_DESC(xxCICADA, CS8201),
92         MII_PHY_DESC(xxCICADA, CS8201A),
93         MII_PHY_DESC(xxCICADA, CS8201B),
94         MII_PHY_DESC(xxCICADA, CS8204),
95         MII_PHY_DESC(xxCICADA, VSC8211),
96         MII_PHY_DESC(xxCICADA, VSC8221),
97         MII_PHY_DESC(xxCICADA, CS8244),
98         MII_PHY_DESC(xxVITESSE, VSC8601),
99         MII_PHY_DESC(xxVITESSE, VSC8641),
100         MII_PHY_END
101 };
102
103 static const struct mii_phy_funcs ciphy_funcs = {
104         ciphy_service,
105         ciphy_status,
106         ciphy_reset
107 };
108
109 static int
110 ciphy_probe(device_t dev)
111 {
112
113         return (mii_phy_dev_probe(dev, ciphys, BUS_PROBE_DEFAULT));
114 }
115
116 static int
117 ciphy_attach(device_t dev)
118 {
119
120         mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
121             &ciphy_funcs, 1);
122         return (0);
123 }
124
125 static int
126 ciphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
127 {
128         struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
129         int reg, speed, gig;
130
131         switch (cmd) {
132         case MII_POLLSTAT:
133                 break;
134
135         case MII_MEDIACHG:
136                 ciphy_fixup(sc);        /* XXX hardware bug work-around */
137
138                 switch (IFM_SUBTYPE(ife->ifm_media)) {
139                 case IFM_AUTO:
140 #ifdef foo
141                         /*
142                          * If we're already in auto mode, just return.
143                          */
144                         if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN)
145                                 return (0);
146 #endif
147                         (void)mii_phy_auto(sc);
148                         break;
149                 case IFM_1000_T:
150                         speed = CIPHY_S1000;
151                         goto setit;
152                 case IFM_100_TX:
153                         speed = CIPHY_S100;
154                         goto setit;
155                 case IFM_10_T:
156                         speed = CIPHY_S10;
157 setit:
158                         if ((ife->ifm_media & IFM_FDX) != 0) {
159                                 speed |= CIPHY_BMCR_FDX;
160                                 gig = CIPHY_1000CTL_AFD;
161                         } else
162                                 gig = CIPHY_1000CTL_AHD;
163
164                         if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) {
165                                 gig |= CIPHY_1000CTL_MSE;
166                                 if ((ife->ifm_media & IFM_ETH_MASTER) != 0)
167                                         gig |= CIPHY_1000CTL_MSC;
168                                 speed |=
169                                     CIPHY_BMCR_AUTOEN | CIPHY_BMCR_STARTNEG;
170                         } else
171                                 gig = 0;
172                         PHY_WRITE(sc, CIPHY_MII_1000CTL, gig);
173                         PHY_WRITE(sc, CIPHY_MII_BMCR, speed);
174                         PHY_WRITE(sc, CIPHY_MII_ANAR, CIPHY_SEL_TYPE);
175                         break;
176                 case IFM_NONE:
177                         PHY_WRITE(sc, MII_BMCR, BMCR_ISO | BMCR_PDOWN);
178                         break;
179                 default:
180                         return (EINVAL);
181                 }
182                 break;
183
184         case MII_TICK:
185                 /*
186                  * Only used for autonegotiation.
187                  */
188                 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
189                         break;
190
191                 /*
192                  * Check to see if we have link.  If we do, we don't
193                  * need to restart the autonegotiation process.  Read
194                  * the BMSR twice in case it's latched.
195                  */
196                 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
197                 if (reg & BMSR_LINK)
198                         break;
199
200                 /* Announce link loss right after it happens. */
201                 if (++sc->mii_ticks == 0)
202                         break;
203                 /*
204                  * Only retry autonegotiation every mii_anegticks seconds.
205                  */
206                 if (sc->mii_ticks <= sc->mii_anegticks)
207                         break;
208
209                 sc->mii_ticks = 0;
210                 mii_phy_auto(sc);
211                 break;
212         }
213
214         /* Update the media status. */
215         PHY_STATUS(sc);
216
217         /*
218          * Callback if something changed. Note that we need to poke
219          * apply fixups for certain PHY revs.
220          */
221         if (sc->mii_media_active != mii->mii_media_active ||
222             sc->mii_media_status != mii->mii_media_status ||
223             cmd == MII_MEDIACHG) {
224                 ciphy_fixup(sc);
225         }
226         mii_phy_update(sc, cmd);
227         return (0);
228 }
229
230 static void
231 ciphy_status(struct mii_softc *sc)
232 {
233         struct mii_data *mii = sc->mii_pdata;
234         int bmsr, bmcr;
235
236         mii->mii_media_status = IFM_AVALID;
237         mii->mii_media_active = IFM_ETHER;
238
239         bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
240
241         if (bmsr & BMSR_LINK)
242                 mii->mii_media_status |= IFM_ACTIVE;
243
244         bmcr = PHY_READ(sc, CIPHY_MII_BMCR);
245
246         if (bmcr & CIPHY_BMCR_LOOP)
247                 mii->mii_media_active |= IFM_LOOP;
248
249         if (bmcr & CIPHY_BMCR_AUTOEN) {
250                 if ((bmsr & CIPHY_BMSR_ACOMP) == 0) {
251                         /* Erg, still trying, I guess... */
252                         mii->mii_media_active |= IFM_NONE;
253                         return;
254                 }
255         }
256
257         bmsr = PHY_READ(sc, CIPHY_MII_AUXCSR);
258         switch (bmsr & CIPHY_AUXCSR_SPEED) {
259         case CIPHY_SPEED10:
260                 mii->mii_media_active |= IFM_10_T;
261                 break;
262         case CIPHY_SPEED100:
263                 mii->mii_media_active |= IFM_100_TX;
264                 break;
265         case CIPHY_SPEED1000:
266                 mii->mii_media_active |= IFM_1000_T;
267                 break;
268         default:
269                 device_printf(sc->mii_dev, "unknown PHY speed %x\n",
270                     bmsr & CIPHY_AUXCSR_SPEED);
271                 break;
272         }
273
274         if (bmsr & CIPHY_AUXCSR_FDX)
275                 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
276         else
277                 mii->mii_media_active |= IFM_HDX;
278
279         if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) &&
280            (PHY_READ(sc, CIPHY_MII_1000STS) & CIPHY_1000STS_MSR) != 0)
281                 mii->mii_media_active |= IFM_ETH_MASTER;
282 }
283
284 static void
285 ciphy_reset(struct mii_softc *sc)
286 {
287
288         mii_phy_reset(sc);
289         DELAY(1000);
290 }
291
292 #define PHY_SETBIT(x, y, z) \
293         PHY_WRITE(x, y, (PHY_READ(x, y) | (z)))
294 #define PHY_CLRBIT(x, y, z) \
295         PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z)))
296
297 static void
298 ciphy_fixup(struct mii_softc *sc)
299 {
300         uint16_t                model;
301         uint16_t                status, speed;
302         uint16_t                val;
303
304         model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2));
305         status = PHY_READ(sc, CIPHY_MII_AUXCSR);
306         speed = status & CIPHY_AUXCSR_SPEED;
307
308         if (mii_phy_mac_match(sc, "nfe")) {
309                 /* need to set for 2.5V RGMII for NVIDIA adapters */
310                 val = PHY_READ(sc, CIPHY_MII_ECTL1);
311                 val &= ~(CIPHY_ECTL1_IOVOL | CIPHY_ECTL1_INTSEL);
312                 val |= (CIPHY_IOVOL_2500MV | CIPHY_INTSEL_RGMII);
313                 PHY_WRITE(sc, CIPHY_MII_ECTL1, val);
314                 /* From Linux. */
315                 val = PHY_READ(sc, CIPHY_MII_AUXCSR);
316                 val |= CIPHY_AUXCSR_MDPPS;
317                 PHY_WRITE(sc, CIPHY_MII_AUXCSR, val);
318                 val = PHY_READ(sc, CIPHY_MII_10BTCSR);
319                 val |= CIPHY_10BTCSR_ECHO;
320                 PHY_WRITE(sc, CIPHY_MII_10BTCSR, val);
321         }
322
323         switch (model) {
324         case MII_MODEL_xxCICADA_CS8204:
325         case MII_MODEL_xxCICADA_CS8201:
326
327                 /* Turn off "aux mode" (whatever that means) */
328                 PHY_SETBIT(sc, CIPHY_MII_AUXCSR, CIPHY_AUXCSR_MDPPS);
329
330                 /*
331                  * Work around speed polling bug in VT3119/VT3216
332                  * when using MII in full duplex mode.
333                  */
334                 if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
335                     (status & CIPHY_AUXCSR_FDX)) {
336                         PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
337                 } else {
338                         PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
339                 }
340
341                 /* Enable link/activity LED blink. */
342                 PHY_SETBIT(sc, CIPHY_MII_LED, CIPHY_LED_LINKACTBLINK);
343
344                 break;
345
346         case MII_MODEL_xxCICADA_CS8201A:
347         case MII_MODEL_xxCICADA_CS8201B:
348
349                 /*
350                  * Work around speed polling bug in VT3119/VT3216
351                  * when using MII in full duplex mode.
352                  */
353                 if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
354                     (status & CIPHY_AUXCSR_FDX)) {
355                         PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
356                 } else {
357                         PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
358                 }
359
360                 break;
361         case MII_MODEL_xxCICADA_VSC8211:
362         case MII_MODEL_xxCICADA_VSC8221:
363         case MII_MODEL_xxCICADA_CS8244:
364         case MII_MODEL_xxVITESSE_VSC8601:
365         case MII_MODEL_xxVITESSE_VSC8641:
366                 break;
367         default:
368                 device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n",
369                     model);
370                 break;
371         }
372 }