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