mii.c revision 1.4 1 /* $NetBSD: mii.c,v 1.4 1998/02/11 19:02:11 bouyer 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 int mii_adapter_print(aux, pnp)
79 void *aux;
80 const char *pnp;
81 {
82 if (pnp)
83 printf("mii at %s", pnp);
84 return UNCONF;
85 }
86
87 int
88 mii_print(aux, pnp)
89 void *aux;
90 const char *pnp;
91 {
92 mii_phy_t *phy = aux;
93
94 if (pnp)
95 printf("PHY ID 0x%x at %s", phy->phy_id, pnp);
96 printf(" dev %d", phy->dev);
97 return (UNCONF);
98 }
99
100 #ifdef __BROKEN_INDIRECT_CONFIG
101 int
102 miimatch(parent, match, aux)
103 struct device *parent;
104 void *match, *aux;
105 {
106 #else
107 int
108 miimatch(parent, cf, aux)
109 struct device *parent;
110 struct cfdata *cf;
111 void *aux;
112 {
113 #endif
114 return 1;
115 }
116
117 void
118 miiattach(parent, self, aux)
119 struct device *parent, *self;
120 void *aux;
121 {
122 int phy_id_l, phy_id_h;
123 int i;
124 mii_phy_t *phy;
125 struct mii_softc *sc = (struct mii_softc *)self;
126 mii_data_t *adapter = aux;
127 /* struct cfdata *cf; */
128
129 printf("\n");
130 sc->adapter = adapter;
131 sc->adapter->mii_sc = sc;
132 sc->current_phy = NULL;
133
134 for (i = 0; i < 32; i++) {
135 phy_id_h = mii_readreg(sc, i, PHY_IDH);
136 phy_id_l = mii_readreg(sc, i, PHY_IDL);
137 #ifdef MII_DEBUG
138 printf("Id of PHY 0x%x: 0x%x%x\n", i, phy_id_h, phy_id_l);
139 #endif
140 if (phy_id_h != -1 && phy_id_l != -1) {
141 phy = malloc(sizeof(mii_phy_t), M_DEVBUF, M_WAITOK);
142 phy->phy_id = ((phy_id_h & 0xffff) << 16) |
143 (phy_id_l & 0xffff);
144 phy->adapter_id = adapter->adapter_id;
145 phy->dev = i;
146 phy->mii_softc = sc;
147 #if 0
148 if ((cf = config_search(mii_configmatch, self,
149 phy)) != NULL) {
150 sc->phy[i] = phy;
151 config_attach(self, cf, phy, mii_print);
152 } else {
153 sc->phy[i] = NULL;
154 mii_print(phy, sc->sc_dev.dv_xname);
155 printf(" not configured\n");
156 free(phy, M_DEVBUF);
157 }
158 #else
159 if (config_found_sm(self, phy, mii_print,
160 mii_configmatch) != NULL) {
161 sc->phy[i] = phy;
162 } else {
163 sc->phy[i] = NULL;
164 free(phy, M_DEVBUF);
165 }
166 #endif
167 }
168 }
169 }
170
171 #ifdef __BROKEN_INDIRECT_CONFIG
172 int
173 mii_configmatch(parent, match, aux)
174 struct device *parent;
175 void *match, *aux;
176 {
177 struct cfdata *cf =match;
178 #else
179 int
180 mii_configmatch(parent, cf, aux)
181 struct device *parent;
182 struct cfdata *cf;
183 void *aux;
184 {
185 #endif
186 mii_phy_t *phy = aux;
187
188 if (cf->cf_loc[MIICF_DEV] != MIICF_DEV_DEFAULT &&
189 cf->cf_loc[MIICF_DEV] != phy->dev)
190 return (0);
191 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
192 }
193
194 static void
195 mii_sync(adapter)
196 mii_data_t* adapter;
197 {
198 int i;
199
200 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
201 for (i = 0; i < 32; i++) {
202 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
203 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
204 }
205 }
206
207 static void
208 mii_sendbit(adapter, data, nbits)
209 mii_data_t *adapter;
210 u_int32_t data;
211 int nbits;
212 {
213 int i;
214
215 (*adapter->mii_setbit)(adapter->adapter_softc, MII_TXEN);
216 for (i = 1 << (nbits -1); i; i = i >> 1) {
217 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
218 (*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
219 if (data & i)
220 (*adapter->mii_setbit)(adapter->adapter_softc,
221 MII_DATA);
222 else
223 (*adapter->mii_clrbit)(adapter->adapter_softc,
224 MII_DATA);
225 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
226 (*adapter->mii_readbit)(adapter->adapter_softc, MII_CLOCK);
227 }
228 }
229
230 int
231 mii_readreg(v, phy, reg)
232 void *v;
233 u_int16_t phy;
234 u_int16_t reg;
235 {
236 mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
237 u_int16_t val = 0;
238 int err =0;
239 int i;
240
241 if (adapter->mii_readreg) /* adapter has a special way to read PHYs */
242 return ((*adapter->mii_readreg)(adapter->adapter_softc,
243 phy, reg));
244
245 /* else read using the control lines */
246 mii_sync(adapter);
247 mii_sendbit(adapter, MII_START, 2);
248 mii_sendbit(adapter, MII_READ, 2);
249 mii_sendbit(adapter, phy, 5);
250 mii_sendbit(adapter, reg, 5);
251
252 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_TXEN);
253 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
254 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
255 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
256
257 err = (*adapter->mii_readbit)(adapter->adapter_softc, MII_DATA);
258 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
259
260 for (i = 0; i < 16; i++) {
261 val = val << 1;
262 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
263 if (err == 0)
264 if ((*adapter->mii_readbit)(adapter->adapter_softc,
265 MII_DATA))
266 val |= 1;
267 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
268 }
269 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
270 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
271
272 if (err == 0)
273 return val;
274 else
275 return -1;
276 }
277
278 void
279 mii_writereg(v, phy, reg, data)
280 void *v;
281 u_int16_t phy;
282 u_int16_t reg;
283 u_int16_t data;
284 {
285 mii_data_t *adapter = ((struct mii_softc *)v)->adapter;
286
287 if (adapter->mii_writereg) {
288 /* Interface has a special way of writing to the PHY. */
289 (*adapter->mii_writereg)(adapter, phy, reg, data);
290 return;
291 }
292
293 /* else write using the control lines */
294 mii_sync(adapter);
295 mii_sendbit(adapter, MII_START, 2);
296 mii_sendbit(adapter, MII_WRITE, 2);
297 mii_sendbit(adapter, phy, 5);
298 mii_sendbit(adapter, reg, 5);
299 mii_sendbit(adapter, MII_ACK, 2);
300 mii_sendbit(adapter, data, 16);
301
302 (*adapter->mii_clrbit)(adapter->adapter_softc, MII_CLOCK);
303 (*adapter->mii_setbit)(adapter->adapter_softc, MII_CLOCK);
304 }
305
306 void
307 mii_media_add(ifmedia, adapter)
308 struct ifmedia *ifmedia;
309 mii_data_t *adapter;
310 {
311 struct mii_softc *sc = adapter->mii_sc;
312 int i;
313 u_int32_t media = 0;
314
315 for (i = 0; i < 32; i++) {
316 if (sc->phy[i])
317 media |= sc->phy[i]->phy_media;
318 }
319 if (media & PHY_BNC)
320 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_2, 0, NULL);
321 if (media & PHY_AUI)
322 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_5, 0, NULL);
323 if (media & PHY_10baseT)
324 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
325 if (media & PHY_10baseTfd)
326 ifmedia_add(ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
327 if (media & PHY_100baseTx)
328 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
329 if (media & PHY_100baseTxfd)
330 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
331 if (media & PHY_100baseT4)
332 ifmedia_add(ifmedia, IFM_ETHER | IFM_100_T4, 0, NULL);
333 ifmedia_add(ifmedia, IFM_ETHER | IFM_NONE, 0, NULL);
334 }
335
336 int
337 mii_mediachg(adapter)
338 mii_data_t *adapter;
339 {
340 struct mii_softc *sc = adapter->mii_sc;
341 int i, best = -1, error = 0;
342 int media = adapter->mii_media_active;
343
344 sc->current_phy = NULL;
345
346 for (i = 0; i < 32; i++) {
347 if (sc->phy[i] == NULL)
348 continue;
349 switch (sc->phy[i]->phy_media_set(media,
350 sc->phy[i]->phy_softc)) {
351 case -1: /* PHY not available */
352 break;
353 case 0: /* link sucessfully selected */
354 sc->current_phy = sc->phy[i];
355 break;
356 case ENETDOWN: /* link selected but not up */
357 best = i;
358 break;
359 default:
360 break;
361 }
362 }
363 if (sc->current_phy == NULL) {
364 /*
365 * We didn't find a valid media. Select the best one (i.e.
366 * last supported but not up). If media != autoselect,
367 * don't report any error code.
368 */
369 if (best < 0)
370 return (EINVAL);
371 sc->current_phy = sc->phy[best];
372 error = sc->phy[best]->phy_media_set(media,
373 sc->phy[best]->phy_softc);
374 if (media != IFM_AUTO)
375 error = 0;
376 }
377 /* power down all but current phy */
378 for (i = 0; i < 32; i++) {
379 if (sc->phy[i] != sc->current_phy) {
380 if (sc->phy[i] == NULL)
381 mii_writereg(sc, i, PHY_CONTROL, CTRL_ISO);
382 else
383 sc->phy[i]->phy_pdown(sc->phy[i]->phy_softc);
384 }
385 }
386 return (error);
387 }
388
389 void
390 mii_pollstat(adapter)
391 mii_data_t *adapter;
392 {
393 struct mii_softc *sc = adapter->mii_sc;
394
395 adapter->mii_media_status = IFM_AVALID;
396 if (sc->current_phy == NULL)
397 return;
398 if ((*sc->current_phy->phy_status)(adapter->mii_media_active,
399 sc->current_phy->phy_softc) == 0)
400 adapter->mii_media_status |= IFM_ACTIVE;
401 }
402