dcphy.c revision 1.7
1169695Skan/*	$OpenBSD: dcphy.c,v 1.7 2002/05/04 11:30:06 fgsch Exp $	*/
2169695Skan
3169695Skan/*
4169695Skan * Copyright (c) 1997, 1998, 1999
5169695Skan *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
6169695Skan *
7169695Skan * Redistribution and use in source and binary forms, with or without
8169695Skan * modification, are permitted provided that the following conditions
9169695Skan * are met:
10169695Skan * 1. Redistributions of source code must retain the above copyright
11169695Skan *    notice, this list of conditions and the following disclaimer.
12169695Skan * 2. Redistributions in binary form must reproduce the above copyright
13169695Skan *    notice, this list of conditions and the following disclaimer in the
14169695Skan *    documentation and/or other materials provided with the distribution.
15169695Skan * 3. All advertising materials mentioning features or use of this software
16169695Skan *    must display the following acknowledgement:
17169695Skan *	This product includes software developed by Bill Paul.
18169695Skan * 4. Neither the name of the author nor the names of any co-contributors
19169695Skan *    may be used to endorse or promote products derived from this software
20169695Skan *    without specific prior written permission.
21169695Skan *
22169695Skan * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23169695Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24169695Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25169695Skan * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26169695Skan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27169695Skan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28169695Skan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29169695Skan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30169695Skan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31169695Skan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32169695Skan * THE POSSIBILITY OF SUCH DAMAGE.
33169695Skan *
34169695Skan * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $
35169695Skan */
36169695Skan
37169695Skan/*
38169695Skan * Pseudo-driver for internal NWAY support on DEC 21143 and workalike
39169695Skan * controllers. Technically we're abusing the miibus code to handle
40169695Skan * media selection and NWAY support here since there is no MII
41169695Skan * interface. However the logical operations are roughly the same,
42169695Skan * and the alternative is to create a fake MII interface in the driver,
43169695Skan * which is harder to do.
44169695Skan */
45169695Skan
46169695Skan#include <sys/param.h>
47169695Skan#include <sys/device.h>
48169695Skan#include <sys/systm.h>
49169695Skan#include <sys/kernel.h>
50169695Skan#include <sys/socket.h>
51169695Skan#include <sys/errno.h>
52169695Skan#include <sys/socket.h>
53169695Skan
54169695Skan#include <net/if.h>
55169695Skan#include <net/if_dl.h>
56169695Skan#include <net/if_types.h>
57169695Skan#include <net/if_media.h>
58169695Skan#include <netinet/in.h>
59169695Skan#include <netinet/if_ether.h>
60169695Skan
61283010Spfg#include <dev/mii/mii.h>
62169695Skan#include <dev/mii/miivar.h>
63169695Skan#include <dev/mii/miidevs.h>
64169695Skan
65283010Spfg#include <machine/bus.h>
66169695Skan
67169695Skan#include <dev/pci/pcivar.h>
68169695Skan
69169695Skan#include <dev/ic/dcreg.h>
70169695Skan
71169695Skan#define DC_SETBIT(sc, reg, x)                           \
72169695Skan        CSR_WRITE_4(sc, reg,                            \
73169695Skan                CSR_READ_4(sc, reg) | x)
74169695Skan
75169695Skan#define DC_CLRBIT(sc, reg, x)                           \
76169695Skan        CSR_WRITE_4(sc, reg,                            \
77169695Skan                CSR_READ_4(sc, reg) & ~x)
78169695Skan
79169695Skan#define MIIF_AUTOTIMEOUT	0x0004
80169695Skan
81169695Skan/*
82169695Skan * This is the subsystem ID for the built-in 21143 ethernet
83169695Skan * in several Compaq Presario systems. Apparently these are
84169695Skan * 10Mbps only, so we need to treat them specially.
85169695Skan */
86169695Skan#define COMPAQ_PRESARIO_ID	0xb0bb0e11
87169695Skan
88169695Skanint	dcphy_match(struct device *, void *, void *);
89169695Skanvoid	dcphy_attach(struct device *, struct device *, void *);
90169695Skan
91169695Skanstruct cfattach dcphy_ca = {
92169695Skan	sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach,
93169695Skan	    mii_phy_activate
94169695Skan};
95169695Skan
96169695Skanstruct cfdriver dcphy_cd = {
97169695Skan	NULL, "dcphy", DV_DULL
98169695Skan};
99169695Skan
100169695Skanint	dcphy_service(struct mii_softc *, struct mii_data *, int);
101169695Skanvoid	dcphy_status(struct mii_softc *);
102169695Skanint	dcphy_auto(struct mii_softc *, int);
103169695Skanvoid	dcphy_reset(struct mii_softc *);
104169695Skan
105169695Skanint
106169695Skandcphy_match(parent, match, aux)
107169695Skan	struct device *parent;
108169695Skan	void *match, *aux;
109169695Skan{
110169695Skan	struct mii_attach_args *ma = aux;
111169695Skan
112169695Skan	/*
113169695Skan	 * The dc driver will report the 21143 vendor and device
114169695Skan	 * ID to let us know that it wants us to attach.
115169695Skan	 */
116169695Skan	if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC &&
117169695Skan	    MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC)
118169695Skan		return (10);
119169695Skan
120169695Skan	return (0);
121169695Skan}
122169695Skan
123169695Skanvoid
124169695Skandcphy_attach(parent, self, aux)
125169695Skan	struct device *parent;
126169695Skan	struct device *self;
127169695Skan	void *aux;
128169695Skan{
129169695Skan	struct mii_softc *sc = (struct mii_softc *)self;
130169695Skan	struct mii_attach_args *ma = aux;
131169695Skan	struct mii_data *mii = ma->mii_data;
132283010Spfg	struct dc_softc *dc_sc;
133169695Skan
134169695Skan	printf(": internal PHY\n");
135169695Skan	sc->mii_inst = mii->mii_instance;
136169695Skan	sc->mii_phy = ma->mii_phyno;
137169695Skan	sc->mii_service = dcphy_service;
138169695Skan	sc->mii_status = dcphy_status;
139169695Skan	sc->mii_pdata = mii;
140169695Skan	sc->mii_flags = mii->mii_flags;
141169695Skan
142169695Skan	sc->mii_flags |= MIIF_NOISOLATE;
143169695Skan	mii->mii_instance++;
144169695Skan
145169695Skan	dc_sc = mii->mii_ifp->if_softc;
146169695Skan	CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
147169695Skan	CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
148169695Skan
149169695Skan#define	ADD(m, c)	ifmedia_add(&mii->mii_media, (m), (c), NULL)
150169695Skan
151169695Skan	switch(dc_sc->dc_csid) {
152169695Skan	case COMPAQ_PRESARIO_ID:
153169695Skan		/* Example of how to only allow 10Mbps modes. */
154169695Skan		sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
155169695Skan		break;
156169695Skan	default:
157283010Spfg		if (dc_sc->dc_pmode == DC_PMODE_SIA) {
158169695Skan			sc->mii_capabilities =
159169695Skan			    BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX;
160169695Skan		} else {
161169695Skan			ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP,
162169695Skan			    sc->mii_inst), BMCR_LOOP|BMCR_S100);
163169695Skan
164169695Skan			sc->mii_capabilities =
165169695Skan			    BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|
166169695Skan			    BMSR_10TFDX|BMSR_10THDX;
167169695Skan		}
168169695Skan		break;
169169695Skan	}
170169695Skan
171169695Skan	sc->mii_capabilities &= ma->mii_capmask;
172169695Skan	if (sc->mii_capabilities & BMSR_MEDIAMASK)
173169695Skan		mii_phy_add_media(sc);
174169695Skan#undef ADD
175169695Skan}
176169695Skan
177169695Skanint
178169695Skandcphy_service(sc, mii, cmd)
179169695Skan	struct mii_softc *sc;
180169695Skan	struct mii_data *mii;
181169695Skan	int cmd;
182169695Skan{
183169695Skan	struct dc_softc *dc_sc;
184169695Skan	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
185169695Skan	int reg;
186169695Skan	u_int32_t mode;
187169695Skan
188169695Skan	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
189169695Skan		return (ENXIO);
190169695Skan
191169695Skan	dc_sc = mii->mii_ifp->if_softc;
192283010Spfg
193283010Spfg	switch (cmd) {
194283010Spfg	case MII_POLLSTAT:
195283010Spfg		/*
196283010Spfg		 * If we're not polling our PHY instance, just return.
197283010Spfg		 */
198283010Spfg		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
199283010Spfg			return (0);
200283010Spfg		}
201283010Spfg		break;
202283010Spfg
203283010Spfg	case MII_MEDIACHG:
204283010Spfg		/*
205283010Spfg		 * If the media indicates a different PHY instance,
206283010Spfg		 * isolate ourselves.
207283010Spfg		 */
208283010Spfg		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
209283010Spfg			return (0);
210283010Spfg		}
211283010Spfg
212283010Spfg		/*
213169695Skan		 * If the interface is not up, don't do anything.
214169695Skan		 */
215169695Skan		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
216169695Skan			break;
217169695Skan
218169695Skan		sc->mii_flags = 0;
219169695Skan		mii->mii_media_active = IFM_NONE;
220169695Skan		mode = CSR_READ_4(dc_sc, DC_NETCFG);
221169695Skan		mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL|
222169695Skan		    DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL);
223169695Skan
224169695Skan		switch (IFM_SUBTYPE(ife->ifm_media)) {
225169695Skan		case IFM_AUTO:
226169695Skan			/*dcphy_reset(sc);*/
227169695Skan			sc->mii_flags &= ~MIIF_DOINGAUTO;
228169695Skan			(void) dcphy_auto(sc, 0);
229169695Skan			break;
230169695Skan		case IFM_100_T4:
231169695Skan			/*
232169695Skan			 * XXX Not supported as a manual setting right now.
233169695Skan			 */
234169695Skan			return (EINVAL);
235169695Skan		case IFM_100_TX:
236169695Skan			dcphy_reset(sc);
237169695Skan			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
238169695Skan			mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS|
239169695Skan			    DC_NETCFG_SCRAMBLER;
240169695Skan			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
241169695Skan				mode |= DC_NETCFG_FULLDUPLEX;
242169695Skan			else
243169695Skan				mode &= ~DC_NETCFG_FULLDUPLEX;
244169695Skan			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
245169695Skan			break;
246169695Skan		case IFM_10_T:
247169695Skan			DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
248169695Skan			DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
249169695Skan			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
250169695Skan				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
251169695Skan			else
252169695Skan				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
253169695Skan			DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
254169695Skan			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
255169695Skan			mode &= ~DC_NETCFG_PORTSEL;
256169695Skan			mode |= DC_NETCFG_SPEEDSEL;
257169695Skan			if ((ife->ifm_media & IFM_GMASK) == IFM_FDX)
258169695Skan				mode |= DC_NETCFG_FULLDUPLEX;
259169695Skan			else
260169695Skan				mode &= ~DC_NETCFG_FULLDUPLEX;
261169695Skan			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
262169695Skan			break;
263169695Skan		default:
264169695Skan			return(EINVAL);
265169695Skan			break;
266169695Skan		}
267169695Skan		break;
268169695Skan
269169695Skan	case MII_TICK:
270169695Skan		/*
271169695Skan		 * If we're not currently selected, just return.
272169695Skan		 */
273169695Skan		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
274169695Skan			return (0);
275169695Skan
276169695Skan		/*
277169695Skan		 * Only used for autonegotiation.
278169695Skan		 */
279169695Skan		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
280169695Skan			return (0);
281169695Skan
282169695Skan		/*
283169695Skan		 * Is the interface even up?
284169695Skan		 */
285169695Skan		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
286169695Skan			return (0);
287169695Skan
288169695Skan		reg = CSR_READ_4(dc_sc, DC_10BTSTAT) &
289169695Skan		    (DC_TSTAT_LS10|DC_TSTAT_LS100);
290169695Skan
291169695Skan		if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
292169695Skan			return (0);
293169695Skan
294169695Skan		/*
295169695Skan		 * Only retry autonegotiation every 5 seconds.
296169695Skan		 */
297169695Skan		if (++sc->mii_ticks != 50)
298169695Skan			return (0);
299169695Skan
300169695Skan		sc->mii_ticks = 0;
301169695Skan		/*if (DC_IS_INTEL(dc_sc))*/
302169695Skan			sc->mii_flags &= ~MIIF_DOINGAUTO;
303169695Skan		dcphy_auto(sc, 0);
304169695Skan
305169695Skan		break;
306169695Skan	}
307169695Skan
308169695Skan	/* Update the media status. */
309169695Skan	mii_phy_status(sc);
310169695Skan
311	/* Callback if something changed. */
312	mii_phy_update(sc, cmd);
313	return (0);
314}
315
316void
317dcphy_status(sc)
318	struct mii_softc *sc;
319{
320	struct mii_data *mii = sc->mii_pdata;
321	int reg, anlpar, tstat = 0;
322	struct dc_softc *dc_sc;
323
324	dc_sc = mii->mii_ifp->if_softc;
325
326	mii->mii_media_status = IFM_AVALID;
327	mii->mii_media_active = IFM_ETHER;
328
329	if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
330		return;
331
332	reg = CSR_READ_4(dc_sc, DC_10BTSTAT) &
333	    (DC_TSTAT_LS10|DC_TSTAT_LS100);
334
335	if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
336		mii->mii_media_status |= IFM_ACTIVE;
337
338	if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) {
339		/* Erg, still trying, I guess... */
340		tstat = CSR_READ_4(dc_sc, DC_10BTSTAT);
341		if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) {
342			if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) &&
343			    (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE)
344				goto skip;
345			mii->mii_media_active |= IFM_NONE;
346			return;
347		}
348
349		if (tstat & DC_TSTAT_LP_CAN_NWAY) {
350			anlpar = tstat >> 16;
351			if (anlpar & ANLPAR_T4 &&
352			    sc->mii_capabilities & BMSR_100TXHDX)
353				mii->mii_media_active |= IFM_100_T4;
354			else if (anlpar & ANLPAR_TX_FD &&
355			    sc->mii_capabilities & BMSR_100TXFDX)
356				mii->mii_media_active |= IFM_100_TX|IFM_FDX;
357			else if (anlpar & ANLPAR_TX &&
358			    sc->mii_capabilities & BMSR_100TXHDX)
359				mii->mii_media_active |= IFM_100_TX;
360			else if (anlpar & ANLPAR_10_FD)
361				mii->mii_media_active |= IFM_10_T|IFM_FDX;
362			else if (anlpar & ANLPAR_10)
363				mii->mii_media_active |= IFM_10_T;
364			else
365				mii->mii_media_active |= IFM_NONE;
366			if (DC_IS_INTEL(dc_sc))
367				DC_CLRBIT(dc_sc, DC_10BTCTRL,
368				    DC_TCTL_AUTONEGENBL);
369			return;
370		}
371		/*
372		 * If the other side doesn't support NWAY, then the
373		 * best we can do is determine if we have a 10Mbps or
374		 * 100Mbps link. There's no way to know if the link
375		 * is full or half duplex, so we default to half duplex
376		 * and hope that the user is clever enough to manually
377		 * change the media settings if we're wrong.
378		 */
379		if (!(reg & DC_TSTAT_LS100))
380			mii->mii_media_active |= IFM_100_TX;
381		else if (!(reg & DC_TSTAT_LS10))
382			mii->mii_media_active |= IFM_10_T;
383		else
384			mii->mii_media_active |= IFM_NONE;
385		if (DC_IS_INTEL(dc_sc))
386			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
387		return;
388	}
389
390skip:
391
392	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL)
393		mii->mii_media_active |= IFM_10_T;
394	else
395		mii->mii_media_active |= IFM_100_TX;
396	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
397		mii->mii_media_active |= IFM_FDX;
398
399	return;
400}
401
402int
403dcphy_auto(mii, waitfor)
404	struct mii_softc	*mii;
405	int			waitfor;
406{
407	int			i;
408	struct dc_softc		*sc;
409
410	sc = mii->mii_pdata->mii_ifp->if_softc;
411
412	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) {
413		DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
414		DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
415		DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
416		if (mii->mii_capabilities & BMSR_100TXHDX)
417			CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
418		else
419			CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
420		DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
421		DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
422		DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
423	}
424
425	if (waitfor) {
426		/* Wait 500ms for it to complete. */
427		for (i = 0; i < 500; i++) {
428			if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT)
429			    == DC_ASTAT_AUTONEGCMP)
430				return(0);
431			DELAY(1000);
432		}
433		/*
434		 * Don't need to worry about clearing MIIF_DOINGAUTO.
435		 * If that's set, a timeout is pending, and it will
436		 * clear the flag.
437		 */
438		return(EIO);
439	}
440
441	/*
442	 * Just let it finish asynchronously.  This is for the benefit of
443	 * the tick handler driving autonegotiation.  Don't want 500ms
444	 * delays all the time while the system is running!
445	 */
446	if ((mii->mii_flags & MIIF_DOINGAUTO) == 0)
447		mii->mii_flags |= MIIF_DOINGAUTO;
448
449	return(EJUSTRETURN);
450}
451
452void
453dcphy_reset(mii)
454	struct mii_softc	*mii;
455{
456	struct dc_softc		*sc;
457
458	sc = mii->mii_pdata->mii_ifp->if_softc;
459
460	DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
461	DELAY(1000);
462	DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
463
464	return;
465}
466