Subject: kern/22480: SiS900 revision 635+ support
To: None <gnats-bugs@gnats.netbsd.org>
From: Joerg Sonnenberger <joerg@britannica.bec.de>
List: netbsd-bugs
Date: 08/14/2003 11:50:14
>Number:         22480
>Category:       kern
>Synopsis:       SiS900 revision 635+ support
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Aug 14 09:52:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     Joerg Sonnenberger
>Release:        NetBSD 1.6W 2003/08/12
>Organization:
>Environment:
System: NetBSD britannica 1.6W NetBSD 1.6W (TURTLE.fw) #1: Wed Aug 13 00:23:37 CEST 2003 root@britannica:/home/NetBSD/src/sys/arch/i386/compile/TURTLE.fw i386
Architecture: i386
Machine: i386
>Description:
SiS900 revision 635+ uses a different way to access the MII registers.
The patch adds the necessary support.
>How-To-Repeat:
>Fix:
--- if_sip.c.orig	2003-03-23 01:56:15.000000000 +0100
+++ if_sip.c	2003-08-13 00:22:31.000000000 +0200
@@ -439,6 +439,9 @@
 void	SIP_DECL(dp83820_mii_writereg)(struct device *, int, int, int);
 void	SIP_DECL(dp83820_mii_statchg)(struct device *);
 #else
+static void sis900_mii_sync(struct sip_softc *);
+static void sis900_mii_send(struct sip_softc *, u_int32_t, int);
+
 int	SIP_DECL(sis900_mii_readreg)(struct device *, int, int);
 void	SIP_DECL(sis900_mii_writereg)(struct device *, int, int, int);
 void	SIP_DECL(sis900_mii_statchg)(struct device *);
@@ -3042,11 +3045,53 @@
 	bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_EROMAR, val);
 }
 #else /* ! DP83820 */
+
+#define SIO_SET(x) bus_space_write_4(sc->sc_st,sc->sc_sh,SIS_EECTL, \
+	bus_space_read_4(sc->sc_st,sc->sc_sh,SIS_EECTL) | (x))
+#define SIO_CLR(x) bus_space_write_4(sc->sc_st,sc->sc_sh,SIS_EECTL, \
+	 bus_space_read_4(sc->sc_st,sc->sc_sh,SIS_EECTL) & ~(x))
+
+static void
+sis900_mii_sync(struct sip_softc *sc)
+{
+	int i;
+
+	SIO_SET(SIS_MII_DIR|SIS_MII_DATA);
+
+	for (i = 0; i < 32; i++) {
+		SIO_SET(SIS_MII_CLK);
+		DELAY(1);
+		SIO_CLR(SIS_MII_CLK);
+		DELAY(1);
+	}
+}
+
+static void
+sis900_mii_send(struct sip_softc *sc, u_int32_t bits, int cnt)
+{
+	int i;
+
+	SIO_CLR(SIS_MII_CLK);
+
+	for (i = (0x1 << (cnt -1)); i; i >>= 1) {
+		if (bits & i) {
+			SIO_SET(SIS_MII_DATA);
+		} else {
+			SIO_CLR(SIS_MII_DATA);
+		}
+		DELAY(1);
+		SIO_CLR(SIS_MII_CLK);
+		DELAY(1);
+		SIO_SET(SIS_MII_CLK);
+	}
+}
+
 /*
  * sip_sis900_mii_readreg:	[mii interface function]
  *
  *	Read a PHY register on the MII.
  */
