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