summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjon <jon@FreeBSD.org>2000-10-19 08:34:32 +0000
committerjon <jon@FreeBSD.org>2000-10-19 08:34:32 +0000
commit4580b26b321a7db757880cf45873ce30aa10fd27 (patch)
treecff7f58d8da222e806bc63d80206f2c95b70295c
parente3c86757f4279913a9a3eaa797fdcc8c2b9dba4f (diff)
downloadFreeBSD-src-4580b26b321a7db757880cf45873ce30aa10fd27.zip
FreeBSD-src-4580b26b321a7db757880cf45873ce30aa10fd27.tar.gz
NEWCARD/Cardbus -
This commit adds support for Xircom X3201 based cardbus cards. Support for the TDK 78Q2120 MII is also added. IBM Etherjet, Intel and Xircom cards uses these chips. Note that as a result of this commit, some Intel/DEC 21143 based cardbus cards will also attach, but not get link. That is being looked at.
-rw-r--r--sys/conf/files1
-rw-r--r--sys/dev/dc/if_dc.c183
-rw-r--r--sys/dev/dc/if_dcreg.h33
-rw-r--r--sys/dev/mii/tdkphy.c362
-rw-r--r--sys/dev/mii/tdkphyreg.h82
-rw-r--r--sys/pci/if_dc.c183
-rw-r--r--sys/pci/if_dcreg.h33
7 files changed, 859 insertions, 18 deletions
diff --git a/sys/conf/files b/sys/conf/files
index baec5aa..a88e292 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -229,6 +229,7 @@ dev/mii/nsphy.c optional miibus
dev/mii/pnphy.c optional miibus
dev/mii/pnaphy.c optional miibus
dev/mii/rlphy.c optional miibus
+dev/mii/tdkphy.c optional miibus
dev/mii/tlphy.c optional miibus
dev/mii/ukphy.c optional miibus
dev/mii/ukphy_subr.c optional miibus
diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c
index 6a437a4..84538e4 100644
--- a/sys/dev/dc/if_dc.c
+++ b/sys/dev/dc/if_dc.c
@@ -45,6 +45,7 @@
* ADMtek AN985 (www.admtek.com.tw)
* Davicom DM9100, DM9102, DM9102A (www.davicom8.com)
* Accton EN1217 (www.accton.com)
+ * Xircom X3201 (www.xircom.com)
*
* Datasheets for the 21143 are available at developer.intel.com.
* Datasheets for the clone parts can be found at their respective sites.
@@ -179,6 +180,8 @@ static struct dc_type dc_devs[] = {
"82c169 PNIC 10/100BaseTX" },
{ DC_VENDORID_ACCTON, DC_DEVICEID_EN1217,
"Accton EN1217 10/100BaseTX" },
+ { DC_VENDORID_XIRCOM, DC_DEVICEID_X3201,
+ "Xircom X3201 10/100BaseTX" },
{ 0, 0, NULL }
};
@@ -212,6 +215,8 @@ static void dc_eeprom_putbyte __P((struct dc_softc *, int));
static void dc_eeprom_getword __P((struct dc_softc *, int, u_int16_t *));
static void dc_eeprom_getword_pnic
__P((struct dc_softc *, int, u_int16_t *));
+static void dc_eeprom_getword_xircom
+ __P((struct dc_softc *, int, u_int16_t *));
static void dc_read_eeprom __P((struct dc_softc *, caddr_t, int,
int, int));
@@ -232,6 +237,7 @@ static u_int32_t dc_crc_be __P((caddr_t));
static void dc_setfilt_21143 __P((struct dc_softc *));
static void dc_setfilt_asix __P((struct dc_softc *));
static void dc_setfilt_admtek __P((struct dc_softc *));
+static void dc_setfilt_xircom __P((struct dc_softc *));
static void dc_setfilt __P((struct dc_softc *));
@@ -284,6 +290,7 @@ static driver_t dc_driver = {
static devclass_t dc_devclass;
+DRIVER_MODULE(if_dc, cardbus, dc_driver, dc_devclass, 0, 0);
DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0);
DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
@@ -402,6 +409,29 @@ static void dc_eeprom_getword_pnic(sc, addr, dest)
/*
* Read a word of data stored in the EEPROM at address 'addr.'
+ * The Xircom X3201 has its own non-standard way to read
+ * the EEPROM, too.
+ */
+static void dc_eeprom_getword_xircom(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
+
+ addr *= 2;
+ CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
+ *dest = (u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff;
+ addr += 1;
+ CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
+ *dest |= ((u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff) << 8;
+
+ SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
*/
static void dc_eeprom_getword(sc, addr, dest)
struct dc_softc *sc;
@@ -466,6 +496,8 @@ static void dc_read_eeprom(sc, dest, off, cnt, swap)
for (i = 0; i < cnt; i++) {
if (DC_IS_PNIC(sc))
dc_eeprom_getword_pnic(sc, off + i, &word);
+ else if (DC_IS_XIRCOM(sc))
+ dc_eeprom_getword_xircom(sc, off + i, &word);
else
dc_eeprom_getword(sc, off + i, &word);
ptr = (u_int16_t *)(dest + (i * 2));
@@ -922,6 +954,15 @@ static u_int32_t dc_crc_le(sc, addr)
if (sc->dc_flags & DC_64BIT_HASH)
return (crc & ((1 << DC_BITS_64) - 1));
+ /* Xircom's hash filtering table is different (read: weird) */
+ /* Xircom uses the LEAST significant bits */
+ if (DC_IS_XIRCOM(sc)) {
+ if ((crc & 0x180) == 0x180)
+ return (crc & 0x0F) + (crc & 0x70)*3 + (14 << 4);
+ else
+ return (crc & 0x1F) + ((crc>>1) & 0xF0)*3 + (12 << 4);
+ }
+
return (crc & ((1 << DC_BITS_512) - 1));
}
@@ -1158,6 +1199,76 @@ void dc_setfilt_asix(sc)
return;
}
+void dc_setfilt_xircom(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *sframe;
+ u_int32_t h, *sp;
+ struct ifmultiaddr *ifma;
+ struct ifnet *ifp;
+ int i;
+
+ ifp = &sc->arpcom.ac_if;
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON));
+
+ i = sc->dc_cdata.dc_tx_prod;
+ DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT);
+ sc->dc_cdata.dc_tx_cnt++;
+ sframe = &sc->dc_ldata->dc_tx_list[i];
+ sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf;
+ bzero((char *)sp, DC_SFRAME_LEN);
+
+ sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf);
+ sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK |
+ DC_FILTER_HASHPERF | DC_TXCTL_FINT;
+
+ sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = dc_crc_le(sc, (caddr_t)&etherbroadcastaddr);
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ /* Set our MAC address */
+ sp[0] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0];
+ sp[1] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1];
+ sp[2] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2];
+
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
+ ifp->if_flags |= IFF_RUNNING;
+ sframe->dc_status = DC_TXSTAT_OWN;
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * wait some time...
+ */
+ DELAY(1000);
+
+ ifp->if_timer = 5;
+
+ return;
+}
+
static void dc_setfilt(sc)
struct dc_softc *sc;
{
@@ -1171,7 +1282,10 @@ static void dc_setfilt(sc)
if (DC_IS_ADMTEK(sc))
dc_setfilt_admtek(sc);
- return;
+ if (DC_IS_XIRCOM(sc))
+ dc_setfilt_xircom(sc);
+
+ return;
}
/*
@@ -1338,7 +1452,7 @@ static void dc_reset(sc)
break;
}
- if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc)) {
+ if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) {
DELAY(10000);
DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
i = 0;
@@ -1788,6 +1902,14 @@ static int dc_attach(dev)
sc->dc_flags |= DC_REDUCED_MII_POLL;
sc->dc_pmode = DC_PMODE_MII;
break;
+ case DC_DEVICEID_X3201:
+ sc->dc_type = DC_TYPE_XIRCOM;
+ sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE;
+ /*
+ * We don't actually need to coalesce, but we're doing
+ * it to obtain a double word aligned buffer.
+ */
+ break;
default:
printf("dc%d: unknown device: %x\n", sc->dc_unit,
sc->dc_info->dc_did);
@@ -1805,7 +1927,7 @@ static int dc_attach(dev)
dc_reset(sc);
/* Take 21143 out of snooze mode */
- if (DC_IS_INTEL(sc)) {
+ if (DC_IS_INTEL(sc) || DC_IS_XIRCOM(sc)) {
command = pci_read_config(dev, DC_PCI_CFDD, 4);
command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
pci_write_config(dev, DC_PCI_CFDD, command, 4);
@@ -1852,6 +1974,9 @@ static int dc_attach(dev)
case DC_TYPE_AN985:
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0);
break;
+ case DC_TYPE_XIRCOM:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, 3, 3, 0);
+ break;
default:
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
break;
@@ -1883,6 +2008,7 @@ static int dc_attach(dev)
ifp->if_softc = sc;
ifp->if_unit = unit;
ifp->if_name = "dc";
+ /* XXX: bleah, MTU gets overwritten in ether_ifattach() */
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = dc_ioctl;
@@ -1937,6 +2063,19 @@ static int dc_attach(dev)
goto fail;
}
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * setup General Purpose Port mode and data so the tulip
+ * can talk to the MII.
+ */
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ }
+
/*
* Call MI attach routine.
*/
@@ -2420,11 +2559,23 @@ static void dc_txeof(sc)
continue;
}
- if (/*sc->dc_type == DC_TYPE_21143 &&*/
- sc->dc_pmode == DC_PMODE_MII &&
- ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
- DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
- txstat &= ~DC_TXSTAT_ERRSUM;
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * XXX: Why does my Xircom taunt me so?
+ * For some reason it likes setting the CARRLOST flag
+ * even when the carrier is there. wtf?!? */
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ } else {
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ }
if (txstat & DC_TXSTAT_ERRSUM) {
ifp->if_oerrors++;
@@ -2557,7 +2708,8 @@ static void dc_intr(arg)
/* Disable interrupts. */
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
- while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) {
+ while(((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS)
+ && status != 0xFFFFFFFF) {
CSR_WRITE_4(sc, DC_ISR, status);
@@ -2883,6 +3035,19 @@ static void dc_init(xsc)
DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715);
}
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * setup General Purpose Port mode and data so the tulip
+ * can talk to the MII.
+ */
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ }
+
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_72BYTES);
diff --git a/sys/dev/dc/if_dcreg.h b/sys/dev/dc/if_dcreg.h
index e72d8b4..f36d6b3 100644
--- a/sys/dev/dc/if_dcreg.h
+++ b/sys/dev/dc/if_dcreg.h
@@ -52,6 +52,7 @@
#define DC_SIARESET 0x68 /* SIA connectivity */
#define DC_10BTCTRL 0x70 /* SIA transmit and receive */
#define DC_WATCHDOG 0x78 /* SIA and general purpose port */
+#define DC_SIAGP 0x78 /* SIA and general purpose port (X3201) */
/*
* There are two general 'types' of MX chips that we need to be
@@ -75,6 +76,7 @@
#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */
#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */
#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */
+#define DC_TYPE_XIRCOM 0xB /* Xircom X3201 */
#define DC_IS_MACRONIX(x) \
(x->dc_type == DC_TYPE_98713 || \
@@ -92,6 +94,7 @@
#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102)
#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII)
#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC)
+#define DC_IS_XIRCOM(x) (x->dc_type == DC_TYPE_XIRCOM)
/* MII/symbol mode port types */
#define DC_PMODE_MII 0x1
@@ -349,6 +352,25 @@
#define DC_WDOG_CTLWREN 0x08000000
/*
+ * SIA and General Purpose Port register (X3201)
+ */
+#define DC_SIAGP_RXMATCH 0x40000000
+#define DC_SIAGP_INT1 0x20000000
+#define DC_SIAGP_INT0 0x10000000
+#define DC_SIAGP_WRITE_EN 0x08000000
+#define DC_SIAGP_RXMATCH_EN 0x04000000
+#define DC_SIAGP_INT1_EN 0x02000000
+#define DC_SIAGP_INT0_EN 0x01000000
+#define DC_SIAGP_LED3 0x00800000
+#define DC_SIAGP_LED2 0x00400000
+#define DC_SIAGP_LED1 0x00200000
+#define DC_SIAGP_LED0 0x00100000
+#define DC_SIAGP_MD_GP3_OUTPUT 0x00080000
+#define DC_SIAGP_MD_GP2_OUTPUT 0x00040000
+#define DC_SIAGP_MD_GP1_OUTPUT 0x00020000
+#define DC_SIAGP_MD_GP0_OUTPUT 0x00010000
+
+/*
* Size of a setup frame.
*/
#define DC_SFRAME_LEN 192
@@ -834,6 +856,17 @@ struct dc_softc {
#define DC_DEVICEID_EN1217 0x1217
/*
+ * Xircom vendor ID
+ */
+#define DC_VENDORID_XIRCOM 0x115d
+
+/*
+ * Xircom device IDs.
+ */
+#define DC_DEVICEID_X3201 0x0003
+
+
+/*
* PCI low memory base and low I/O base register, and
* other PCI registers.
*/
diff --git a/sys/dev/mii/tdkphy.c b/sys/dev/mii/tdkphy.c
new file mode 100644
index 0000000..e0cc5d8
--- /dev/null
+++ b/sys/dev/mii/tdkphy.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2000,2001 Jonathan Chen.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Driver for the TDK 78Q2120 MII
+ *
+ * References:
+ * Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf
+ * Most of this code stolen from ukphy.c
+ */
+
+/*
+ * The TDK 78Q2120 is found on some Xircom X3201 based cardbus cards. It's just
+ * like any other normal phy, except it does auto negotiation in a different
+ * way.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <machine/clock.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mii/miidevs.h>
+
+#include <dev/mii/tdkphyreg.h>
+
+#include "miibus_if.h"
+
+#if !defined(lint)
+static const char rcsid[] =
+ "$Id: tdkphy.c,v 1.3 2000/10/14 06:20:56 jon Exp $";
+#endif
+
+static int tdkphy_probe __P((device_t));
+static int tdkphy_attach __P((device_t));
+static int tdkphy_detach __P((device_t));
+
+static device_method_t tdkphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, tdkphy_probe),
+ DEVMETHOD(device_attach, tdkphy_attach),
+ DEVMETHOD(device_detach, tdkphy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ { 0, 0 }
+};
+
+static devclass_t tdkphy_devclass;
+
+static driver_t tdkphy_driver = {
+ "tdkphy",
+ tdkphy_methods,
+ sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0);
+
+int tdkphy_service __P((struct mii_softc *, struct mii_data *, int));
+void tdkphy_status __P((struct mii_softc *));
+
+static int
+tdkphy_probe(device_t dev)
+{
+ struct mii_attach_args *ma;
+ ma = device_get_ivars(dev);
+ if ((MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_TDK ||
+ MII_MODEL(ma->mii_id2) != MII_MODEL_TDK_78Q2120))
+ return (ENXIO);
+
+ device_set_desc(dev, MII_STR_TDK_78Q2120);
+ return (0);
+}
+
+static int
+tdkphy_attach(device_t dev)
+{
+ struct mii_softc *sc;
+ struct mii_attach_args *ma;
+ struct mii_data *mii;
+ sc = device_get_softc(dev);
+ ma = device_get_ivars(dev);
+ sc->mii_dev = device_get_parent(dev);
+ mii = device_get_softc(sc->mii_dev);
+ LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
+
+ if (bootverbose)
+ device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
+ MII_OUI(ma->mii_id1, ma->mii_id2),
+ MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
+
+ sc->mii_inst = mii->mii_instance;
+ sc->mii_phy = ma->mii_phyno;
+ sc->mii_service = tdkphy_service;
+ sc->mii_pdata = mii;
+
+ mii->mii_instance++;
+
+ sc->mii_flags |= MIIF_NOISOLATE;
+
+#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
+
+ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
+ BMCR_ISO);
+#if 0
+ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
+ BMCR_LOOP|BMCR_S100);
+#endif
+
+ mii_phy_reset(sc);
+
+ sc->mii_capabilities =
+ PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+ device_printf(dev, " ");
+ if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
+ printf("no media present");
+ else
+ mii_add_media(mii, sc->mii_capabilities,
+ sc->mii_inst);
+ printf("\n");
+#undef ADD
+
+ MIIBUS_MEDIAINIT(sc->mii_dev);
+
+ return(0);
+}
+
+static int tdkphy_detach(device_t dev)
+{
+ struct mii_softc *sc;
+ struct mii_data *mii;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(device_get_parent(dev));
+ sc->mii_dev = NULL;
+ LIST_REMOVE(sc, mii_list);
+
+ return(0);
+}
+
+int
+tdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int reg;
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ /*
+ * If we're not polling our PHY instance, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+ break;
+
+ case MII_MEDIACHG:
+ /*
+ * If the media indicates a different PHY instance,
+ * isolate ourselves.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
+ reg = PHY_READ(sc, MII_BMCR);
+ PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
+ return (0);
+ }
+
+ /*
+ * If the interface is not up, don't do anything.
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ break;
+
+ switch (IFM_SUBTYPE(ife->ifm_media)) {
+ case IFM_AUTO:
+ /*
+ * If we're already in auto mode, just return.
+ */
+ if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
+ return (0);
+ (void) mii_phy_auto(sc, 1);
+ break;
+ case IFM_100_T4:
+ /*
+ * Not supported on MII
+ */
+ return (EINVAL);
+ default:
+ /*
+ * BMCR data is stored in the ifmedia entry.
+ */
+ PHY_WRITE(sc, MII_ANAR,
+ mii_anar(ife->ifm_media));
+ PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
+ }
+ break;
+
+ case MII_TICK:
+ /*
+ * If we're not currently selected, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+
+ /*
+ * Only used for autonegotiation.
+ */
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
+ return (0);
+
+ /*
+ * Is the interface even up?
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ return (0);
+
+ /*
+ * Check to see if we have link. If we do, we don't
+ * need to restart the autonegotiation process. Read
+ * the BMSR twice in case it's latched.
+ */
+ reg = PHY_READ(sc, MII_BMSR) |
+ PHY_READ(sc, MII_BMSR);
+ if (reg & BMSR_LINK)
+ return (0);
+
+ /*
+ * Only retry autonegotiation every 5 seconds.
+ */
+ if (++sc->mii_ticks != 5)
+ return (0);
+
+ sc->mii_ticks = 0;
+ mii_phy_reset(sc);
+ if (mii_phy_auto(sc, 0) == EJUSTRETURN)
+ return (0);
+ break;
+ }
+
+ /* Update the media status. */
+ tdkphy_status(sc);
+ if (sc->mii_pdata->mii_media_active & IFM_FDX)
+ PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX);
+ else
+ PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX);
+ /* Callback if something changed. */
+ if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
+ MIIBUS_STATCHG(sc->mii_dev);
+ sc->mii_active = mii->mii_media_active;
+ }
+ return (0);
+}
+
+void
+tdkphy_status(struct mii_softc *phy)
+{
+ struct mii_data *mii = phy->mii_pdata;
+ int bmsr, bmcr, anlpar, diag;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
+ if (bmsr & BMSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+
+ bmcr = PHY_READ(phy, MII_BMCR);
+ if (bmcr & BMCR_ISO) {
+ mii->mii_media_active |= IFM_NONE;
+ mii->mii_media_status = 0;
+ return;
+ }
+
+ if (bmcr & BMCR_LOOP)
+ mii->mii_media_active |= IFM_LOOP;
+
+ if (bmcr & BMCR_AUTOEN) {
+ /*
+ * NWay autonegotiation takes the highest-order common
+ * bit of the ANAR and ANLPAR (i.e. best media advertised
+ * both by us and our link partner).
+ */
+ if ((bmsr & BMSR_ACOMP) == 0) {
+ /* Erg, still trying, I guess... */
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+
+ anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR);
+ /*
+ * ANLPAR doesn't get set on my card, but we check it anyway,
+ * since it is mentioned in the 78Q2120 specs.
+ */
+ if (anlpar & ANLPAR_T4)
+ mii->mii_media_active |= IFM_100_T4;
+ else if (anlpar & ANLPAR_TX_FD)
+ mii->mii_media_active |= IFM_100_TX|IFM_FDX;
+ else if (anlpar & ANLPAR_TX)
+ mii->mii_media_active |= IFM_100_TX;
+ else if (anlpar & ANLPAR_10_FD)
+ mii->mii_media_active |= IFM_10_T|IFM_FDX;
+ else if (anlpar & ANLPAR_10)
+ mii->mii_media_active |= IFM_10_T;
+ else {
+ /*
+ * ANLPAR isn't set, which leaves two possibilities:
+ * 1) Auto negotiation failed
+ * 2) Auto negotiation completed, but the card forgot
+ * to set ANLPAR.
+ * So we check the MII_DIAG(18) register...
+ */
+ diag = PHY_READ(phy, MII_DIAG);
+ if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */
+ mii->mii_media_active |= IFM_10_T;
+ else {
+ if (diag & DIAG_DUPLEX)
+ mii->mii_media_active |= IFM_FDX;
+ if (diag & DIAG_RATE_100)
+ mii->mii_media_active |= IFM_100_TX;
+ else
+ mii->mii_media_active |= IFM_10_T;
+ }
+ }
+ } else {
+ mii->mii_media_active = mii_media_from_bmcr(bmcr);
+ }
+}
diff --git a/sys/dev/mii/tdkphyreg.h b/sys/dev/mii/tdkphyreg.h
new file mode 100644
index 0000000..1f10625
--- /dev/null
+++ b/sys/dev/mii/tdkphyreg.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2000,2001 Jonathan Chen.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for TDK 78Q2120
+ */
+
+#ifndef _DEV_MII_TDKPHYREG_H_
+#define _DEV_MII_TDKPHYREG_H_
+
+#define MII_VENDOR 16
+#define VENDOR_RXCC 0x0001
+#define VENDOR_PCSBP 0x0002
+#define VENDOR_RVSPOL 0x0010
+#define VENDOR_NOAPOL 0x0020
+#define VENDOR_GPIO0DIR 0x0040
+#define VENDOR_GPIO0DAT 0x0080
+#define VENDOR_GPIO1DIR 0x0100
+#define VENDOR_GPIO1DAT 0x0200
+#define VENDOR_10BTLOOP 0x0400
+#define VENDOR_NOSQE 0x0800
+#define VENDOR_TXHIM 0x1000
+#define VENDOR_INTLVL 0x4000
+#define VENDOR_RPTR 0x8000
+
+#define MII_INT 17
+#define INT_STAT_MASK 0x00ff
+#define INT_STAT_ACOMP 0x0001
+#define INT_STAT_RFAULT 0x0002
+#define INT_STAT_LSCHG 0x0004
+#define INT_STAT_LPACK 0x0008
+#define INT_STAT_PDF 0x0010
+#define INT_STAT_PRX 0x0020
+#define INT_STAT_RXERR 0x0040
+#define INT_STAT_JABBER 0x0080
+#define INT_CTRL_MASK 0xff00
+#define INT_CTRL_ACOMP 0x0100
+#define INT_CTRL_RFAULT 0x0200
+#define INT_CTRL_LSCHG 0x0400
+#define INT_CTRL_LPACK 0x0800
+#define INT_CTRL_PDF 0x1000
+#define INT_CTRL_PRX 0x2000
+#define INT_CTRL_RXERR 0x4000
+#define INT_CTRL_JABBER 0x8000
+
+
+#define MII_DIAG 18
+#define DIAG_RLOCK 0x0100
+#define DIAG_RPASS 0x0200
+#define DIAG_RATE_100 0x0400
+#define DIAG_DUPLEX 0x0800
+#define DIAG_NEGFAIL 0x1000
+
+
+#endif
diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c
index 6a437a4..84538e4 100644
--- a/sys/pci/if_dc.c
+++ b/sys/pci/if_dc.c
@@ -45,6 +45,7 @@
* ADMtek AN985 (www.admtek.com.tw)
* Davicom DM9100, DM9102, DM9102A (www.davicom8.com)
* Accton EN1217 (www.accton.com)
+ * Xircom X3201 (www.xircom.com)
*
* Datasheets for the 21143 are available at developer.intel.com.
* Datasheets for the clone parts can be found at their respective sites.
@@ -179,6 +180,8 @@ static struct dc_type dc_devs[] = {
"82c169 PNIC 10/100BaseTX" },
{ DC_VENDORID_ACCTON, DC_DEVICEID_EN1217,
"Accton EN1217 10/100BaseTX" },
+ { DC_VENDORID_XIRCOM, DC_DEVICEID_X3201,
+ "Xircom X3201 10/100BaseTX" },
{ 0, 0, NULL }
};
@@ -212,6 +215,8 @@ static void dc_eeprom_putbyte __P((struct dc_softc *, int));
static void dc_eeprom_getword __P((struct dc_softc *, int, u_int16_t *));
static void dc_eeprom_getword_pnic
__P((struct dc_softc *, int, u_int16_t *));
+static void dc_eeprom_getword_xircom
+ __P((struct dc_softc *, int, u_int16_t *));
static void dc_read_eeprom __P((struct dc_softc *, caddr_t, int,
int, int));
@@ -232,6 +237,7 @@ static u_int32_t dc_crc_be __P((caddr_t));
static void dc_setfilt_21143 __P((struct dc_softc *));
static void dc_setfilt_asix __P((struct dc_softc *));
static void dc_setfilt_admtek __P((struct dc_softc *));
+static void dc_setfilt_xircom __P((struct dc_softc *));
static void dc_setfilt __P((struct dc_softc *));
@@ -284,6 +290,7 @@ static driver_t dc_driver = {
static devclass_t dc_devclass;
+DRIVER_MODULE(if_dc, cardbus, dc_driver, dc_devclass, 0, 0);
DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0);
DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
@@ -402,6 +409,29 @@ static void dc_eeprom_getword_pnic(sc, addr, dest)
/*
* Read a word of data stored in the EEPROM at address 'addr.'
+ * The Xircom X3201 has its own non-standard way to read
+ * the EEPROM, too.
+ */
+static void dc_eeprom_getword_xircom(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
+
+ addr *= 2;
+ CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
+ *dest = (u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff;
+ addr += 1;
+ CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
+ *dest |= ((u_int16_t)CSR_READ_4(sc, DC_SIO)&0xff) << 8;
+
+ SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
*/
static void dc_eeprom_getword(sc, addr, dest)
struct dc_softc *sc;
@@ -466,6 +496,8 @@ static void dc_read_eeprom(sc, dest, off, cnt, swap)
for (i = 0; i < cnt; i++) {
if (DC_IS_PNIC(sc))
dc_eeprom_getword_pnic(sc, off + i, &word);
+ else if (DC_IS_XIRCOM(sc))
+ dc_eeprom_getword_xircom(sc, off + i, &word);
else
dc_eeprom_getword(sc, off + i, &word);
ptr = (u_int16_t *)(dest + (i * 2));
@@ -922,6 +954,15 @@ static u_int32_t dc_crc_le(sc, addr)
if (sc->dc_flags & DC_64BIT_HASH)
return (crc & ((1 << DC_BITS_64) - 1));
+ /* Xircom's hash filtering table is different (read: weird) */
+ /* Xircom uses the LEAST significant bits */
+ if (DC_IS_XIRCOM(sc)) {
+ if ((crc & 0x180) == 0x180)
+ return (crc & 0x0F) + (crc & 0x70)*3 + (14 << 4);
+ else
+ return (crc & 0x1F) + ((crc>>1) & 0xF0)*3 + (12 << 4);
+ }
+
return (crc & ((1 << DC_BITS_512) - 1));
}
@@ -1158,6 +1199,76 @@ void dc_setfilt_asix(sc)
return;
}
+void dc_setfilt_xircom(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *sframe;
+ u_int32_t h, *sp;
+ struct ifmultiaddr *ifma;
+ struct ifnet *ifp;
+ int i;
+
+ ifp = &sc->arpcom.ac_if;
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON));
+
+ i = sc->dc_cdata.dc_tx_prod;
+ DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT);
+ sc->dc_cdata.dc_tx_cnt++;
+ sframe = &sc->dc_ldata->dc_tx_list[i];
+ sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf;
+ bzero((char *)sp, DC_SFRAME_LEN);
+
+ sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf);
+ sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK |
+ DC_FILTER_HASHPERF | DC_TXCTL_FINT;
+
+ sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = dc_crc_le(sc, (caddr_t)&etherbroadcastaddr);
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ /* Set our MAC address */
+ sp[0] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0];
+ sp[1] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1];
+ sp[2] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2];
+
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
+ ifp->if_flags |= IFF_RUNNING;
+ sframe->dc_status = DC_TXSTAT_OWN;
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * wait some time...
+ */
+ DELAY(1000);
+
+ ifp->if_timer = 5;
+
+ return;
+}
+
static void dc_setfilt(sc)
struct dc_softc *sc;
{
@@ -1171,7 +1282,10 @@ static void dc_setfilt(sc)
if (DC_IS_ADMTEK(sc))
dc_setfilt_admtek(sc);
- return;
+ if (DC_IS_XIRCOM(sc))
+ dc_setfilt_xircom(sc);
+
+ return;
}
/*
@@ -1338,7 +1452,7 @@ static void dc_reset(sc)
break;
}
- if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc)) {
+ if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) {
DELAY(10000);
DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
i = 0;
@@ -1788,6 +1902,14 @@ static int dc_attach(dev)
sc->dc_flags |= DC_REDUCED_MII_POLL;
sc->dc_pmode = DC_PMODE_MII;
break;
+ case DC_DEVICEID_X3201:
+ sc->dc_type = DC_TYPE_XIRCOM;
+ sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE;
+ /*
+ * We don't actually need to coalesce, but we're doing
+ * it to obtain a double word aligned buffer.
+ */
+ break;
default:
printf("dc%d: unknown device: %x\n", sc->dc_unit,
sc->dc_info->dc_did);
@@ -1805,7 +1927,7 @@ static int dc_attach(dev)
dc_reset(sc);
/* Take 21143 out of snooze mode */
- if (DC_IS_INTEL(sc)) {
+ if (DC_IS_INTEL(sc) || DC_IS_XIRCOM(sc)) {
command = pci_read_config(dev, DC_PCI_CFDD, 4);
command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
pci_write_config(dev, DC_PCI_CFDD, command, 4);
@@ -1852,6 +1974,9 @@ static int dc_attach(dev)
case DC_TYPE_AN985:
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0);
break;
+ case DC_TYPE_XIRCOM:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, 3, 3, 0);
+ break;
default:
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
break;
@@ -1883,6 +2008,7 @@ static int dc_attach(dev)
ifp->if_softc = sc;
ifp->if_unit = unit;
ifp->if_name = "dc";
+ /* XXX: bleah, MTU gets overwritten in ether_ifattach() */
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = dc_ioctl;
@@ -1937,6 +2063,19 @@ static int dc_attach(dev)
goto fail;
}
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * setup General Purpose Port mode and data so the tulip
+ * can talk to the MII.
+ */
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ }
+
/*
* Call MI attach routine.
*/
@@ -2420,11 +2559,23 @@ static void dc_txeof(sc)
continue;
}
- if (/*sc->dc_type == DC_TYPE_21143 &&*/
- sc->dc_pmode == DC_PMODE_MII &&
- ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
- DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
- txstat &= ~DC_TXSTAT_ERRSUM;
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * XXX: Why does my Xircom taunt me so?
+ * For some reason it likes setting the CARRLOST flag
+ * even when the carrier is there. wtf?!? */
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ } else {
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ }
if (txstat & DC_TXSTAT_ERRSUM) {
ifp->if_oerrors++;
@@ -2557,7 +2708,8 @@ static void dc_intr(arg)
/* Disable interrupts. */
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
- while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) {
+ while(((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS)
+ && status != 0xFFFFFFFF) {
CSR_WRITE_4(sc, DC_ISR, status);
@@ -2883,6 +3035,19 @@ static void dc_init(xsc)
DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715);
}
+ if (DC_IS_XIRCOM(sc)) {
+ /*
+ * setup General Purpose Port mode and data so the tulip
+ * can talk to the MII.
+ */
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN |
+ DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT);
+ DELAY(10);
+ }
+
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_72BYTES);
diff --git a/sys/pci/if_dcreg.h b/sys/pci/if_dcreg.h
index e72d8b4..f36d6b3 100644
--- a/sys/pci/if_dcreg.h
+++ b/sys/pci/if_dcreg.h
@@ -52,6 +52,7 @@
#define DC_SIARESET 0x68 /* SIA connectivity */
#define DC_10BTCTRL 0x70 /* SIA transmit and receive */
#define DC_WATCHDOG 0x78 /* SIA and general purpose port */
+#define DC_SIAGP 0x78 /* SIA and general purpose port (X3201) */
/*
* There are two general 'types' of MX chips that we need to be
@@ -75,6 +76,7 @@
#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */
#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */
#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */
+#define DC_TYPE_XIRCOM 0xB /* Xircom X3201 */
#define DC_IS_MACRONIX(x) \
(x->dc_type == DC_TYPE_98713 || \
@@ -92,6 +94,7 @@
#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102)
#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII)
#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC)
+#define DC_IS_XIRCOM(x) (x->dc_type == DC_TYPE_XIRCOM)
/* MII/symbol mode port types */
#define DC_PMODE_MII 0x1
@@ -349,6 +352,25 @@
#define DC_WDOG_CTLWREN 0x08000000
/*
+ * SIA and General Purpose Port register (X3201)
+ */
+#define DC_SIAGP_RXMATCH 0x40000000
+#define DC_SIAGP_INT1 0x20000000
+#define DC_SIAGP_INT0 0x10000000
+#define DC_SIAGP_WRITE_EN 0x08000000
+#define DC_SIAGP_RXMATCH_EN 0x04000000
+#define DC_SIAGP_INT1_EN 0x02000000
+#define DC_SIAGP_INT0_EN 0x01000000
+#define DC_SIAGP_LED3 0x00800000
+#define DC_SIAGP_LED2 0x00400000
+#define DC_SIAGP_LED1 0x00200000
+#define DC_SIAGP_LED0 0x00100000
+#define DC_SIAGP_MD_GP3_OUTPUT 0x00080000
+#define DC_SIAGP_MD_GP2_OUTPUT 0x00040000
+#define DC_SIAGP_MD_GP1_OUTPUT 0x00020000
+#define DC_SIAGP_MD_GP0_OUTPUT 0x00010000
+
+/*
* Size of a setup frame.
*/
#define DC_SFRAME_LEN 192
@@ -834,6 +856,17 @@ struct dc_softc {
#define DC_DEVICEID_EN1217 0x1217
/*
+ * Xircom vendor ID
+ */
+#define DC_VENDORID_XIRCOM 0x115d
+
+/*
+ * Xircom device IDs.
+ */
+#define DC_DEVICEID_X3201 0x0003
+
+
+/*
* PCI low memory base and low I/O base register, and
* other PCI registers.
*/
OpenPOWER on IntegriCloud