Line data Source code
1 : /* $OpenBSD: dcphy.c,v 1.25 2013/12/28 03:30:41 deraadt Exp $ */
2 :
3 : /*
4 : * Copyright (c) 1997, 1998, 1999
5 : * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * 2. Redistributions in binary form must reproduce the above copyright
13 : * notice, this list of conditions and the following disclaimer in the
14 : * documentation and/or other materials provided with the distribution.
15 : * 3. All advertising materials mentioning features or use of this software
16 : * must display the following acknowledgement:
17 : * This product includes software developed by Bill Paul.
18 : * 4. Neither the name of the author nor the names of any co-contributors
19 : * may be used to endorse or promote products derived from this software
20 : * without specific prior written permission.
21 : *
22 : * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 : * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 : * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 : * THE POSSIBILITY OF SUCH DAMAGE.
33 : *
34 : * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $
35 : */
36 :
37 : /*
38 : * Pseudo-driver for internal NWAY support on DEC 21143 and workalike
39 : * controllers. Technically we're abusing the miibus code to handle
40 : * media selection and NWAY support here since there is no MII
41 : * interface. However the logical operations are roughly the same,
42 : * and the alternative is to create a fake MII interface in the driver,
43 : * which is harder to do.
44 : */
45 :
46 : #include <sys/param.h>
47 : #include <sys/systm.h>
48 : #include <sys/kernel.h>
49 : #include <sys/device.h>
50 : #include <sys/socket.h>
51 : #include <sys/errno.h>
52 :
53 : #include <machine/bus.h>
54 :
55 : #include <net/if.h>
56 : #include <net/if_media.h>
57 :
58 : #include <netinet/in.h>
59 : #include <netinet/if_ether.h>
60 :
61 : #include <dev/mii/mii.h>
62 : #include <dev/mii/miivar.h>
63 : #include <dev/mii/miidevs.h>
64 :
65 : #include <dev/ic/dcreg.h>
66 :
67 : #define DC_SETBIT(sc, reg, x) \
68 : CSR_WRITE_4(sc, reg, \
69 : CSR_READ_4(sc, reg) | x)
70 :
71 : #define DC_CLRBIT(sc, reg, x) \
72 : CSR_WRITE_4(sc, reg, \
73 : CSR_READ_4(sc, reg) & ~x)
74 :
75 : #define MIIF_AUTOTIMEOUT 0x0004
76 :
77 : /*
78 : * This is the subsystem ID for the built-in 21143 ethernet
79 : * in several Compaq Presario systems. Apparently these are
80 : * 10Mbps only, so we need to treat them specially.
81 : */
82 : #define COMPAQ_PRESARIO_ID 0xb0bb0e11
83 :
84 : int dcphy_match(struct device *, void *, void *);
85 : void dcphy_attach(struct device *, struct device *, void *);
86 :
87 : struct cfattach dcphy_ca = {
88 : sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach
89 : };
90 :
91 : struct cfdriver dcphy_cd = {
92 : NULL, "dcphy", DV_DULL
93 : };
94 :
95 : int dcphy_service(struct mii_softc *, struct mii_data *, int);
96 : void dcphy_status(struct mii_softc *);
97 : int dcphy_mii_phy_auto(struct mii_softc *, int);
98 : void dcphy_reset(struct mii_softc *);
99 :
100 : const struct mii_phy_funcs dcphy_funcs = {
101 : dcphy_service, dcphy_status, dcphy_reset,
102 : };
103 :
104 : int
105 0 : dcphy_match(struct device *parent, void *match, void *aux)
106 : {
107 0 : struct mii_attach_args *ma = aux;
108 :
109 : /*
110 : * The dc driver will report the 21143 vendor and device
111 : * ID to let us know that it wants us to attach.
112 : */
113 0 : if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC &&
114 0 : MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC)
115 0 : return (10);
116 :
117 0 : return (0);
118 0 : }
119 :
120 : void
121 0 : dcphy_attach(struct device *parent, struct device *self, void *aux)
122 : {
123 0 : struct mii_softc *sc = (struct mii_softc *)self;
124 0 : struct mii_attach_args *ma = aux;
125 0 : struct mii_data *mii = ma->mii_data;
126 : struct dc_softc *dc_sc;
127 :
128 0 : printf(": internal PHY\n");
129 0 : sc->mii_inst = mii->mii_instance;
130 0 : sc->mii_phy = ma->mii_phyno;
131 0 : sc->mii_funcs = &dcphy_funcs;
132 0 : sc->mii_pdata = mii;
133 0 : sc->mii_flags = ma->mii_flags;
134 0 : sc->mii_anegticks = 50;
135 :
136 0 : sc->mii_flags |= MIIF_NOISOLATE;
137 :
138 0 : dc_sc = mii->mii_ifp->if_softc;
139 0 : CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
140 0 : CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
141 :
142 : #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
143 :
144 0 : switch(dc_sc->dc_csid) {
145 : case COMPAQ_PRESARIO_ID:
146 : /* Example of how to only allow 10Mbps modes. */
147 0 : sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
148 0 : break;
149 : default:
150 0 : if (dc_sc->dc_pmode == DC_PMODE_SIA) {
151 0 : sc->mii_capabilities =
152 : BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
153 0 : } else {
154 0 : ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
155 : sc->mii_inst), BMCR_LOOP|BMCR_S100);
156 :
157 0 : sc->mii_capabilities =
158 : BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|
159 : BMSR_10TFDX|BMSR_10THDX;
160 : }
161 : break;
162 : }
163 :
164 0 : if (dc_sc->dc_type == DC_TYPE_21145)
165 0 : sc->mii_capabilities = BMSR_10THDX;
166 :
167 0 : sc->mii_capabilities &= ma->mii_capmask;
168 0 : if (sc->mii_capabilities & BMSR_MEDIAMASK)
169 0 : mii_phy_add_media(sc);
170 : #undef ADD
171 0 : }
172 :
173 : int
174 0 : dcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
175 : {
176 : struct dc_softc *dc_sc;
177 0 : struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178 : int reg;
179 : u_int32_t mode;
180 :
181 0 : if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
182 0 : return (ENXIO);
183 :
184 0 : dc_sc = mii->mii_ifp->if_softc;
185 :
186 0 : switch (cmd) {
187 : case MII_POLLSTAT:
188 : /*
189 : * If we're not polling our PHY instance, just return.
190 : */
191 0 : if (IFM_INST(ife->ifm_media) != sc->mii_inst)
192 0 : return (0);
193 : break;
194 :
195 : case MII_MEDIACHG:
196 : /*
197 : * If the media indicates a different PHY instance,
198 : * isolate ourselves.
199 : */
200 0 : if (IFM_INST(ife->ifm_media) != sc->mii_inst)
201 0 : return (0);
202 :
203 : /*
204 : * If the interface is not up, don't do anything.
205 : */
206 0 : if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
207 : break;
208 :
209 0 : sc->mii_flags = 0;
210 0 : mii->mii_media_active = IFM_NONE;
211 0 : mode = CSR_READ_4(dc_sc, DC_NETCFG);
212 0 : mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL|
213 : DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL);
214 :
215 0 : switch (IFM_SUBTYPE(ife->ifm_media)) {
216 : case IFM_AUTO:
217 : /*PHY_RESET(sc);*/
218 0 : sc->mii_flags &= ~MIIF_DOINGAUTO;
219 0 : (void) dcphy_mii_phy_auto(sc, 0);
220 0 : break;
221 : case IFM_100_TX:
222 0 : PHY_RESET(sc);
223 0 : DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
224 0 : mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS|
225 : DC_NETCFG_SCRAMBLER;
226 0 : if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
227 0 : mode |= DC_NETCFG_FULLDUPLEX;
228 : else
229 : mode &= ~DC_NETCFG_FULLDUPLEX;
230 0 : CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
231 0 : break;
232 : case IFM_10_T:
233 0 : DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
234 0 : DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
235 0 : if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
236 0 : DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
237 : else
238 0 : DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
239 0 : DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
240 0 : DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
241 : mode &= ~DC_NETCFG_PORTSEL;
242 0 : mode |= DC_NETCFG_SPEEDSEL;
243 0 : if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
244 0 : mode |= DC_NETCFG_FULLDUPLEX;
245 : else
246 : mode &= ~DC_NETCFG_FULLDUPLEX;
247 0 : CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
248 0 : break;
249 : default:
250 0 : return (EINVAL);
251 : }
252 : break;
253 :
254 : case MII_TICK:
255 : /*
256 : * If we're not currently selected, just return.
257 : */
258 0 : if (IFM_INST(ife->ifm_media) != sc->mii_inst)
259 0 : return (0);
260 :
261 : /*
262 : * Is the interface even up?
263 : */
264 0 : if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
265 0 : return (0);
266 :
267 : /*
268 : * Only used for autonegotiation.
269 : */
270 0 : if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
271 : break;
272 :
273 0 : reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
274 0 : if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) {
275 0 : sc->mii_ticks = 0;
276 0 : break;
277 : }
278 :
279 : /*
280 : * Only retry autonegotiation every mii_anegticks seconds.
281 : *
282 : * Otherwise, fall through to calling dcphy_status()
283 : * since real Intel 21143 chips don't show valid link
284 : * status until autonegotiation is switched off, and
285 : * that only happens in dcphy_status(). Without this,
286 : * successful autonegotiation is never recognised on
287 : * these chips.
288 : */
289 0 : if (++sc->mii_ticks <= sc->mii_anegticks)
290 : break;
291 :
292 0 : sc->mii_ticks = 0;
293 0 : sc->mii_flags &= ~MIIF_DOINGAUTO;
294 0 : dcphy_mii_phy_auto(sc, 0);
295 :
296 0 : break;
297 : }
298 :
299 : /* Update the media status. */
300 0 : mii_phy_status(sc);
301 :
302 : /* Callback if something changed. */
303 0 : mii_phy_update(sc, cmd);
304 0 : return (0);
305 0 : }
306 :
307 : void
308 0 : dcphy_status(struct mii_softc *sc)
309 : {
310 0 : struct mii_data *mii = sc->mii_pdata;
311 : int reg, anlpar, tstat = 0;
312 : struct dc_softc *dc_sc;
313 :
314 0 : dc_sc = mii->mii_ifp->if_softc;
315 :
316 0 : mii->mii_media_status = IFM_AVALID;
317 0 : mii->mii_media_active = IFM_ETHER;
318 :
319 0 : reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
320 0 : if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
321 0 : mii->mii_media_status |= IFM_ACTIVE;
322 :
323 0 : if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) {
324 : /* Erg, still trying, I guess... */
325 0 : tstat = CSR_READ_4(dc_sc, DC_10BTSTAT);
326 0 : if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) {
327 0 : if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) &&
328 0 : (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE)
329 : goto skip;
330 0 : mii->mii_media_active |= IFM_NONE;
331 0 : return;
332 : }
333 :
334 0 : if (tstat & DC_TSTAT_LP_CAN_NWAY) {
335 0 : anlpar = tstat >> 16;
336 0 : if (anlpar & ANLPAR_TX_FD &&
337 0 : sc->mii_capabilities & BMSR_100TXFDX)
338 0 : mii->mii_media_active |= IFM_100_TX|IFM_FDX;
339 0 : else if (anlpar & ANLPAR_T4 &&
340 0 : sc->mii_capabilities & BMSR_100T4)
341 0 : mii->mii_media_active |= IFM_100_T4|IFM_HDX;
342 0 : else if (anlpar & ANLPAR_TX &&
343 0 : sc->mii_capabilities & BMSR_100TXHDX)
344 0 : mii->mii_media_active |= IFM_100_TX|IFM_HDX;
345 0 : else if (anlpar & ANLPAR_10_FD)
346 0 : mii->mii_media_active |= IFM_10_T|IFM_FDX;
347 0 : else if (anlpar & ANLPAR_10)
348 0 : mii->mii_media_active |= IFM_10_T|IFM_HDX;
349 : else
350 0 : mii->mii_media_active |= IFM_NONE;
351 0 : if (DC_IS_INTEL(dc_sc))
352 0 : DC_CLRBIT(dc_sc, DC_10BTCTRL,
353 : DC_TCTL_AUTONEGENBL);
354 0 : return;
355 : }
356 :
357 : /*
358 : * If the other side doesn't support NWAY, then the
359 : * best we can do is determine if we have a 10Mbps or
360 : * 100Mbps link. There's no way to know if the link
361 : * is full or half duplex, so we default to half duplex
362 : * and hope that the user is clever enough to manually
363 : * change the media settings if we're wrong.
364 : */
365 0 : if (!(reg & DC_TSTAT_LS100))
366 0 : mii->mii_media_active |= IFM_100_TX|IFM_HDX;
367 0 : else if (!(reg & DC_TSTAT_LS10))
368 0 : mii->mii_media_active |= IFM_10_T|IFM_HDX;
369 : else
370 0 : mii->mii_media_active |= IFM_NONE;
371 0 : if (DC_IS_INTEL(dc_sc))
372 0 : DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
373 0 : return;
374 : }
375 :
376 : skip:
377 0 : if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL)
378 0 : mii->mii_media_active |= IFM_10_T;
379 : else
380 0 : mii->mii_media_active |= IFM_100_TX;
381 :
382 0 : if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
383 0 : mii->mii_media_active |= IFM_FDX;
384 : else
385 0 : mii->mii_media_active |= IFM_HDX;
386 0 : }
387 :
388 : int
389 0 : dcphy_mii_phy_auto(struct mii_softc *mii, int waitfor)
390 : {
391 : int i;
392 : struct dc_softc *sc;
393 :
394 0 : sc = mii->mii_pdata->mii_ifp->if_softc;
395 :
396 0 : if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
397 0 : DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
398 0 : DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
399 0 : DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
400 0 : if (mii->mii_capabilities & BMSR_100TXHDX)
401 0 : CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
402 : else
403 0 : CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
404 0 : DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
405 0 : DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
406 0 : DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
407 0 : }
408 :
409 0 : if (waitfor) {
410 : /* Wait 500ms for it to complete. */
411 0 : for (i = 0; i < 500; i++) {
412 0 : if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT)
413 0 : == DC_ASTAT_AUTONEGCMP)
414 0 : return (0);
415 0 : DELAY(1000);
416 : }
417 : /*
418 : * Don't need to worry about clearing MIIF_DOINGAUTO.
419 : * If that's set, a timeout is pending, and it will
420 : * clear the flag.
421 : */
422 0 : return (EIO);
423 : }
424 :
425 : /*
426 : * Just let it finish asynchronously. This is for the benefit of
427 : * the tick handler driving autonegotiation. Don't want 500ms
428 : * delays all the time while the system is running!
429 : */
430 0 : if ((mii->mii_flags & MIIF_DOINGAUTO) == 0)
431 0 : mii->mii_flags |= MIIF_DOINGAUTO;
432 :
433 0 : return (EJUSTRETURN);
434 0 : }
435 :
436 : void
437 0 : dcphy_reset(struct mii_softc *mii)
438 : {
439 : struct dc_softc *sc;
440 :
441 0 : sc = mii->mii_pdata->mii_ifp->if_softc;
442 :
443 0 : DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
444 0 : DELAY(1000);
445 0 : DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
446 :
447 : return;
448 0 : }
|