Subject: ASIX AX88140 support under tlp(4)
To: None <tech-net@NetBSD.org>
From: Rui Paulo <rpaulo@NetBSD.org>
List: tech-net
Date: 06/23/2005 19:03:45
Hi,
I'm trying to finish a project I started long time ago: adding support
for AX88140 chipsets in the tlp(4) device driver. I checked the
dc(4) driver from FreeBSD and now the NIC is working (almost) properly.
I also did some benchmarks in which the card is doing more than 11MB/s.
Multicast also works.

I have some problems still pending:
	* tlp0: transmit process failed to idle: state RUNNING - WAIT
	  I have no idea why this is happening and I think CSR STATUS
	  bits are the same as 21143.
	* I notice a delay when doing MII media switching like going
	  from '10BaseT' to 'auto', what could be causing this ?
	* I don't have an AX88140 NIC with a PHY so I didn't added
	  any code for PHY handling. The FreeBSD driver doesn't support
	  this either because the author has never saw one..
	* I also don't have an AX88141 so I can't code any support for it
	  (no datasheet either but I think ASIX gives that freely)

The patch is below, I would be glad if someone could point me out
some solution to the problems.

Thanks!

Index: tulip.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/tulip.c,v
retrieving revision 1.136
diff -u -r1.136 tulip.c
--- tulip.c	23 Mar 2005 13:24:47 -0000	1.136
+++ tulip.c	23 Jun 2005 18:51:29 -0000
@@ -116,6 +116,7 @@
 void	tlp_filter_setup(struct tulip_softc *);
 void	tlp_winb_filter_setup(struct tulip_softc *);
 void	tlp_al981_filter_setup(struct tulip_softc *);
+void	tlp_asix_filter_setup(struct tulip_softc *);
 
 void	tlp_rxintr(struct tulip_softc *);
 void	tlp_txintr(struct tulip_softc *);
@@ -141,6 +142,7 @@
 void	tlp_2114x_mii_preinit(struct tulip_softc *);
 void	tlp_pnic_preinit(struct tulip_softc *);
 void	tlp_dm9102_preinit(struct tulip_softc *);
+void	tlp_asix_preinit(struct tulip_softc *);
 
 void	tlp_21140_reset(struct tulip_softc *);
 void	tlp_21142_reset(struct tulip_softc *);
@@ -241,6 +243,10 @@
 		sc->sc_filter_setup = tlp_al981_filter_setup;
 		break;
 
+	case TULIP_CHIP_AX88140:
+		sc->sc_filter_setup = tlp_asix_filter_setup;
+		break;
+
 	default:
 		sc->sc_filter_setup = tlp_filter_setup;
 		break;
@@ -360,6 +366,12 @@
 		sc->sc_txthresh = TXTH_DM9102_SF;
 		break;
 
+	case TULIP_CHIP_AX88140:
+		sc->sc_tdctl_ch = 0;
+		sc->sc_tdctl_er = TDCTL_ER;
+		sc->sc_preinit = tlp_asix_preinit;
+		break;
+
 	default:
 		/*
 		 * Default to running in ring mode.
@@ -434,6 +446,7 @@
 	case TULIP_CHIP_X3201_3:
 	case TULIP_CHIP_DM9102:
 	case TULIP_CHIP_DM9102A:
+	case TULIP_CHIP_AX88140:
 		sc->sc_ntxsegs = 1;
 		break;
 
@@ -1579,11 +1592,13 @@
 	TULIP_WRITE(sc, CSR_BUSMODE, BUSMODE_SWR);
 
 	/*
-	 * Xircom clone doesn't bring itself out of reset automatically.
+	 * Xircom and ASIX clones don't bring themselves out of 
+	 * reset automatically.
 	 * Instead, we have to wait at least 50 PCI cycles, and then
 	 * clear SWR.
 	 */