+
 int
 SIP_DECL(sis900_mii_readreg)(struct device *self, int phy, int reg)
 {
@@ -3054,8 +3099,75 @@
 	u_int32_t enphy;
 
 	/*
-	 * The SiS 900 has only an internal PHY on the MII.  Only allow
-	 * MII address 0.
+	 * The SiS 900 revision 635 and later uses a different interface.
+	 */
+	if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
+	    sc->sc_rev >= SIS_REV_635) {
+		int i, ack, s;
+		u_int16_t mii_data = 0;
+
+		s = splnet();
+
+		/* Turn on data xmit. */
+		SIO_SET(SIS_MII_DIR);
+		sis900_mii_sync(sc);
+
+		/* Send command/address info */
+		sis900_mii_send(sc,SIS_MII_STARTDELIM,2);
+		sis900_mii_send(sc,SIS_MII_READOP,2);
+		sis900_mii_send(sc,phy,5);
+		sis900_mii_send(sc,reg,5);
+
+		/* Idle bit */
+		SIO_CLR(SIS_MII_CLK|SIS_MII_DATA);
+		DELAY(1);
+		SIO_SET(SIS_MII_CLK);
+		DELAY(1);
+
+		/* Turn off xmit. */
+		SIO_CLR(SIS_MII_DIR);
+
+		/* Check for ack */
+		SIO_CLR(SIS_MII_CLK);
+		DELAY(1);
+		ack = bus_space_read_4(sc->sc_st,sc->sc_sh,SIS_EECTL) & SIS_MII_DATA;
+		SIO_SET(SIS_MII_CLK);
+		DELAY(1);
+
+		/*
+		 * Now try reading data bits. If the ack failed, we still
+		 * need to clock through 16 cycles to keep the PHY(s) in sync.
+		 */
+		if (ack) {
+			for(i = 0; i < 16; i++) {
+				SIO_CLR(SIS_MII_CLK);
+				DELAY(1);
+				SIO_SET(SIS_MII_CLK);
+				DELAY(1);
+			}
+		} else {
+			for (i = 0x8000; i; i >>= 1) {
+				SIO_CLR(SIS_MII_CLK);
+				DELAY(1);
+				if (bus_space_read_4(sc->sc_st,sc->sc_sh,SIS_EECTL) & SIS_MII_DATA)
+					mii_data |= i;
+				SIO_SET(SIS_MII_CLK);
+				DELAY(1);
+			}
+		}
+
+		SIO_CLR(SIS_MII_CLK);
+		DELAY(1);
+		SIO_SET(SIS_MII_CLK);
+		DELAY(1);
+
+		splx(s);
+		return mii_data;
+	}	
+
+	/*
+	 * The SiS 900 before revision 635 has only an internal PHY
+	 * on the MII.  Only allow MII address 0.
 	 */
 	if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
 	    sc->sc_rev < SIS_REV_635 && phy != 0)
@@ -3082,8 +3194,39 @@
 	u_int32_t enphy;
 
 	/*
-	 * The SiS 900 has only an internal PHY on the MII.  Only allow
-	 * MII address 0.
+	 * The SiS 900 revision 635 and later uses a different interface.
+	 */
+	if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
+	    sc->sc_rev >= SIS_REV_635 && phy != 0) {
+		int s;
+
+		s = splnet();
+
+		/* Turn on data output. */
+		SIO_SET(SIS_MII_DIR);
+		sis900_mii_sync(sc);
+		sis900_mii_send(sc,SIS_MII_STARTDELIM,2);
+		sis900_mii_send(sc,SIS_MII_WRITEOP,2);
+		sis900_mii_send(sc,phy,5);
+		sis900_mii_send(sc,reg,5);
+		sis900_mii_send(sc,SIS_MII_TURNAROUND,2);
+		sis900_mii_send(sc,val,16);
+
+		/* Idle bit. */
+		SIO_SET(SIS_MII_CLK);
+		DELAY(1);
+		SIO_CLR(SIS_MII_CLK);
+		DELAY(1);
+
+		/* Turn off xmit. */
+		SIO_CLR(SIS_MII_DIR);
+
+		splx(s);
+	}
+
+	/*
+	 * The SiS 900 before revision 635 has only an internal PHY
+	 * on the MII.  Only allow MII address 0.
 	 */
 	if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
 	    sc->sc_rev < SIS_REV_635 && phy != 0)
--- if_sipreg.h.orig	2002-06-30 20:04:12.000000000 +0200
+++ if_sipreg.h	2003-08-13 00:13:35.000000000 +0200
@@ -596,6 +596,17 @@
 #ifndef DP83820
 #define	SIP_NS_PHY(miireg)	/* PHY registers (83815) */		\
 	(0x80 + ((miireg) << 2))
+
+#define SIS_EECTL		0x08
+
+#define SIS_MII_CLK		0x00000040
+#define SIS_MII_DIR		0x00000020
+#define SIS_MII_DATA		0x00000010
+
+#define SIS_MII_STARTDELIM	0x01
+#define SIS_MII_READOP		0x02
+#define SIS_MII_WRITEOP		0x01
+#define SIS_MII_TURNAROUND	0x02
 #endif
 
 #ifdef DP83820
>Release-Note:
>Audit-Trail:
>Unformatted: