FreeBSD/Linux Kernel Cross Reference
sys/dev/mii/rlphy.c
1 /*
2 * Copyright (c) 1997, 1998, 1999
3 * Bill Paul <wpaul@ctr.columbia.edu>. 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, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
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
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * $FreeBSD: releng/5.0/sys/dev/mii/rlphy.c 105135 2002-10-14 22:31:52Z alfred $
33 */
34
35 /*
36 * driver for RealTek 8139 internal PHYs
37 */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/socket.h>
43 #include <sys/bus.h>
44
45 #include <net/if.h>
46 #include <net/if_arp.h>
47 #include <net/if_media.h>
48
49 #include <dev/mii/mii.h>
50 #include <dev/mii/miivar.h>
51 #include <dev/mii/miidevs.h>
52
53 #include <machine/bus.h>
54 #include <pci/if_rlreg.h>
55
56 #include "miibus_if.h"
57
58 #if !defined(lint)
59 static const char rcsid[] =
60 "$FreeBSD: releng/5.0/sys/dev/mii/rlphy.c 105135 2002-10-14 22:31:52Z alfred $";
61 #endif
62
63 static int rlphy_probe(device_t);
64 static int rlphy_attach(device_t);
65
66 static device_method_t rlphy_methods[] = {
67 /* device interface */
68 DEVMETHOD(device_probe, rlphy_probe),
69 DEVMETHOD(device_attach, rlphy_attach),
70 DEVMETHOD(device_detach, mii_phy_detach),
71 DEVMETHOD(device_shutdown, bus_generic_shutdown),
72 { 0, 0 }
73 };
74
75 static devclass_t rlphy_devclass;
76
77 static driver_t rlphy_driver = {
78 "rlphy",
79 rlphy_methods,
80 sizeof(struct mii_softc)
81 };
82
83 DRIVER_MODULE(rlphy, miibus, rlphy_driver, rlphy_devclass, 0, 0);
84
85 static int rlphy_service(struct mii_softc *, struct mii_data *, int);
86 static void rlphy_status(struct mii_softc *);
87
88 static int
89 rlphy_probe(dev)
90 device_t dev;
91 {
92 struct mii_attach_args *ma;
93 device_t parent;
94
95 ma = device_get_ivars(dev);
96 parent = device_get_parent(device_get_parent(dev));
97
98 /* Test for RealTek 8201L PHY */
99 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_REALTEK &&
100 MII_MODEL(ma->mii_id2) == MII_MODEL_REALTEK_RTL8201L) {
101 device_set_desc(dev, MII_STR_REALTEK_RTL8201L);
102 return(0);
103 }
104
105 /*
106 * RealTek PHY doesn't have vendor/device ID registers:
107 * the rl driver fakes up a return value of all zeros.
108 */
109 if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
110 MII_MODEL(ma->mii_id2) != 0)
111 return (ENXIO);
112
113 /*
114 * Make sure the parent is an `rl'.
115 */
116 if (strcmp(device_get_name(parent), "rl") != 0)
117 return (ENXIO);
118
119 device_set_desc(dev, "RealTek internal media interface");
120
121 return (0);
122 }
123
124 static int
125 rlphy_attach(dev)
126 device_t dev;
127 {
128 struct mii_softc *sc;
129 struct mii_attach_args *ma;
130 struct mii_data *mii;
131
132 sc = device_get_softc(dev);
133 ma = device_get_ivars(dev);
134 sc->mii_dev = device_get_parent(dev);
135 mii = device_get_softc(sc->mii_dev);
136
137 /*
138 * The RealTek PHY can never be isolated, so never allow non-zero
139 * instances!
140 */
141 if (mii->mii_instance != 0) {
142 device_printf(dev, "ignoring this PHY, non-zero instance\n");
143 return(ENXIO);
144 }
145
146 LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
147
148 sc->mii_inst = mii->mii_instance;
149 sc->mii_phy = ma->mii_phyno;
150 sc->mii_service = rlphy_service;
151 sc->mii_pdata = mii;
152 mii->mii_instance++;
153
154 sc->mii_flags |= MIIF_NOISOLATE;
155
156 #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
157
158 #if 0 /* See above. */
159 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
160 BMCR_ISO);
161 #endif
162
163 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
164 BMCR_LOOP|BMCR_S100);
165
166 mii_phy_reset(sc);
167
168 sc->mii_capabilities =
169 PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
170 device_printf(dev, " ");
171 mii_add_media(sc);
172 printf("\n");
173 #undef ADD
174 MIIBUS_MEDIAINIT(sc->mii_dev);
175 return(0);
176 }
177
178 static int
179 rlphy_service(sc, mii, cmd)
180 struct mii_softc *sc;
181 struct mii_data *mii;
182 int cmd;
183 {
184 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
185
186 /*
187 * We can't isolate the RealTek PHY, so it has to be the only one!
188 */
189 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
190 panic("rlphy_service: can't isolate RealTek PHY");
191
192 switch (cmd) {
193 case MII_POLLSTAT:
194 break;
195
196 case MII_MEDIACHG:
197 /*
198 * If the interface is not up, don't do anything.
199 */
200 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
201 break;
202
203 switch (IFM_SUBTYPE(ife->ifm_media)) {
204 case IFM_AUTO:
205 /*
206 * If we're already in auto mode, just return.
207 */
208 if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
209 return (0);
210 (void) mii_phy_auto(sc);
211 break;
212 case IFM_100_T4:
213 /*
214 * XXX Not supported as a manual setting right now.
215 */
216 return (EINVAL);
217 default:
218 /*
219 * BMCR data is stored in the ifmedia entry.
220 */
221 PHY_WRITE(sc, MII_ANAR,
222 mii_anar(ife->ifm_media));
223 PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
224 }
225 break;
226
227 case MII_TICK:
228 /*
229 * Is the interface even up?
230 */
231 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
232 return (0);
233
234 /*
235 * Only used for autonegotiation.
236 */
237 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
238 break;
239
240 /*
241 * The RealTek PHY's autonegotiation doesn't need to be
242 * kicked; it continues in the background.
243 */
244 break;
245 }
246
247 /* Update the media status. */
248 rlphy_status(sc);
249
250 /* Callback if something changed. */
251 mii_phy_update(sc, cmd);
252 return (0);
253 }
254
255 static void
256 rlphy_status(phy)
257 struct mii_softc *phy;
258 {
259 struct mii_data *mii = phy->mii_pdata;
260 int bmsr, bmcr, anlpar;
261 device_t parent;
262
263 mii->mii_media_status = IFM_AVALID;
264 mii->mii_media_active = IFM_ETHER;
265
266 bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
267 if (bmsr & BMSR_LINK)
268 mii->mii_media_status |= IFM_ACTIVE;
269
270 bmcr = PHY_READ(phy, MII_BMCR);
271 if (bmcr & BMCR_ISO) {
272 mii->mii_media_active |= IFM_NONE;
273 mii->mii_media_status = 0;
274 return;
275 }
276
277 if (bmcr & BMCR_LOOP)
278 mii->mii_media_active |= IFM_LOOP;
279
280 if (bmcr & BMCR_AUTOEN) {
281 /*
282 * NWay autonegotiation takes the highest-order common
283 * bit of the ANAR and ANLPAR (i.e. best media advertised
284 * both by us and our link partner).
285 */
286 if ((bmsr & BMSR_ACOMP) == 0) {
287 /* Erg, still trying, I guess... */
288 mii->mii_media_active |= IFM_NONE;
289 return;
290 }
291
292 if ((anlpar = PHY_READ(phy, MII_ANAR) &
293 PHY_READ(phy, MII_ANLPAR))) {
294 if (anlpar & ANLPAR_T4)
295 mii->mii_media_active |= IFM_100_T4;
296 else if (anlpar & ANLPAR_TX_FD)
297 mii->mii_media_active |= IFM_100_TX|IFM_FDX;
298 else if (anlpar & ANLPAR_TX)
299 mii->mii_media_active |= IFM_100_TX;
300 else if (anlpar & ANLPAR_10_FD)
301 mii->mii_media_active |= IFM_10_T|IFM_FDX;
302 else if (anlpar & ANLPAR_10)
303 mii->mii_media_active |= IFM_10_T;
304 else
305 mii->mii_media_active |= IFM_NONE;
306 return;
307 }
308 /*
309 * If the other side doesn't support NWAY, then the
310 * best we can do is determine if we have a 10Mbps or
311 * 100Mbps link. There's no way to know if the link
312 * is full or half duplex, so we default to half duplex
313 * and hope that the user is clever enough to manually
314 * change the media settings if we're wrong.
315 */
316
317
318 /*
319 * The RealTek PHY supports non-NWAY link speed
320 * detection, however it does not report the link
321 * detection results via the ANLPAR or BMSR registers.
322 * (What? RealTek doesn't do things the way everyone
323 * else does? I'm just shocked, shocked I tell you.)
324 * To determine the link speed, we have to do one
325 * of two things:
326 *
327 * - If this is a standalone RealTek RTL8201(L) PHY,
328 * we can determine the link speed by testing bit 0
329 * in the magic, vendor-specific register at offset
330 * 0x19.
331 *
332 * - If this is a RealTek MAC with integrated PHY, we
333 * can test the 'SPEED10' bit of the MAC's media status
334 * register.
335 */
336 parent = device_get_parent(phy->mii_dev);
337 if (strcmp(device_get_name(parent), "rl") != 0) {
338 if (PHY_READ(phy, 0x0019) & 0x01)
339 mii->mii_media_active |= IFM_100_TX;
340 else
341 mii->mii_media_active |= IFM_10_T;
342 } else {
343 if (PHY_READ(phy, RL_MEDIASTAT) &
344 RL_MEDIASTAT_SPEED10)
345 mii->mii_media_active |= IFM_10_T;
346 else
347 mii->mii_media_active |= IFM_100_TX;
348 }
349
350 } else
351 mii->mii_media_active = mii_media_from_bmcr(bmcr);
352 }
Cache object: 60928cf49a15119e2f03f0f172707991
|