1 /* $OpenBSD: tlphy.c,v 1.22 2022/04/06 18:59:29 naddy Exp $ */
2 /* $NetBSD: tlphy.c,v 1.26 2000/07/04 03:29:00 thorpej Exp $ */
3
4 /*-
5 * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10 * NASA Ames Research Center.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * Copyright (c) 1997 Manuel Bouyer. All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
50 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
51 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
52 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
55 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57
58 /*
59 * Driver for Texas Instruments's ThunderLAN PHYs
60 */
61
62 #include <sys/param.h>
63 #include <sys/systm.h>
64 #include <sys/kernel.h>
65 #include <sys/device.h>
66 #include <sys/socket.h>
67 #include <sys/errno.h>
68
69 #include <machine/bus.h>
70
71 #include <net/if.h>
72 #include <net/if_media.h>
73
74 #include <netinet/in.h>
75 #include <netinet/if_ether.h>
76
77 #include <dev/mii/mii.h>
78 #include <dev/mii/miivar.h>
79 #include <dev/mii/miidevs.h>
80
81 #include <dev/mii/tlphyreg.h>
82 #include <dev/mii/tlphyvar.h>
83
84 /* ThunderLAN PHY can only be on a ThunderLAN */
85 #include <dev/pci/if_tlreg.h>
86
87 struct tlphy_softc {
88 struct mii_softc sc_mii; /* generic PHY */
89 int sc_tlphycap;
90 int sc_need_acomp;
91 };
92
93 struct cfdriver tlphy_cd = {
94 NULL, "tlphy", DV_DULL
95 };
96
97 int tlphymatch(struct device *, void *, void *);
98 void tlphyattach(struct device *, struct device *, void *);
99
100 const struct cfattach tlphy_ca = {
101 sizeof(struct tlphy_softc), tlphymatch, tlphyattach, mii_phy_detach
102 };
103
104 int tlphy_service(struct mii_softc *, struct mii_data *, int);
105 int tlphy_mii_phy_auto(struct tlphy_softc *, int);
106 void tlphy_acomp(struct tlphy_softc *);
107 void tlphy_status(struct mii_softc *);
108
109 const struct mii_phy_funcs tlphy_funcs = {
110 tlphy_service, tlphy_status, mii_phy_reset,
111 };
112
113 static const struct mii_phydesc tlphys[] = {
114 { MII_OUI_xxTI, MII_MODEL_xxTI_TLAN10T,
115 MII_STR_xxTI_TLAN10T },
116
117 { 0, 0,
118 NULL },
119 };
120
121 int
tlphymatch(struct device * parent,void * match,void * aux)122 tlphymatch(struct device *parent, void *match, void *aux)
123 {
124 struct mii_attach_args *ma = aux;
125
126 if (mii_phy_match(ma, tlphys) != NULL)
127 return (10);
128
129 return (0);
130 }
131
132 void
tlphyattach(struct device * parent,struct device * self,void * aux)133 tlphyattach(struct device *parent, struct device *self, void *aux)
134 {
135 struct tlphy_softc *sc = (struct tlphy_softc *)self;
136 struct tl_softc *tlsc = (struct tl_softc *)self->dv_parent;
137 struct mii_attach_args *ma = aux;
138 struct mii_data *mii = ma->mii_data;
139 const struct mii_phydesc *mpd;
140
141 mpd = mii_phy_match(ma, tlphys);
142 printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
143
144 sc->sc_mii.mii_inst = mii->mii_instance;
145 sc->sc_mii.mii_phy = ma->mii_phyno;
146 sc->sc_mii.mii_funcs = &tlphy_funcs;
147 sc->sc_mii.mii_pdata = mii;
148 sc->sc_mii.mii_flags = ma->mii_flags;
149
150 sc->sc_mii.mii_flags &= ~MIIF_NOISOLATE;
151 PHY_RESET(&sc->sc_mii);
152 sc->sc_mii.mii_flags |= MIIF_NOISOLATE;
153
154 /*
155 * Note that if we're on a device that also supports 100baseTX,
156 * we are not going to want to use the built-in 10baseT port,
157 * since there will be another PHY on the MII wired up to the
158 * UTP connector. The parent indicates this to us by specifying
159 * the TLPHY_MEDIA_NO_10_T bit.
160 */
161 sc->sc_tlphycap = tlsc->tl_product->tp_tlphymedia;
162 if ((sc->sc_tlphycap & TLPHY_MEDIA_NO_10_T) == 0)
163 sc->sc_mii.mii_capabilities =
164 PHY_READ(&sc->sc_mii, MII_BMSR) & ma->mii_capmask;
165 else
166 sc->sc_mii.mii_capabilities = 0;
167
168
169 if (sc->sc_tlphycap & TLPHY_MEDIA_10_2)
170 ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER,
171 IFM_10_2, 0, sc->sc_mii.mii_inst), 0, NULL);
172 if (sc->sc_tlphycap & TLPHY_MEDIA_10_5)
173 ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER,
174 IFM_10_5, 0, sc->sc_mii.mii_inst), 0, NULL);
175 if (sc->sc_mii.mii_capabilities & BMSR_MEDIAMASK)
176 mii_phy_add_media(&sc->sc_mii);
177 }
178
179 int
tlphy_service(struct mii_softc * self,struct mii_data * mii,int cmd)180 tlphy_service(struct mii_softc *self, struct mii_data *mii, int cmd)
181 {
182 struct tlphy_softc *sc = (struct tlphy_softc *)self;
183 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
184 int reg;
185
186 if ((sc->sc_mii.mii_dev.dv_flags & DVF_ACTIVE) == 0)
187 return (ENXIO);
188
189 if ((sc->sc_mii.mii_flags & MIIF_DOINGAUTO) == 0 && sc->sc_need_acomp)
190 tlphy_acomp(sc);
191
192 switch (cmd) {
193 case MII_POLLSTAT:
194 /*
195 * If we're not polling our PHY instance, just return.
196 */
197 if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
198 return (0);
199 break;
200
201 case MII_MEDIACHG:
202 /*
203 * If the media indicates a different PHY instance,
204 * isolate ourselves.
205 */
206 if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst) {
207 reg = PHY_READ(&sc->sc_mii, MII_BMCR);
208 PHY_WRITE(&sc->sc_mii, MII_BMCR, reg | BMCR_ISO);
209 return (0);
210 }
211
212 /*
213 * If the interface is not up, don't do anything.
214 */
215 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
216 break;
217
218 switch (IFM_SUBTYPE(ife->ifm_media)) {
219 case IFM_AUTO:
220 /*
221 * The ThunderLAN PHY doesn't self-configure after
222 * an autonegotiation cycle, so there's no such
223 * thing as "already in auto mode".
224 */
225 (void) tlphy_mii_phy_auto(sc, 1);
226 break;
227 case IFM_10_2:
228 case IFM_10_5:
229 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
230 PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, CTRL_AUISEL);
231 delay(100000);
232 break;
233 default:
234 PHY_WRITE(&sc->sc_mii, MII_TLPHY_CTRL, 0);
235 delay(100000);
236 mii_phy_setmedia(&sc->sc_mii);
237 }
238 break;
239
240 case MII_TICK:
241 /*
242 * XXX WHAT ABOUT CHECKING LINK ON THE BNC/AUI?!
243 */
244
245 /*
246 * If we're not currently selected, just return.
247 */
248 if (IFM_INST(ife->ifm_media) != sc->sc_mii.mii_inst)
249 return (0);
250
251 if (mii_phy_tick(&sc->sc_mii) == EJUSTRETURN)
252 return (0);
253 break;
254
255 case MII_DOWN:
256 mii_phy_down(&sc->sc_mii);
257 return (0);
258 }
259
260 /* Update the media status. */
261 mii_phy_status(&sc->sc_mii);
262
263 /* Callback if something changed. */
264 mii_phy_update(&sc->sc_mii, cmd);
265 return (0);
266 }
267
268 void
tlphy_status(struct mii_softc * physc)269 tlphy_status(struct mii_softc *physc)
270 {
271 struct tlphy_softc *sc = (void *) physc;
272 struct mii_data *mii = sc->sc_mii.mii_pdata;
273 int bmsr, bmcr, tlctrl;
274
275 mii->mii_media_status = IFM_AVALID;
276 mii->mii_media_active = IFM_ETHER;
277
278 bmcr = PHY_READ(&sc->sc_mii, MII_BMCR);
279 if (bmcr & BMCR_ISO) {
280 mii->mii_media_active |= IFM_NONE;
281 mii->mii_media_status = 0;
282 return;
283 }
284
285 tlctrl = PHY_READ(&sc->sc_mii, MII_TLPHY_CTRL);
286 if (tlctrl & CTRL_AUISEL) {
287 mii->mii_media_status = 0;
288 mii->mii_media_active = mii->mii_media.ifm_cur->ifm_media;
289 return;
290 }
291
292 bmsr = PHY_READ(&sc->sc_mii, MII_BMSR) |
293 PHY_READ(&sc->sc_mii, MII_BMSR);
294 if (bmsr & BMSR_LINK)
295 mii->mii_media_status |= IFM_ACTIVE;
296
297 if (bmcr & BMCR_LOOP)
298 mii->mii_media_active |= IFM_LOOP;
299
300 /*
301 * Grr, braindead ThunderLAN PHY doesn't have any way to
302 * tell which media is actually active. (Note it also
303 * doesn't self-configure after autonegotiation.) We
304 * just have to report what's in the BMCR.
305 */
306 if (bmcr & BMCR_FDX)
307 mii->mii_media_active |= IFM_FDX;
308 else
309 mii->mii_media_active |= IFM_HDX;
310 mii->mii_media_active |= IFM_10_T;
311 }
312
313 int
tlphy_mii_phy_auto(struct tlphy_softc * sc,int waitfor)314 tlphy_mii_phy_auto(struct tlphy_softc *sc, int waitfor)
315 {
316 int error;
317
318 switch ((error = mii_phy_auto(&sc->sc_mii, waitfor))) {
319 case EIO:
320 /*
321 * Just assume we're not in full-duplex mode.
322 * XXX Check link and try AUI/BNC?
323 */
324 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
325 break;
326
327 case EJUSTRETURN:
328 /* Flag that we need to program when it completes. */
329 sc->sc_need_acomp = 1;
330 break;
331
332 default:
333 tlphy_acomp(sc);
334 }
335
336 return (error);
337 }
338
339 void
tlphy_acomp(struct tlphy_softc * sc)340 tlphy_acomp(struct tlphy_softc *sc)
341 {
342 int aner, anlpar;
343
344 sc->sc_need_acomp = 0;
345
346 /*
347 * Grr, braindead ThunderLAN PHY doesn't self-configure
348 * after autonegotiation. We have to do it ourselves
349 * based on the link partner status.
350 */
351
352 aner = PHY_READ(&sc->sc_mii, MII_ANER);
353 if (aner & ANER_LPAN) {
354 anlpar = PHY_READ(&sc->sc_mii, MII_ANLPAR) &
355 PHY_READ(&sc->sc_mii, MII_ANAR);
356 if (anlpar & ANAR_10_FD) {
357 PHY_WRITE(&sc->sc_mii, MII_BMCR, BMCR_FDX);
358 return;
359 }
360 }
361 PHY_WRITE(&sc->sc_mii, MII_BMCR, 0);
362 }
363