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