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