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