dcphy.c revision 1.1
1/*	$OpenBSD: dcphy.c,v 1.1 2000/01/09 01:15:16 jason 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.2 1999/12/13 21:45:13 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/device.h>
48#include <sys/systm.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/socket.h>
52#include <sys/errno.h>
53#include <sys/socket.h>
54
55#include <net/if.h>
56#include <net/if_dl.h>
57#include <net/if_types.h>
58#include <net/if_media.h>
59#include <netinet/in.h>
60#include <netinet/if_ether.h>
61
62#include <dev/mii/mii.h>
63#include <dev/mii/miivar.h>
64#include <dev/mii/miidevs.h>
65
66#include <machine/bus.h>
67
68#include <dev/pci/pcivar.h>
69
70#include <dev/pci/if_dcreg.h>
71
72#define DC_SETBIT(sc, reg, x)                           \
73        CSR_WRITE_4(sc, reg,                            \
74                CSR_READ_4(sc, reg) | x)
75
76#define DC_CLRBIT(sc, reg, x)                           \
77        CSR_WRITE_4(sc, reg,                            \
78                CSR_READ_4(sc, reg) & ~x)
79
80#define MIIF_AUTOTIMEOUT	0x0004
81
82/*
83 * This is the subsystem ID for the built-in 21143 ethernet
84 * in several Compaq Presario systems. Apparently these are
85 * 10Mbps only, so we need to treat them specially.
86 */
87#define COMPAQ_PRESARIO_ID	0xb0bb0e11
88
89int	dcphy_match	__P((struct device *, void *, void *));
90void	dcphy_attach	__P((struct device *, struct device *, void *));
91
92struct cfattach dcphy_ca = {
93	sizeof(struct mii_softc), dcphy_match, dcphy_attach
94};
95
96struct cfdriver dcphy_cd = {
97	NULL, "dcphy", DV_DULL
98};
99
100int	dcphy_service	__P((struct mii_softc *, struct mii_data *, int));
101void	dcphy_status	__P((struct mii_softc *));
102int	dcphy_auto	__P((struct mii_softc *, int));
103void	dcphy_reset	__P((struct mii_softc *));
104
105int
106dcphy_match(parent, match, aux)
107	struct device *parent;
108	void *match, *aux;
109{
110	struct mii_attach_args *ma = aux;
111
112	/*
113	 * The dc driver will report the 21143 vendor and device
114	 * ID to let us know that it wants us to attach.
115	 */
116	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC &&
117	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC)
118		return (10);
119
120	return (0);
121}
122
123void
124dcphy_attach(parent, self, aux)
125	struct device *parent;
126	struct device *self;
127	void *aux;
128{
129	struct mii_softc *sc = (struct mii_softc *)self;
130	struct mii_attach_args *ma = aux;
131	struct mii_data *mii = ma->mii_data;
132	struct dc_softc *dc_sc;
133
134	printf(": internal PHY\n");
135	sc->mii_inst = mii->mii_instance;
136	sc->mii_phy = ma->mii_phyno;
137	sc->mii_service = dcphy_service;
138	sc->mii_pdata = mii;
139
140	sc->mii_flags |= MIIF_NOISOLATE;
141	mii->mii_instance++;
142
143	dc_sc = mii->mii_ifp->if_softc;
144	CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
145	CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
146
147#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
148
149	switch(dc_sc->dc_csid) {
150	case COMPAQ_PRESARIO_ID:
151		/* Example of how to only allow 10Mbps modes. */
152		sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
153		break;
154	default:
155		ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
156		    sc->mii_inst), BMCR_LOOP|BMCR_S100);
157
158		sc->mii_capabilities =
159		    BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|
160		    BMSR_10TFDX|BMSR_10THDX;
161		break;
162	}
163
164	sc->mii_capabilities &= ma->mii_capmask;
165	if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
166		printf("no media present");
167	else
168		mii_add_media(sc);
169#undef ADD
170}
171
172int
173dcphy_service(sc, mii, cmd)
174	struct mii_softc *sc;
175	struct mii_data *mii;
176	int cmd;
177{
178	struct dc_softc *dc_sc;
179	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
180	int reg;
181	u_int32_t mode;
182
183	dc_sc = mii->mii_ifp->if_softc;
184
185	switch (cmd) {
186	case MII_POLLSTAT:
187		/*
188		 * If we're not polling our PHY instance, just return.
189		 */
190		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
191			return (0);
192		}
193		break;
194
195	case MII_MEDIACHG:
196		/*
197		 * If the media indicates a different PHY instance,
198		 * isolate ourselves.
199		 */
200		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
201			return (0);
202		}
203
204		/*
205		 * If the interface is not up, don't do anything.
206		 */
207		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
208			break;
209
210		sc->mii_flags = 0;
211		mii->mii_media_active = IFM_NONE;
212		mode = CSR_READ_4(dc_sc, DC_NETCFG);
213		mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL|
214		    DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL);
215
216		switch (IFM_SUBTYPE(ife->ifm_media)) {
217		case IFM_AUTO:
218			/*dcphy_reset(sc);*/
219			(void) dcphy_auto(sc, 0);
220			break;
221		case IFM_100_T4:
222			/*
223			 * XXX Not supported as a manual setting right now.
224			 */
225			return (EINVAL);
226		case IFM_100_TX:
227			dcphy_reset(sc);
228			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
229			mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS|
230			    DC_NETCFG_SCRAMBLER;
231			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
232				mode |= DC_NETCFG_FULLDUPLEX;
233			else
234				mode &= ~DC_NETCFG_FULLDUPLEX;
235			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
236			break;
237		case IFM_10_T:
238			DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
239			DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
240			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
241				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
242			else
243				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
244			DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
245			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
246			mode &= ~DC_NETCFG_PORTSEL;
247			mode |= DC_NETCFG_SPEEDSEL;
248			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
249				mode |= DC_NETCFG_FULLDUPLEX;
250			else
251				mode &= ~DC_NETCFG_FULLDUPLEX;
252			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
253			break;
254		default:
255			return(EINVAL);
256			break;
257		}
258		break;
259
260	case MII_TICK:
261		/*
262		 * If we're not currently selected, just return.
263		 */
264		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
265			return (0);
266
267		/*
268		 * Only used for autonegotiation.
269		 */
270		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
271			return (0);
272
273		/*
274		 * Is the interface even up?
275		 */
276		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
277			return (0);
278
279		if (sc->mii_flags & MIIF_DOINGAUTO) {
280			if (++sc->mii_ticks != 5)
281				return(0);
282			else {
283				sc->mii_ticks = 0;
284				sc->mii_flags &= ~MIIF_DOINGAUTO;
285				sc->mii_flags |= MIIF_AUTOTIMEOUT;
286			}
287		}
288
289		sc->mii_flags &= ~MIIF_DOINGAUTO;
290
291		/*
292		 * Check to see if we have link.  If we do, we don't
293		 * need to restart the autonegotiation process.  Read
294		 * the BMSR twice in case it's latched.
295		 */
296		reg = CSR_READ_4(dc_sc, DC_10BTSTAT) &
297		    (DC_TSTAT_LS10|DC_TSTAT_LS100);
298
299		if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX &&
300		    !(reg & DC_TSTAT_LS100)) {
301			if (sc->mii_flags & MIIF_AUTOTIMEOUT) {
302				sc->mii_flags &= ~MIIF_AUTOTIMEOUT;
303				break;
304			} else
305				return(0);
306		} else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T &&
307		    !(reg & DC_TSTAT_LS10)) {
308			if (sc->mii_flags & MIIF_AUTOTIMEOUT) {
309				sc->mii_flags &= ~MIIF_AUTOTIMEOUT;
310				break;
311			} else
312				return(0);
313		} else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE &&
314		    (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))) {
315			if (sc->mii_flags & MIIF_AUTOTIMEOUT) {
316				sc->mii_flags &= ~MIIF_AUTOTIMEOUT;
317				break;
318			} else
319				return(0);
320		} else if (CSR_READ_4(dc_sc, DC_ISR) & DC_ISR_LINKGOOD) {
321			if (sc->mii_flags & MIIF_AUTOTIMEOUT) {
322				sc->mii_flags &= ~MIIF_AUTOTIMEOUT;
323				break;
324			} else
325				return(0);
326		}
327
328		sc->mii_ticks = 0;
329		/*dcphy_reset(sc);*/
330		dcphy_auto(sc, 0);
331
332		break;
333	}
334
335	/* Update the media status. */
336	dcphy_status(sc);
337
338	/* Callback if something changed. */
339	if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
340		(*mii->mii_statchg)(sc->mii_dev.dv_parent);
341		sc->mii_active = mii->mii_media_active;
342	}
343	return (0);
344}
345
346void
347dcphy_status(sc)
348	struct mii_softc *sc;
349{
350	struct mii_data *mii = sc->mii_pdata;
351	int reg, anlpar;
352	struct dc_softc *dc_sc;
353
354	dc_sc = mii->mii_ifp->if_softc;
355
356	mii->mii_media_status = IFM_AVALID;
357	mii->mii_media_active = IFM_ETHER;
358
359	reg = CSR_READ_4(dc_sc, DC_10BTSTAT) &
360	     (DC_TSTAT_LS10|DC_TSTAT_LS100);
361
362	if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
363		mii->mii_media_status |= IFM_ACTIVE;
364
365	if (sc->mii_flags & MIIF_DOINGAUTO) {
366		mii->mii_media_active |= IFM_NONE;
367		return;
368	}
369
370	if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL &&
371	    CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) {
372		/* Erg, still trying, I guess... */
373		if ((CSR_READ_4(dc_sc, DC_10BTSTAT) &
374		    DC_ASTAT_AUTONEGCMP) != DC_ASTAT_AUTONEGCMP) {
375			mii->mii_media_active |= IFM_NONE;
376			return;
377		}
378
379		if (CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_LP_CAN_NWAY) {
380			anlpar = CSR_READ_4(dc_sc, DC_10BTSTAT) >> 16;
381			if (anlpar & ANLPAR_T4 &&
382			    sc->mii_capabilities & BMSR_100TXHDX)
383				mii->mii_media_active |= IFM_100_T4;
384			else if (anlpar & ANLPAR_TX_FD &&
385			    sc->mii_capabilities & BMSR_100TXHDX)
386				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
387			else if (anlpar & ANLPAR_TX &&
388			    sc->mii_capabilities & BMSR_100TXHDX)
389				mii->mii_media_active |= IFM_100_TX;
390			else if (anlpar & ANLPAR_10_FD)
391				mii->mii_media_active |= IFM_10_T|IFM_FDX;
392			else if (anlpar & ANLPAR_10)
393				mii->mii_media_active |= IFM_10_T;
394			else
395				mii->mii_media_active |= IFM_NONE;
396			if (DC_IS_INTEL(dc_sc))
397				DC_CLRBIT(dc_sc, DC_10BTCTRL,
398				    DC_TCTL_AUTONEGENBL);
399			return;
400		}
401		/*
402		 * If the other side doesn't support NWAY, then the
403		 * best we can do is determine if we have a 10Mbps or
404		 * 100Mbps link. There's no way to know if the link
405		 * is full or half duplex, so we default to half duplex
406		 * and hope that the user is clever enough to manually
407		 * change the media settings if we're wrong.
408		 */
409		if (!(reg & DC_TSTAT_LS100))
410			mii->mii_media_active |= IFM_100_TX;
411		else if (!(reg & DC_TSTAT_LS10))
412			mii->mii_media_active |= IFM_10_T;
413		else
414			mii->mii_media_active |= IFM_NONE;
415		if (DC_IS_INTEL(dc_sc))
416			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
417		return;
418	}
419
420	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER)
421		mii->mii_media_active |= IFM_100_TX;
422	else
423		mii->mii_media_active |= IFM_10_T;
424	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
425		mii->mii_media_active |= IFM_FDX;
426
427	return;
428}
429
430int
431dcphy_auto(mii, waitfor)
432	struct mii_softc	*mii;
433	int			waitfor;
434{
435	int			i;
436	struct dc_softc		*sc;
437
438	sc = mii->mii_pdata->mii_ifp->if_softc;
439
440	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
441		DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
442		DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
443		DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
444		if (mii->mii_capabilities & BMSR_100TXHDX)
445			CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
446		else
447			CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
448		DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
449		DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
450		DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
451	}
452
453	if (waitfor) {
454		/* Wait 500ms for it to complete. */
455		for (i = 0; i < 500; i++) {
456			if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT)
457			    == DC_ASTAT_AUTONEGCMP)
458				return(0);
459			DELAY(1000);
460		}
461		/*
462		 * Don't need to worry about clearing MIIF_DOINGAUTO.
463		 * If that's set, a timeout is pending, and it will
464		 * clear the flag.
465		 */
466		return(EIO);
467	}
468
469	/*
470	 * Just let it finish asynchronously.  This is for the benefit of
471	 * the tick handler driving autonegotiation.  Don't want 500ms
472	 * delays all the time while the system is running!
473	 */
474	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0)
475		mii->mii_flags |= MIIF_DOINGAUTO;
476
477	return(EJUSTRETURN);
478}
479
480void
481dcphy_reset(mii)
482	struct mii_softc	*mii;
483{
484	struct dc_softc		*sc;
485
486	sc = mii->mii_pdata->mii_ifp->if_softc;
487
488	DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
489	DELAY(1000);
490	DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
491
492	return;
493}
494