-	if (sc->sc_chip == TULIP_CHIP_X3201_3) {
+	if (sc->sc_chip == TULIP_CHIP_X3201_3 ||
+	    sc->sc_chip == TULIP_CHIP_AX88140) {
 		delay(10);
 		TULIP_WRITE(sc, CSR_BUSMODE, 0);
 	}
@@ -1684,6 +1699,11 @@
 		if (sc->sc_maxburst == 0)
 			sc->sc_maxburst = 16;
 		break;
+	
+	case TULIP_CHIP_AX88140:
+		if (sc->sc_maxburst == 0)
+			sc->sc_maxburst = 16;
+		break;
 
 	default:
 		/* Nothing. */
@@ -1915,6 +1935,25 @@
 		reg = enaddr[4] |
 		      (enaddr[5] << 8);
 		bus_space_write_4(sc->sc_st, sc->sc_sh, CSR_ADM_PAR1, reg);
+		break;
+	    }
+
+	case TULIP_CHIP_AX88140:
+	    {
+		u_int32_t reg;
+		u_int8_t *enaddr = LLADDR(ifp->if_sadl);
+		    
+		reg = enaddr[0] |
+		      (enaddr[1] << 8) |
+		      (enaddr[2] << 16) |
+		      (enaddr[3] << 24);
+		TULIP_WRITE(sc, CSR_AX_FILTIDX, AX_FILTIDX_PAR0);
+		TULIP_WRITE(sc, CSR_AX_FILTDATA, reg);
+
+		reg = enaddr[4] | (enaddr[5] << 8);
+		TULIP_WRITE(sc, CSR_AX_FILTIDX, AX_FILTIDX_PAR1);
+		TULIP_WRITE(sc, CSR_AX_FILTDATA, reg);
+		break;
 	    }
 
 	default:
@@ -2980,6 +3019,78 @@
 }
 
 /*
+ * tlp_asix_filter_setup:
+ * 
+ * 	Set the ASIX AX88140x recieve filter.
+ */
+void
+tlp_asix_filter_setup(sc)
+	struct tulip_softc *sc;
+{
+	struct ethercom *ec = &sc->sc_ethercom;
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	struct ether_multi *enm;
+	struct ether_multistep step;
+	u_int32_t hash, mchash[2];
+
+	DPRINTF(sc, ("%s: tlp_asix_filter_setup: sc_flags 0x%08x\n",
+		sc->sc_dev.dv_xname, sc->sc_flags));
+
+	sc->sc_opmode &= ~(OPMODE_PM|OPMODE_AX_RB|OPMODE_PR);
+
+	if (ifp->if_flags & IFF_MULTICAST)
+		sc->sc_opmode |= OPMODE_PM;
+
+	if (ifp->if_flags & IFF_BROADCAST)
+		sc->sc_opmode |= OPMODE_AX_RB;
+
+	if (ifp->if_flags & IFF_PROMISC) {
+		sc->sc_opmode |= OPMODE_PR;
+		goto allmulti;
+	}
+
+	mchash[0] = mchash[1] = 0;
+
+	ETHER_FIRST_MULTI(step, ec, enm);
+	while (enm != NULL) {
+		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
+			/*
+			 * We must listen to a range of multicast addresses.
+			 * For now, just accept all multicasts, rather than
+			 * trying to set only those filter bits needed to match
+			 * the range.  (At this time, the only use of address
+			 * ranges is for IP multicast routing, for which the
+			 * range is big enough to require all bits set.)
+			 */
+			goto allmulti;
+		}
+		hash = (ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26) 
+		       & 0x3f;
+		if (hash < 32)
+			mchash[0] |= (1 << hash);
+		else
+			mchash[1] |= (1 << (hash - 32));
+		ETHER_NEXT_MULTI(step, enm);
+	}
+	ifp->if_flags &= ~IFF_ALLMULTI;
+	goto setit;
+
+allmulti:
+	ifp->if_flags |= IFF_ALLMULTI;
+	mchash[0] = mchash[1] = 0xffffffff;
+
+setit:
+	TULIP_WRITE(sc, CSR_AX_FILTIDX, AX_FILTIDX_MAR0);
+	TULIP_WRITE(sc, CSR_AX_FILTDATA, mchash[0]);
+	TULIP_WRITE(sc, CSR_AX_FILTIDX, AX_FILTIDX_MAR1);
+	TULIP_WRITE(sc, CSR_AX_FILTDATA, mchash[1]);
+	TULIP_WRITE(sc, CSR_OPMODE, sc->sc_opmode);
+	DPRINTF(sc, ("%s: tlp_asix_filter_setup: returning\n",
+		sc->sc_dev.dv_xname));
+}
+
+
+/*
  * tlp_idle:
  *
  *	Cause the transmit and/or receive processes to go idle.
@@ -3544,6 +3655,29 @@
 }
 
 /*
+ * tlp_asix_preinit:
+ * 
+ * 	Pre-init function for the ASIX chipsets.
+ */
+void
+tlp_asix_preinit(sc)
+	struct tulip_softc *sc;
+{
+
+	switch (sc->sc_chip) {
+		case TULIP_CHIP_AX88140:
+			/* XXX Handle PHY. */
+			sc->sc_opmode |= OPMODE_HBD|OPMODE_PS; 
+			break;
+		default:
+			/* Nothing */
+			break;
+	}
+
+	TULIP_WRITE(sc, CSR_OPMODE, sc->sc_opmode);
+}
+
+/*
  * tlp_dm9102_preinit:
  *
  *	Pre-init function for the Davicom DM9102.
@@ -6095,3 +6229,82 @@
 	/* XXX HomePNA on DM9102A. */
 	return (tlp_mii_setmedia(sc));
 }
+
+/*
+ * ASIX AX88140x media switch. Internal PHY or MII.
+ */
+
+void	tlp_asix_tmsw_init(struct tulip_softc *);
+void	tlp_asix_tmsw_getmedia(struct tulip_softc *, struct ifmediareq *);
+int	tlp_asix_tmsw_setmedia(struct tulip_softc *);
+
+const struct tulip_mediasw tlp_asix_mediasw = {
+	tlp_asix_tmsw_init, tlp_asix_tmsw_getmedia,
+	tlp_asix_tmsw_setmedia
+};
+
+void
+tlp_asix_tmsw_init(sc)
+	struct tulip_softc *sc;
+{
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	u_int32_t opmode;
+
+	sc->sc_mii.mii_ifp = ifp;
+        sc->sc_mii.mii_readreg = tlp_bitbang_mii_readreg;
+        sc->sc_mii.mii_writereg = tlp_bitbang_mii_writereg;
+	sc->sc_mii.mii_statchg = sc->sc_statchg;
+	ifmedia_init(&sc->sc_mii.mii_media, 0, tlp_mediachange,
+            tlp_mediastatus);
+
+	/*
+	 * Configure OPMODE properly for the internal MII interface.
+	 */
+	switch (sc->sc_chip) {
+	case TULIP_CHIP_AX88140:
+		opmode = OPMODE_HBD|OPMODE_PS;
+		break;
+        default:
+                opmode = 0;
+                break;
+        }
+
+	TULIP_WRITE(sc, CSR_OPMODE, opmode);
+
+	/* Now, probe the internal MII for the internal PHY. */
+	mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
+	    MII_OFFSET_ANY, 0);
+
+	/* XXX Figure how to handle the PHY. */
+	
+	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
+		ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
+		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE);
+	} else {
+		sc->sc_flags |= TULIPF_HAS_MII;
+		sc->sc_tick = tlp_mii_tick;
+		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
+	}
+
+	
+}
+
+void
+tlp_asix_tmsw_getmedia(sc, ifmr)
+	struct tulip_softc *sc;
+	struct ifmediareq *ifmr;
+{
+	
+	/* XXX PHY handling. */
+	tlp_mii_getmedia(sc, ifmr);
+}
+
+int
+tlp_asix_tmsw_setmedia(sc)
+	struct tulip_softc *sc;
+{
+	
+	/* XXX PHY handling. */
+	return (tlp_mii_setmedia(sc));
+}
+
Index: tulipreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/tulipreg.h,v
retrieving revision 1.30
diff -u -r1.30 tulipreg.h
--- tulipreg.h	27 Feb 2005 00:27:03 -0000	1.30
+++ tulipreg.h	23 Jun 2005 18:51:29 -0000
@@ -139,10 +139,19 @@
  *		- There seems to be an interrupt logic bug, requiring
  *		  that interrupts be disabled on the chip during the
  *		  interrupt handler.
+ *	
+ *	- ASIX AX88140
+ *	
+ *	  21433 clone with a few differences:
+ *
+ *	  	- Specific broadcast bit in the OPMODE register.
+ *	  	- Transmit buffer must be 32-bit aligned.
+ *	  	- The BUSMODE_SWR bit is not self-clearing.
+ *	  	- External 10BaseT PHY or 10/100 MII.
  *
  * Some of the clone chips have different registers, and some have
  * different bits in the same registers.  These will be denoted by
