mii.c revision 1.2 1 /* $NetBSD: mii.c,v 1.2 1997/11/17 08:52:38 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1997 Manuel Bouyer. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following 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 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Manuel Bouyer.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/malloc.h>
37 #include <sys/socket.h>
38 #include <sys/sockio.h>
39
40 #include <net/if.h>
41 #if defined(SIOCSIFMEDIA)
42 #include <net/if_media.h>
43 #endif
44
45 #include <dev/mii/mii_adapter.h>
46 #include <dev/mii/mii_phy.h>
47 #include <dev/mii/generic_phy.h>
48
49 #include "locators.h"
50
51 /* The mii bus private data definitions */
52
53 struct mii_softc {
54 struct device sc_dev;
55 mii_data_t *adapter;
56 mii_phy_t *phy[32];
57 mii_phy_t *current_phy;
58 };
59
60 static void mii_sync __P((mii_data_t *));
61 static void mii_sendbit __P((mii_data_t *, u_int32_t, int));
62
63 #ifdef __BROKEN_INDIRECT_CONFIG
64 int miimatch __P((struct device *, void *, void *));
65 int mii_configmatch __P((struct device *, void *, void *));
66 #else
67 int miimatch __P((struct device *, struct cfdata *, void *));
68 int mii_configmatch __P((struct device *, struct cfdata *, void *));
69 #endif
70 void miiattach __P((struct device *, struct device *, void *));
71
72 int mii_print __P((void *, const char *));
73
74 struct cfattach mii_ca = {
75 sizeof(struct mii_softc), miimatch, miiattach
76 };
77
78 struct cfdriver mii_cd = {
79 NULL, "mii", DV_DULL
80 };
81
82 int
83 mii_print(aux, pnp)
84 void *aux;
85 const char *pnp;
86 {
87 mii_phy_t *phy = aux;
88
89 if (pnp)
90 printf("PHY ID 0x%x at %s", phy->phy_id, pnp);
91 printf(" dev %d", phy->dev);
92 return (UNCONF);
93 }
94
95 #ifdef __BROKEN_INDIRECT_CONFIG
96 int
97 miimatch(parent, match, aux)
98 struct device *parent;
99 void *match, *aux;
100 {
101 #else
102 int
103 miimatch(parent, cf, aux)
104 struct device *parent;
105 struct cfdata *cf;
106 void *aux;
107 {
108 #endif
109 return 1;
110 }
111
112 void
113 miiattach(parent, self, aux)
114 struct device *parent, *self;
115 void *aux;
116 {
117 int phy_id_l, phy_id_h;
118 int i;
119 mii_phy_t *phy;
120 struct mii_softc *sc = (struct mii_softc *)self;
121 mii_data_t *adapter = aux;
122 /* struct cfdata *cf; */
123
124 printf("\n");
125 sc->adapter = adapter;
126 sc->adapter->mii_sc = sc;
127 sc->current_phy = NULL;
128
129 for (i = 0; i < 32; i++) {
130 phy_id_h = mii_readreg(sc, i, PHY_IDH);
131 phy_id_l = mii_readreg(sc, i, PHY_IDL);
132 #ifdef MII_DEBUG
133 printf("Id of PHY 0x%x: 0x%x%x\n", i, phy_id_h, phy_id_l);
134 #endif
135 if (phy_id_h != -1 && phy_id_l != -1) {
136 phy = malloc(sizeof(mii_phy_t), M_DEVBUF, M_WAITOK);
137 phy->phy_id = ((phy_id_h & 0xffff) << 16) |
138 (phy_id_l & 0xffff);
139 phy->adapter_id = adapter->adapter_id;
140 phy->dev = i;
141 phy->mii_softc = sc;
142 #if 0
143 if ((cf = config_search(mii_configmatch, self,
144 phy)) != NULL) {
145 sc->phy[i] = phy;
146 config_attach(self, cf, phy, mii_print);
147 } else {
148 sc->phy[i] = NULL;
149 mii_print(phy, sc->sc_dev.dv_xname);
150 printf(" not configured\n");
151 free(phy, M_DEVBUF);
152 }
153 #else
154 if (config_found_sm(self, phy, mii_print,
155 mii_configmatch) != NULL) {
156 sc->phy[i] = phy;
157 } else {
158 sc->phy[i] = NULL;
159 free(phy, M_DEVBUF);
160 }
161 #endif
162 }
163 }
164 }
165
166 #ifdef __BROKEN_INDIRECT_CONFIG
167 int
168 mii_configmatch(parent, match, aux)
169 struct device *parent;
170 void *match, *aux;
171 {
172 struct cfdata *cf =match;
173 #else
174 int
175 mii_configmatch(parent, cf, aux)
176 struct device *parent;
177 struct cfdata *cf;
178 void *aux;
179 {
180 #endif
181 mii_phy_t *phy = aux;
182
183 if (cf->cf_loc[MIICF_DEV] != MIICF_DEV_DEFAULT &&
184 cf->cf_loc[MIICF_DEV] != phy->dev)
185 return (0);
186 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
187 }
188
189 static void
190 mii_sync(adapter)
191 mii_data_t* adapter;
192 {
193 int i;
194
195 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
196 for (i = 0; i < 32; i++) {
197 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
198 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
199 }
200 }
201
202 static void
203 mii_sendbit(adapter, data, nbits)
204 mii_data_t *adapter;
205 u_int32_t data;
206 int nbits;
207 {
208 int i;
209
210 (*adapter->mii_setbit)(adapter->adapter_softc, MII_TXEN);
211 for (i = 1 << (nbits -1); i; i = i >> 1) {
212 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
213 (*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
214 if (data & i)
215 (*adapter->mii_setbit)(adapter->adapter_softc,
216 MII_DATA);
217 else
218 (*adapter->mii_clrbit)(adapter->adapter_softc,
219 MII_DATA);
220 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
221 (*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
222 }
223 }
224
225 int
226 mii_readreg(v, phy, reg)
227 void *v;
228 u_int16_t phy;
229 u_int16_t reg;
230 {
231 mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
232 u_int16_t val = 0;
233 int err =0;
234 int i;
235
236 if (adapter->mii_readreg) /* adapter has a special way to read PHYs */
237 return ((*adapter->mii_readreg)(adapter->adapter_softc,
238 phy, reg));
239
240 /* else read using the control lines */
241 mii_sync(adapter);
242 mii_sendbit(adapter, MII_START, 2);
243 mii_sendbit(adapter, MII_READ, 2);
244 mii_sendbit(adapter, phy, 5);
245 mii_sendbit(adapter, reg, 5);
246
247 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
248 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
249 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
250 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
251
252 err = (*adapter->mii_readbit)(adapter->adapter_softc, MII_DATA);
253 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
254
255 for (i = 0; i < 16; i++) {
256 val = val << 1;
257 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
258 if (err == 0)
259 if ((*adapter->mii_readbit)(adapter->adapter_softc,
260 MII_DATA))
261 val |= 1;
262 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
263 }
264 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
265 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
266
267 if (err == 0)
268 return val;
269 else
270 return -1;
271 }
272
273 void
274 mii_writereg(v, phy, reg, data)
275 void *v;
276 u_int16_t phy;
277 u_int16_t reg;
278 u_int16_t data;
279 {
280 mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
281
282 if (adapter->mii_writereg) {
283 /* Interface has a special way of writing to the PHY. */
284 (*adapter->mii_writereg)(adapter, phy, reg, data);
285 return;
286 }
287
288 /* else write using the control lines */
289 mii_sync(adapter);
290 mii_sendbit(adapter, MII_START, 2);
291 mii_sendbit(adapter, MII_WRITE, 2);
292 mii_sendbit(adapter, phy, 5);
293 mii_sendbit(adapter, reg, 5);
294 mii_sendbit(adapter, MII_ACK, 2);
295 mii_sendbit(adapter, data, 16);
296
297 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
298 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
299 }
300
301 void
302 mii_media_add(ifmedia, adapter)
303 struct ifmedia *ifmedia;
304 mii_data_t *adapter;
305 {
306 struct mii_softc *sc = adapter->mii_sc;
307 int i;
308 u_int32_t media = 0;
309
310 for (i = 0; i < 32; i++) {
311 if (sc->phy[i])
312 media |= sc->phy[i]->phy_media;
313 }
314 if (media & PHY_BNC)
315 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_2, 0, NULL);
316 if (media & PHY_AUI)
317 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_5, 0, NULL);
318 if (media & PHY_10baseT)
319 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
320 if (media & PHY_10baseTfd)
321 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
322 if (media & PHY_100baseTx)
323 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
324 if (media & PHY_100baseTxfd)
325 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
326 if (media & PHY_100baseT4)
327 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_T4, 0, NULL);
328 ifmedia_add(ifmedia, IFM_ETHER | IFM_NONE, 0, NULL);
329 }
330
331 int
332 mii_mediachg(adapter)
333 mii_data_t *adapter;
334 {
335 struct mii_softc *sc = adapter->mii_sc;
336 int i, best = -1, error = 0;
337 int media = adapter->mii_media_active;
338
339 sc->current_phy = NULL;
340
341 for (i = 0; i < 32; i++) {
342 if (sc->phy[i] == NULL)
343 continue;
344 switch (sc->phy[i]->phy_media_set(media,
345 sc->phy[i]->phy_softc)) {
346 case -1: /* PHY not available */
347 break;
348 case 0: /* link sucessfully selected */
349 sc->current_phy = sc->phy[i];
350 break;
351 case ENETDOWN: /* link selected but not up */
352 best = i;
353 break;
354 default:
355 break;
356 }
357 }
358 if (sc->current_phy == NULL) {
359 /*
360 * We didn't find a valid media. Select the best one (i.e.
361 * last supported but not up). If media != autoselect,
362 * don't report any error code.
363 */
364 if (best < 0)
365 return (EINVAL);
366 sc->current_phy = sc->phy[best];
367 error = sc->phy[best]->phy_media_set(media,
368 sc->phy[best]->phy_softc);
369 if (media != IFM_AUTO)
370 error = 0;
371 }
372 /* power down all but current phy */
373 for (i = 0; i < 32; i++) {
374 if (sc->phy[i] != sc->current_phy) {
375 if (sc->phy[i] == NULL)
376 mii_writereg(sc, i, PHY_CONTROL, CTRL_ISO);
377 else
378 sc->phy[i]->phy_pdown(sc->phy[i]->phy_softc);
379 }
380 }
381 return (error);
382 }
383
384 void
385 mii_pollstat(adapter)
386 mii_data_t *adapter;
387 {
388 struct mii_softc *sc = adapter->mii_sc;
389
390 adapter->mii_media_status = IFM_AVALID;
391 if (sc->current_phy == NULL)
392 return;
393 if ((*sc->current_phy->phy_status)(adapter->mii_media_active,
394 sc->current_phy->phy_softc) == 0)
395 adapter->mii_media_status |= IFM_ACTIVE;
396 }
397