- * PMAC, PNICII, PNIC, DM, WINB, and ADM in the register/bit names.
+ * PMAC, PNICII, PNIC, DM, WINB, ADM and AX in the register/bit names.
  */
 
 /*
@@ -437,6 +446,7 @@
 		 *	Winbond 89C040F
 		 *	Xircom X3201-3
 		 *	Davicom DM9102 (buggy BUSMODE register)
+		 *	ASIX AX88140
 		 */
 #define	BUSMODE_TAP_NONE	0x00000000	/*     no auto-polling */
 #define	BUSMODE_TAP_200us	0x00020000	/*   200 uS */
@@ -585,6 +595,7 @@
 #define	OPMODE_PM		0x00000080	/* pass all multicast */
 #define	OPMODE_WINB_AEP		0x00000080	/* accept error packet */
 #define	OPMODE_FKD		0x00000100	/* flaky oscillator disable */
+#define OPMODE_AX_RB		0x00000100	/* recieve broadcast packets */
 #define	OPMODE_FD		0x00000200	/* full-duplex mode */
 #define	OPMODE_OM		0x00000c00	/* operating mode */
 #define	OPMODE_OM_NORMAL	0x00000000	/*     normal mode */
@@ -1539,4 +1550,20 @@
 #define	CSR_DM_SFDR		TULIP_CSR14
 	/* See 21143 SIAGEN register */
 
+/*
+ * ASIX AX8814x registers.
+ */
+
+/* CSR13 - Filtering Index */
+#define CSR_AX_FILTIDX		TULIP_CSR13
+
+/* CSR14 - Filtering data */
+#define CSR_AX_FILTDATA		TULIP_CSR14
+
+/* Filtering Index values */
+#define AX_FILTIDX_PAR0		0x00000000
+#define AX_FILTIDX_PAR1		0x00000001
+#define AX_FILTIDX_MAR0		0x00000002
+#define AX_FILTIDX_MAR1		0x00000003
+
 #endif /* _DEV_IC_TULIPREG_H_ */
Index: tulipvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/tulipvar.h,v
retrieving revision 1.54
diff -u -r1.54 tulipvar.h
--- tulipvar.h	27 Feb 2005 00:27:03 -0000	1.54
+++ tulipvar.h	23 Jun 2005 18:51:30 -0000
@@ -595,6 +595,7 @@
 extern const struct tulip_mediasw tlp_al981_mediasw;
 extern const struct tulip_mediasw tlp_an985_mediasw;
 extern const struct tulip_mediasw tlp_dm9102_mediasw;
+extern const struct tulip_mediasw tlp_asix_mediasw;
 
 void	tlp_attach(struct tulip_softc *, const u_int8_t *);
 int	tlp_activate(struct device *, enum devact);
Index: ../pci/if_tlp_pci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_tlp_pci.c,v
retrieving revision 1.79
diff -u -r1.79 if_tlp_pci.c
--- ../pci/if_tlp_pci.c	13 Jun 2005 16:37:38 -0000	1.79
+++ ../pci/if_tlp_pci.c	23 Jun 2005 18:51:30 -0000
@@ -169,10 +169,8 @@
 	{ PCI_VENDOR_3COM,		PCI_PRODUCT_3COM_3C910SOHOB,
 	  TULIP_CHIP_AN985 },
 
-#if 0
 	{ PCI_VENDOR_ASIX,		PCI_PRODUCT_ASIX_AX88140A,
 	  TULIP_CHIP_AX88140 },
-#endif
 
 	{ 0,				0,
 	  TULIP_CHIP_INVALID },
@@ -486,6 +484,7 @@
 	case TULIP_CHIP_MX98725:
 	case TULIP_CHIP_DM9102:
 	case TULIP_CHIP_DM9102A:
+	case TULIP_CHIP_AX88140:
 		/*
 		 * Clear the "sleep mode" bit in the CFDA register.
 		 */
@@ -930,6 +929,20 @@
 		sc->sc_mediasw = &tlp_dm9102_mediasw;
 		break;
 
+	case TULIP_CHIP_AX88140:
+		/*
+		 * ASIX AX88140 Ethernet Address is located at offset
+		 * 20 of the SROM.
+		 */
+		memcpy(enaddr, &sc->sc_srom[20], ETHER_ADDR_LEN);
+
+		/*
+		 * ASIX AX88140 chip can have a built-in PHY or
+		 * an external MII interface.
+		 */
+		sc->sc_mediasw = &tlp_asix_mediasw;
+		break;
+
 	default:
  cant_cope:
 		printf("%s: sorry, unable to handle your board\n",

		-- Rui Paulo