summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1999-05-18 19:18:22 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1999-05-18 19:18:22 +0000
commite4bdc87c54aaf38993d4fb762bbf0b2eaa1f386f (patch)
treeb43ac8b9c9186fd588663c342042c3e117978872
parente24f1614a91d071845f6dab13b9115e56e342414 (diff)
Xircom ethernet, with some rough ends still, but it can do 100Mbit if tickled.
It is no cardbus code though. So far tested on CE2 and Realport.
-rw-r--r--sys/dev/pcmcia/if_xe.c1526
-rw-r--r--sys/dev/pcmcia/if_xereg.h305
2 files changed, 1831 insertions, 0 deletions
diff --git a/sys/dev/pcmcia/if_xe.c b/sys/dev/pcmcia/if_xe.c
new file mode 100644
index 00000000000..b121869a1c1
--- /dev/null
+++ b/sys/dev/pcmcia/if_xe.c
@@ -0,0 +1,1526 @@
+/* $OpenBSD: if_xe.c,v 1.1 1999/05/18 19:18:21 niklas Exp $ */
+
+/*
+ * Copyright (c) 1999 Niklas Hallqvist, C Stone, Job de Haas
+ * 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.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niklas Hallqvist,
+ * C Stone and Job de Haas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * A driver for Xircom ethernet PC-cards.
+ *
+ * The driver has been inspired by the xirc2ps_cs.c driver found in Linux'
+ * PCMCIA package written by Werner Koch <werner.koch@guug.de>:
+ * [xirc2ps_cs.c wk 14.04.97] (1.31 1998/12/09 19:32:55)
+ * I will note that no code was used verbatim from that driver as it is under
+ * the much too strong GNU General Public License, it was only used as a
+ * "specification" of sorts.
+ * Other inspirations have been if_fxp.c, if_ep_pcmcia.c and elink3.c as
+ * they were found in OpenBSD 2.4.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#define ETHER_MIN_LEN 64
+#define ETHER_CRC_LEN 4
+
+/*
+ * Maximum number of bytes to read per interrupt. Linux recommends
+ * somewhere between 2000-22000.
+ * XXX This is currently a hard maximum.
+ */
+#define MAX_BYTES_INTR 12000
+
+#include <dev/mii/miivar.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciadevs.h>
+#include <dev/pcmcia/if_xereg.h>
+
+#ifdef __GNUC__
+#define INLINE __inline
+#else
+#define INLINE
+#endif /* __GNUC__ */
+
+#ifdef XEDEBUG
+
+#define XED_CONFIG 0x1
+#define XED_MII 0x2
+#define XED_INTR 0x4
+
+#ifndef XEDEBUG_DEF
+#define XEDEBUG_DEF (XED_CONFIG|XED_INTR)
+#endif /* XEDEBUG_DEF */
+
+int xedebug = XEDEBUG_DEF;
+
+#define DPRINTF(cat, x) if (xedebug & (cat)) printf x
+
+#else /* XEDEBUG */
+#define DPRINTF(cat, x) (void)0
+#endif /* XEDEBUG */
+
+int xe_pcmcia_match __P((struct device *, void *, void *));
+void xe_pcmcia_attach __P((struct device *, struct device *, void *));
+
+/*
+ * In case this chipset ever turns up out of pcmcia attachments (very
+ * unlikely) do the driver splitup.
+ */
+struct xe_softc {
+ struct device sc_dev; /* Generic device info */
+ u_int32_t sc_flags; /* Misc. flags */
+ void *sc_ih; /* Interrupt handler */
+ struct arpcom sc_arpcom; /* Ethernet common part */
+ struct ifmedia sc_media; /* Media control */
+ struct mii_data sc_mii; /* MII media information */
+ int sc_all_mcasts; /* Receive all multicasts */
+ bus_space_tag_t sc_bst; /* Bus cookie */
+ bus_space_handle_t sc_bsh; /* Bus I/O handle */
+ bus_addr_t sc_offset; /* Offset of registers */
+ u_int8_t sc_rev; /* Chip revision */
+};
+
+#define XEF_MOHAWK 0x001
+#define XEF_DINGO 0x002
+#define XEF_MODEM 0x004
+#define XEF_UNSUPPORTED 0x008
+#define XEF_CE 0x010
+#define XEF_CE2 0x020
+#define XEF_CE3 0x040
+#define XEF_CE33 0x080
+#define XEF_CE56 0x100
+
+struct xe_pcmcia_softc {
+ struct xe_softc sc_xe; /* Generic device info */
+ struct pcmcia_mem_handle sc_pcmh; /* PCMCIA memspace info */
+ int sc_mem_window; /* mem window */
+ struct pcmcia_io_handle sc_pcioh; /* iospace info */
+ int sc_io_window; /* io window info */
+ struct pcmcia_function *sc_pf; /* PCMCIA function */
+};
+
+/* Autoconfig definition of driver back-end */
+struct cfdriver xe_cd = {
+ NULL, "xe", DV_IFNET
+};
+
+struct cfattach xe_pcmcia_ca = {
+ sizeof (struct xe_pcmcia_softc), xe_pcmcia_match, xe_pcmcia_attach
+};
+
+void xe_cycle_power __P((struct xe_softc *));
+int xe_ether_ioctl __P((struct ifnet *, u_long cmd, caddr_t));
+void xe_full_reset __P((struct xe_softc *));
+void xe_init __P((struct xe_softc *));
+int xe_intr __P((void *));
+int xe_ioctl __P((struct ifnet *, u_long, caddr_t));
+int xe_mdi_read __P((struct device *, int, int));
+void xe_mdi_write __P((struct device *, int, int, int));
+int xe_mediachange __P((struct ifnet *));
+void xe_mediastatus __P((struct ifnet *, struct ifmediareq *));
+int xe_pcmcia_funce_enaddr __P((struct device *, u_int8_t *));
+u_int32_t xe_pcmcia_interpret_manfid __P((struct device *));
+int xe_pcmcia_lan_nid_ciscallback __P((struct pcmcia_tuple *, void *));
+int xe_pcmcia_manfid_ciscallback __P((struct pcmcia_tuple *, void *));
+u_int16_t xe_get __P((struct xe_softc *));
+void xe_reset __P((struct xe_softc *));
+void xe_set_address __P((struct xe_softc *));
+void xe_start __P((struct ifnet *));
+void xe_statchg __P((struct device *));
+void xe_stop __P((struct xe_softc *));
+void xe_watchdog __P((struct ifnet *));
+#ifdef XEDEBUG
+void xe_reg_dump __P((struct xe_softc *));
+#endif /* XEDEBUG */
+
+int
+xe_pcmcia_match(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct pcmcia_attach_args *pa = aux;
+
+ if (pa->pf->function != PCMCIA_FUNCTION_NETWORK)
+ return (0);
+
+ switch (pa->manufacturer) {
+ case PCMCIA_VENDOR_COMPAQ:
+ case PCMCIA_VENDOR_COMPAQ2:
+ case PCMCIA_VENDOR_INTEL:
+ return (0);
+
+ case PCMCIA_VENDOR_XIRCOM:
+ /* XXX Per-productid checking here. */
+ return (1);
+
+ default:
+ return (0);
+ }
+}
+
+void
+xe_pcmcia_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct xe_pcmcia_softc *psc = (struct xe_pcmcia_softc *)self;
+ struct xe_softc *sc = &psc->sc_xe;
+ struct pcmcia_attach_args *pa = aux;
+ struct pcmcia_function *pf = pa->pf;
+ struct pcmcia_config_entry *cfe;
+ struct ifnet *ifp;
+ u_int8_t myla[ETHER_ADDR_LEN], *enaddr = NULL;
+ int state = 0;
+ struct pcmcia_mem_handle pcmh;
+ int ccr_window;
+ bus_addr_t ccr_offset;
+
+
+ psc->sc_pf = pf;
+
+#if 0
+ /* Figure out what card we are. */
+ sc->sc_flags = xe_pcmcia_interpret_manfid(parent);
+#endif
+ if (sc->sc_flags & XEF_UNSUPPORTED) {
+ printf(": card unsupported\n");
+ goto bad;
+ }
+
+ /* Tell the pcmcia framework where the CCR is. */
+ pf->ccr_base = 0x800;
+ pf->ccr_mask = 0x67;
+
+ /* Fake a cfe. */
+ SIMPLEQ_FIRST(&pa->pf->cfe_head) = cfe = (struct pcmcia_config_entry *)
+ malloc(sizeof *cfe, M_DEVBUF, M_NOWAIT);
+ if (!cfe) {
+ printf(": function enable failed\n");
+ return;
+ }
+ bzero(cfe, sizeof *cfe);
+
+ /*
+ * XXX Use preprocessor symbols instead.
+ * Enable ethernet & its interrupts, wiring them to -INT
+ * No I/O base.
+ */
+ cfe->number = 0x5;
+ cfe->flags = 0; /* XXX Check! */
+ cfe->iftype = PCMCIA_IFTYPE_IO;
+ cfe->num_iospace = 0;
+ cfe->num_memspace = 0;
+ cfe->irqmask = 0x8eb0;
+
+ /* Enable the card. */
+ pcmcia_function_init(pa->pf, cfe);
+ if (pcmcia_function_enable(pa->pf)) {
+ printf(": function enable failed\n");
+ goto bad;
+ }
+
+ state++;
+
+ if (pcmcia_io_alloc(pa->pf, 0, 16, 16, &psc->sc_pcioh)) {
+ printf(": io allocation failed\n");
+ goto bad;
+ }
+
+ state++;
+
+ if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_IO16, 0, 16, &psc->sc_pcioh,
+ &psc->sc_io_window)) {
+ printf(": can't map io space\n");
+ goto bad;
+ }
+ sc->sc_bst = psc->sc_pcioh.iot;
+ sc->sc_bsh = psc->sc_pcioh.ioh;
+ sc->sc_offset = 0;
+
+#if 0
+ if (pcmcia_mem_alloc(pf, 16, &psc->sc_pcmh)) {
+ printf(": pcmcia memory allocation failed\n");
+ goto bad;
+ }
+ state++;
+
+ if (pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, 0x300, 16, &psc->sc_pcmh,
+ &sc->sc_offset, &psc->sc_mem_window)) {
+ printf(": pcmcia memory mapping failed\n");
+ goto bad;
+ }
+
+ sc->sc_bst = psc->sc_pcmh.memt;
+ sc->sc_bsh = psc->sc_pcmh.memh;
+#endif
+
+ /* Figure out what card we are. */
+ sc->sc_flags = xe_pcmcia_interpret_manfid(parent);
+
+ /*
+ * Configuration as adviced by DINGO documentation.
+ * We only know about this flag after the manfid interpretation.
+ * Dingo has some extra configuration registers in the CCR space.
+ */
+ if (sc->sc_flags & XEF_DINGO) {
+ if (pcmcia_mem_alloc(pf, PCMCIA_CCR_SIZE_DINGO, &pcmh)) {
+ DPRINTF(XED_CONFIG, ("bad mem alloc\n"));
+ goto bad;
+ }
+
+ if (pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, pf->ccr_base,
+ PCMCIA_CCR_SIZE_DINGO, &pcmh, &ccr_offset,
+ &ccr_window)) {
+ DPRINTF(XED_CONFIG, ("bad mem map\n"));
+ pcmcia_mem_free(pf, &pcmh);
+ goto bad;
+ }
+
+ bus_space_write_1(pcmh.memt, pcmh.memh,
+ ccr_offset + PCMCIA_CCR_DCOR0, PCMCIA_CCR_DCOR0_SFINT);
+ bus_space_write_1(pcmh.memt, pcmh.memh,
+ ccr_offset + PCMCIA_CCR_DCOR1,
+ PCMCIA_CCR_DCOR1_FORCE_LEVIREQ | PCMCIA_CCR_DCOR1_D6);
+ bus_space_write_1(pcmh.memt, pcmh.memh,
+ ccr_offset + PCMCIA_CCR_DCOR2, 0);
+ bus_space_write_1(pcmh.memt, pcmh.memh,
+ ccr_offset + PCMCIA_CCR_DCOR3, 0);
+ bus_space_write_1(pcmh.memt, pcmh.memh,
+ ccr_offset + PCMCIA_CCR_DCOR4, 0);
+
+ /* We don't need them anymore and can free them (I think). */
+ pcmcia_mem_unmap(pf, ccr_window);
+ pcmcia_mem_free(pf, &pcmh);
+ }
+
+ /*
+ * Try to get the ethernet address from FUNCE/LAN_NID tuple.
+ */
+ if (xe_pcmcia_funce_enaddr(parent, myla))
+ enaddr = myla;
+ ifp = &sc->sc_arpcom.ac_if;
+ if (enaddr)
+ bcopy(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
+ else {
+ printf("%s: unable to get ethernet address\n",
+ sc->sc_dev.dv_xname);
+ goto bad;
+ }
+
+ bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+ ifp->if_flags =
+ IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = xe_ioctl;
+ ifp->if_start = xe_start;
+ ifp->if_watchdog = xe_watchdog;
+
+ printf(": address %s", ether_sprintf(sc->sc_arpcom.ac_enaddr));
+
+ /* Reset and initialize the card. */
+ xe_full_reset(sc);
+
+ /* Initialize our media structures and probe the phy. */
+ sc->sc_mii.mii_ifp = ifp;
+ sc->sc_mii.mii_readreg = xe_mdi_read;
+ sc->sc_mii.mii_writereg = xe_mdi_write;
+ sc->sc_mii.mii_statchg = xe_statchg;
+ ifmedia_init(&sc->sc_mii.mii_media, 0, xe_mediachange,
+ xe_mediastatus);
+ DPRINTF(XED_MII | XED_CONFIG,
+ ("bmsr %x\n", xe_mdi_read(&sc->sc_dev, 0, 1)));
+ mii_phy_probe(self, &sc->sc_mii, 0xffffffff);
+ if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
+ printf(", no phy found, using auto mode");
+ ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO, 0,
+ NULL);
+ }
+ ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
+
+ /*
+ * Attach the interface.
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+#if NBPFILTER > 0
+ bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB,
+ sizeof(struct ether_header));
+#endif /* NBPFILTER > 0 */
+
+ printf("\n");
+
+ /* Establish the interrupt. */
+ sc->sc_ih = pcmcia_intr_establish(pa->pf, IPL_NET, xe_intr, sc);
+ if (sc->sc_ih == NULL) {
+ printf("%s: couldn't establish interrupt\n",
+ sc->sc_dev.dv_xname);
+ goto bad;
+ }
+
+ /*
+ * Reset and initialize the card again for DINGO (as found in Linux
+ * driver). Without this Dingo will get a watchdog timeout the first
+ * time.
+ */
+ if (sc->sc_flags & XEF_DINGO)
+ xe_full_reset(sc);
+
+#ifdef notyet
+ /*
+ * XXX This should be done once the framework has enable/disable hooks.
+ */
+ pcmcia_function_disable(pa->pf);
+#endif /* notyet */
+
+ return;
+
+bad:
+ if (state > 2)
+ pcmcia_chip_io_unmap(pf->sc->pct, pf->sc->pch,
+ psc->sc_io_window);
+ if (state > 1)
+ pcmcia_chip_io_free(pf->sc->pct, pf->sc->pch, &psc->sc_pcioh);
+ if (state > 0)
+ pcmcia_function_disable(pa->pf);
+ free(cfe, M_DEVBUF);
+}
+
+/*
+ * XXX These two functions might be OK to factor out into pcmcia.c since
+ * if_sm_pcmcia.c uses similar ones.
+ */
+int
+xe_pcmcia_funce_enaddr(parent, myla)
+ struct device *parent;
+ u_int8_t *myla;
+{
+ /* XXX The Linux driver has more ways to do this in case of failure. */
+ return (pcmcia_scan_cis(parent, xe_pcmcia_lan_nid_ciscallback, myla));
+}
+
+int
+xe_pcmcia_lan_nid_ciscallback(tuple, arg)
+ struct pcmcia_tuple *tuple;
+ void *arg;
+{
+ u_int8_t *myla = arg;
+ int i;
+
+ if (tuple->code == PCMCIA_CISTPL_FUNCE) {
+ if (tuple->length < 2)
+ return (0);
+
+ switch (pcmcia_tuple_read_1(tuple, 0)) {
+ case PCMCIA_TPLFE_TYPE_LAN_NID:
+ if (pcmcia_tuple_read_1(tuple, 1) != ETHER_ADDR_LEN)
+ return (0);
+ break;
+
+ case 0x02:
+ /*
+ * Not sure about this, I don't have a CE2
+ * that puts the ethernet addr here.
+ */
+ if (pcmcia_tuple_read_1(tuple, 1) != 13)
+ return (0);
+ break;
+
+ default:
+ return (0);
+ }
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ myla[i] = pcmcia_tuple_read_1(tuple, i + 2);
+ return (1);
+ }
+
+ /* Yet another spot where this might be. */
+ if (tuple->code == 0x89) {
+ pcmcia_tuple_read_1(tuple, 1);
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ myla[i] = pcmcia_tuple_read_1(tuple, i + 2);
+ return (1);
+ }
+ return (0);
+}
+
+u_int32_t
+xe_pcmcia_interpret_manfid (parent)
+ struct device *parent;
+{
+ u_int32_t flags = 0;
+ struct pcmcia_softc *psc = (struct pcmcia_softc *)parent;
+ char *tptr;
+
+ if (!pcmcia_scan_cis(parent, xe_pcmcia_manfid_ciscallback, &flags))
+ return (XEF_UNSUPPORTED);
+
+ if (flags & XEF_CE) {
+ tptr = memchr(psc->card.cis1_info[2], 'C',
+ strlen(psc->card.cis1_info[2]));
+ /* XXX not sure if other CE2s hide "CE2" in different places */
+ if (tptr && *(tptr + 1) == 'E' && *(tptr + 2) == '2') {
+ flags ^= (XEF_CE | XEF_UNSUPPORTED);
+ flags |= XEF_CE2;
+ }
+ }
+ return (flags);
+}
+
+int
+xe_pcmcia_manfid_ciscallback(tuple, arg)
+ struct pcmcia_tuple *tuple;
+ void *arg;
+{
+ u_int32_t *flagsp = arg;
+ u_int8_t media, product;
+
+ if (tuple->code == PCMCIA_CISTPL_MANFID) {
+ if (tuple->length < 2)
+ return (0);
+
+ media = pcmcia_tuple_read_1(tuple, 3);
+ product = pcmcia_tuple_read_1(tuple, 4);
+
+ if (!(product & XEPROD_CREDITCARD) ||
+ !(media & XEMEDIA_ETHER)) {
+ *flagsp |= XEF_UNSUPPORTED;
+ return (1);
+ }
+
+ if (media & XEMEDIA_MODEM)
+ *flagsp |= XEF_MODEM;
+
+ switch (product & XEPROD_IDMASK) {
+ case 1:
+ /* XXX Can be CE2 too (we double-check later). */
+ *flagsp |= XEF_CE | XEF_UNSUPPORTED;
+ break;
+ case 2:
+ *flagsp |= XEF_CE2;
+ break;
+ case 3:
+ /*
+ * XXX Linux driver suggests this can be Mohawk if
+ * there is no modem function on the card.
+ */
+ *flagsp |= XEF_CE3;
+ break;
+ case 4:
+ *flagsp |= XEF_CE33;
+ break;
+ case 5:
+ *flagsp |= XEF_CE56 | XEF_MOHAWK;
+ break;
+ case 6:
+ case 7:
+ *flagsp |= XEF_CE56 | XEF_MOHAWK | XEF_DINGO;
+ break;
+ default:
+ *flagsp |= XEF_UNSUPPORTED;
+ break;
+ }
+
+ return (1);
+ }
+ return (0);
+}
+
+int
+xe_intr(arg)
+ void *arg;
+{
+ struct xe_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ u_int8_t esr, rsr, isr, rx_status, savedpage;
+ u_int16_t tx_status, recvcount = 0, tempint;
+
+ ifp->if_timer = 0; /* turn watchdog timer off */
+
+ if (sc->sc_flags & XEF_MOHAWK) {
+ /* Disable interrupt (Linux does it). */
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
+ 0);
+ }
+
+ savedpage =
+ bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + PR);
+
+ PAGE(sc, 0);
+ esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + ESR);
+ isr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + ISR0);
+ rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RSR);
+
+ /* Check to see if card has been ejected. */
+ if (isr == 0xff) {
+ printf("%s: interrupt for dead card\n", sc->sc_dev.dv_xname);
+ goto end;
+ }
+
+ PAGE(sc, 40);
+ rx_status =
+ bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RXST0);
+ tx_status =
+ bus_space_read_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + TXST0);
+
+ /*
+ * XXX Linux writes to RXST0 and TXST* here. My CE2 works just fine
+ * without it, and I can't see an obvious reason for it.
+ */
+
+ PAGE(sc, 0);
+ while (esr & FULL_PKT_RCV) {
+ if (!(rsr & RSR_RX_OK))
+ break;
+
+ /* Compare bytes read this interrupt to hard maximum. */
+ if (recvcount > MAX_BYTES_INTR) {
+ DPRINTF(XED_INTR,
+ ("%s: too many bytes this interrupt\n",
+ sc->sc_dev.dv_xname));
+ ifp->if_iqdrops++;
+ /* Drop packet. */
+ bus_space_write_2(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + DO0, DO_SKIP_RX_PKT);
+ }
+ tempint = xe_get(sc);
+ recvcount += tempint;
+ ifp->if_ibytes += tempint;
+ esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + ESR);
+ rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + RSR);
+ }
+
+ /* Packet too long? */
+ if (rsr & RSR_TOO_LONG) {
+ ifp->if_ierrors++;
+ DPRINTF(XED_INTR,
+ ("%s: packet too long\n", sc->sc_dev.dv_xname));
+ }
+
+ /* CRC error? */
+ if (rsr & RSR_CRCERR) {
+ ifp->if_ierrors++;
+ DPRINTF(XED_INTR,
+ ("%s: CRC error detected\n", sc->sc_dev.dv_xname));
+ }
+
+ /* Alignment error? */
+ if (rsr & RSR_ALIGNERR) {
+ ifp->if_ierrors++;
+ DPRINTF(XED_INTR,
+ ("%s: alignment error detected\n", sc->sc_dev.dv_xname));
+ }
+
+ /* Check for rx overrun. */
+ if (rx_status & RX_OVERRUN) {
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
+ CLR_RX_OVERRUN);
+ DPRINTF(XED_INTR, ("overrun cleared\n"));
+ }
+
+ /* Try to start more packets transmitting. */
+ if (ifp->if_snd.ifq_head)
+ xe_start(ifp);
+
+ /* Detected excessive collisions? */
+ if ((tx_status & EXCESSIVE_COLL) && ifp->if_opackets > 0) {
+ DPRINTF(XED_INTR,
+ ("%s: excessive collisions\n", sc->sc_dev.dv_xname));
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
+ RESTART_TX);
+ ifp->if_oerrors++;
+ }
+
+ if ((tx_status & TX_ABORT) && ifp->if_opackets > 0)
+ ifp->if_oerrors++;
+
+end:
+ /* Reenable interrupts. */
+ PAGE(sc, savedpage);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
+ ENABLE_INT);
+
+ return (1);
+}
+
+u_int16_t
+xe_get(sc)
+ struct xe_softc *sc;
+{
+ u_int8_t rsr;
+ struct mbuf *top, **mp, *m;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ u_int16_t pktlen, len, recvcount = 0;
+ u_int8_t *data;
+ struct ether_header *eh;
+
+ PAGE(sc, 0);
+ rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RSR);
+
+ pktlen =
+ bus_space_read_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RBC0) &
+ RBC_COUNT_MASK;
+ if (pktlen == 0) {
+ /*
+ * XXX At least one CE2 sets RBC0 == 0 occasionally, and only
+ * when MPE is set. It is not known why.
+ */
+ return (0);
+ }
+ recvcount += pktlen;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (recvcount);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = pktlen;
+ len = MHLEN;
+ top = 0;
+ mp = &top;
+
+ while (pktlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (recvcount);
+ }
+ len = MLEN;
+ }
+ if (pktlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ m_freem(m);
+ m_freem(top);
+ return (recvcount);
+ }
+ len = MCLBYTES;
+ }
+ if (!top) {
+ caddr_t newdata = (caddr_t)ALIGN(m->m_data +
+ sizeof (struct ether_header)) -
+ sizeof (struct ether_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+ len = min(pktlen, len);
+
+ data = mtod(m, u_int8_t *);
+ if (len > 1) {
+ len &= ~1;
+ bus_space_read_raw_multi_2(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + EDP, data, len);
+ } else
+ *data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + EDP);
+ m->m_len = len;
+ pktlen -= len;
+ *mp = m;
+ mp = &m->m_next;
+ }
+
+ /* Skip Rx packet. */
+ bus_space_write_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + DO0,
+ DO_SKIP_RX_PKT);
+
+ ifp->if_ipackets++;
+
+ eh = mtod(top, struct ether_header *);
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, top);
+#endif
+
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(ifp, eh, top);
+ return (recvcount);
+}
+
+
+/*
+ * Serial management for the MII.
+ * The DELAY's below stem from the fact that the maximum frequency
+ * acceptable on the MDC pin is 2.5 MHz and fast processors can easily
+ * go much faster than that.
+ */
+
+/* Let the MII serial management be idle for one period. */
+static INLINE void xe_mdi_idle __P((struct xe_softc *));
+static INLINE void
+xe_mdi_idle(sc)
+ struct xe_softc *sc;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+
+ /* Drive MDC low... */
+ bus_space_write_1(bst, bsh, offset + GP2, MDC_LOW);
+ DELAY(1);
+
+ /* and high again. */
+ bus_space_write_1(bst, bsh, offset + GP2, MDC_HIGH);
+ DELAY(1);
+}
+
+/* Pulse out one bit of data. */
+static INLINE void xe_mdi_pulse __P((struct xe_softc *, int));
+static INLINE void
+xe_mdi_pulse(sc, data)
+ struct xe_softc *sc;
+ int data;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+ u_int8_t bit = data ? MDIO_HIGH : MDIO_LOW;
+
+ /* First latch the data bit MDIO with clock bit MDC low...*/
+ bus_space_write_1(bst, bsh, offset + GP2, bit | MDC_LOW);
+ DELAY(1);
+
+ /* then raise the clock again, preserving the data bit. */
+ bus_space_write_1(bst, bsh, offset + GP2, bit | MDC_HIGH);
+ DELAY(1);
+}
+
+/* Probe one bit of data. */
+static INLINE int xe_mdi_probe __P((struct xe_softc *sc));
+static INLINE int
+xe_mdi_probe(sc)
+ struct xe_softc *sc;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+ u_int8_t x;
+
+ /* Pull clock bit MDCK low... */
+ bus_space_write_1(bst, bsh, offset + GP2, MDC_LOW);
+ DELAY(1);
+
+ /* Read data and drive clock high again. */
+ x = bus_space_read_1(bst, bsh, offset + GP2) & MDIO;
+ bus_space_write_1(bst, bsh, offset + GP2, MDC_HIGH);
+ DELAY(1);
+
+ return (x);
+}
+
+/* Pulse out a sequence of data bits. */
+static INLINE void xe_mdi_pulse_bits __P((struct xe_softc *, u_int32_t, int));
+static INLINE void
+xe_mdi_pulse_bits(sc, data, len)
+ struct xe_softc *sc;
+ u_int32_t data;
+ int len;
+{
+ u_int32_t mask;
+
+ for (mask = 1 << (len - 1); mask; mask >>= 1)
+ xe_mdi_pulse (sc, data & mask);
+}
+
+/* Read a PHY register. */
+int
+xe_mdi_read(self, phy, reg)
+ struct device *self;
+ int phy;
+ int reg;
+{
+ struct xe_softc *sc = (struct xe_softc *)self;
+ int i;
+ u_int32_t mask;
+ u_int32_t data = 0;
+
+ PAGE(sc, 2);
+ for (i = 0; i < 32; i++) /* Synchronize. */
+ xe_mdi_pulse(sc, 1);
+ xe_mdi_pulse_bits(sc, 0x06, 4); /* Start + Read opcode */
+ xe_mdi_pulse_bits(sc, phy, 5); /* PHY address */
+ xe_mdi_pulse_bits(sc, reg, 5); /* PHY register */
+ xe_mdi_idle(sc); /* Turn around. */
+ xe_mdi_probe(sc); /* Drop initial zero bit. */
+
+ for (mask = 1 << 15; mask; mask >>= 1)
+ if (xe_mdi_probe(sc))
+ data |= mask;
+ xe_mdi_idle(sc);
+
+ DPRINTF(XED_MII,
+ ("xe_mdi_read: phy %d reg %d -> %x\n", phy, reg, data));
+ return (data);
+}
+
+/* Write a PHY register. */
+void
+xe_mdi_write(self, phy, reg, value)
+ struct device *self;
+ int phy;
+ int reg;
+ int value;
+{
+ struct xe_softc *sc = (struct xe_softc *)self;
+ int i;
+
+ PAGE(sc, 2);
+ for (i = 0; i < 32; i++) /* Synchronize. */
+ xe_mdi_pulse(sc, 1);
+ xe_mdi_pulse_bits(sc, 0x05, 4); /* Start + Write opcode */
+ xe_mdi_pulse_bits(sc, phy, 5); /* PHY address */
+ xe_mdi_pulse_bits(sc, reg, 5); /* PHY register */
+ xe_mdi_pulse_bits(sc, 0x02, 2); /* Turn around. */
+ xe_mdi_pulse_bits(sc, value, 16); /* Write the data */
+ xe_mdi_idle(sc); /* Idle away. */
+
+ DPRINTF(XED_MII,
+ ("xe_mdi_write: phy %d reg %d val %x\n", phy, reg, value));
+}
+
+void
+xe_statchg(self)
+ struct device *self;
+{
+ /* XXX Update ifp->if_baudrate */
+}
+
+/*
+ * Change media according to request.
+ */
+int
+xe_mediachange(ifp)
+ struct ifnet *ifp;
+{
+ if (ifp->if_flags & IFF_UP)
+ xe_init(ifp->if_softc);
+ return (0);
+}
+
+/*
+ * Notify the world which media we're using.
+ */
+void
+xe_mediastatus(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct xe_softc *sc = ifp->if_softc;
+
+ mii_pollstat(&sc->sc_mii);
+ ifmr->ifm_status = sc->sc_mii.mii_media_status;
+ ifmr->ifm_active = sc->sc_mii.mii_media_active;
+}
+
+void
+xe_reset(sc)
+ struct xe_softc *sc;
+{
+ int s;
+
+ s = splnet();
+ xe_stop(sc);
+ xe_full_reset(sc);
+ xe_init(sc);
+ splx(s);
+}
+
+void
+xe_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct xe_softc *sc = ifp->if_softc;
+
+ log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
+ ++sc->sc_arpcom.ac_if.if_oerrors;
+
+ xe_reset(sc);
+}
+
+void
+xe_stop(sc)
+ register struct xe_softc *sc;
+{
+ /* Disable interrupts. */
+ PAGE(sc, 0);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR, 0);
+
+ PAGE(sc, 1);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + IMR0, 0);
+
+ /* Power down, wait. */
+ PAGE(sc, 4);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + GP1, 0);
+ DELAY(40000);
+
+ /* Cancel watchdog timer. */
+ sc->sc_arpcom.ac_if.if_timer = 0;
+}
+
+void
+xe_init(sc)
+ struct xe_softc *sc;
+{
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ int s;
+
+ DPRINTF(XED_CONFIG, ("xe_init\n"));
+
+ s = splimp();
+
+ xe_set_address(sc);
+
+ /* Set current media. */
+ mii_mediachg(&sc->sc_mii);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ splx(s);
+}
+
+/*
+ * Start outputting on the interface.
+ * Always called as splnet().
+ */
+void
+xe_start(ifp)
+ struct ifnet *ifp;
+{
+ struct xe_softc *sc = ifp->if_softc;
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+ unsigned int s, len, pad = 0;
+ struct mbuf *m0, *m;
+
+ /* Don't transmit if interface is busy or not running. */
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ /* Peek at the next packet. */
+ m0 = ifp->if_snd.ifq_head;
+ if (m0 == 0)
+ return;
+
+ /* We need to use m->m_pkthdr.len, so require the header. */
+ if (!(m0->m_flags & M_PKTHDR))
+ panic("xe_start: no header mbuf");
+
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ len = m0->m_pkthdr.len;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+
+ /* Pad to ETHER_MIN_LEN - ETHER_CRC_LEN. */
+ if (len < ETHER_MIN_LEN - ETHER_CRC_LEN)
+ pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len;
+
+ /*
+ * Do the output at splhigh() so that an interrupt from another device
+ * won't cause a FIFO underrun.
+ */
+ s = splhigh();
+
+ PAGE(sc, 0);
+ bus_space_write_2(bst, bsh, offset + TSO2, (u_int16_t)len + pad + 2);
+ bus_space_write_2(bst, bsh, offset + EDP, (u_int16_t)len + pad);
+ for (m = m0; m; ) {
+ if (m->m_len > 1)
+ bus_space_write_raw_multi_2(bst, bsh, offset + EDP,
+ mtod(m, u_int8_t *), m->m_len & ~1);
+ if (m->m_len & 1)
+ bus_space_write_1(bst, bsh, offset + EDP,
+ *(mtod(m, u_int8_t *) + m->m_len - 1));
+ MFREE(m, m0);
+ m = m0;
+ }
+ for (; pad > 0; pad--)
+ bus_space_write_1(bst, bsh, offset + EDP, 0);
+
+ if (sc->sc_flags & XEF_MOHAWK)
+ bus_space_write_1(bst, bsh, offset + CR, TX_PKT | ENABLE_INT);
+ splx(s);
+
+ ifp->if_timer = 5;
+ ++ifp->if_opackets;
+}
+
+int
+xe_ether_ioctl(ifp, cmd, data)
+ struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct xe_softc *sc = ifp->if_softc;
+#ifdef NS
+ struct ns_addr *ina;
+#endif /* NS */
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ xe_init(sc);
+ arp_ifinit(&sc->sc_arpcom, ifa);
+ break;
+#endif /* INET */
+
+#ifdef NS
+ case AF_NS:
+ ina = &IA_SNS(ifa)->sns_addr;
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)sc->sc_arpcom.ac_enaddr;
+ else
+ bcopy(ina->x_host.c_host,
+ sc->sc_arpcom.ac_enaddr, ifp->if_addrlen);
+ /* Set new address. */
+ xe_init(sc);
+ break;
+#endif /* NS */
+
+ default:
+ xe_init(sc);
+ break;
+ }
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+int
+xe_ioctl(ifp, command, data)
+ struct ifnet *ifp;
+ u_long command;
+ caddr_t data;
+{
+ struct xe_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ error = xe_ether_ioctl(ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ sc->sc_all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
+
+ PAGE(sc, 0x42);
+ if ((ifp->if_flags & IFF_PROMISC) ||
+ (ifp->if_flags & IFF_ALLMULTI))
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + SWC1,
+ SWC1_PROMISC | SWC1_MCAST_PROM);
+ else
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + SWC1, 0);
+
+ /*
+ * If interface is marked up and not running, then start it.
+ * If it is marked down and running, stop it.
+ * XXX If it's up then re-initialize it. This is so flags
+ * such as IFF_PROMISC are handled.
+ */
+ if (ifp->if_flags & IFF_UP) {
+ xe_full_reset(sc);
+ xe_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ xe_stop(sc);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ sc->sc_all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_arpcom) :
+ ether_delmulti(ifr, &sc->sc_arpcom);
+
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware
+ * filter accordingly.
+ */
+ if (!sc->sc_all_mcasts &&
+ !(ifp->if_flags & IFF_PROMISC))
+ xe_set_address(sc);
+
+ /*
+ * xe_set_address() can turn on all_mcasts if we run
+ * out of space, so check it again rather than else {}.
+ */
+ if (sc->sc_all_mcasts)
+ xe_init(sc);
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error =
+ ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+void
+xe_set_address(sc)
+ struct xe_softc *sc;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+ struct arpcom *arp = &sc->sc_arpcom;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ int i, page, pos, num;
+
+ PAGE(sc, 0x50);
+ for (i = 0; i < 6; i++) {
+ bus_space_write_1(bst, bsh, offset + IA + i,
+ sc->sc_arpcom.ac_enaddr[(sc->sc_flags & XEF_MOHAWK) ?
+ 5 - i : i]);
+ }
+
+ if (arp->ac_multicnt > 0) {
+ if (arp->ac_multicnt > 9) {
+ PAGE(sc, 0x42);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh,
+ sc->sc_offset + SWC1,
+ SWC1_PROMISC | SWC1_MCAST_PROM);
+ return;
+ }
+
+ ETHER_FIRST_MULTI(step, arp, enm);
+
+ pos = IA + 6;
+ for (page = 0x50, num = arp->ac_multicnt; num > 0 && enm;
+ num--) {
+ if (bcmp(enm->enm_addrlo, enm->enm_addrhi,
+ sizeof (enm->enm_addrlo)) != 0) {
+ /*
+ * The multicast address is really a range;
+ * it's easier just to accept all multicasts.
+ * XXX should we be setting IFF_ALLMULTI here?
+ */
+ ifp->if_flags |= IFF_ALLMULTI;
+ sc->sc_all_mcasts=1;
+ break;
+ }
+
+ for (i = 0; i < 6; i++) {
+ bus_space_write_1(bst, bsh, offset + pos,
+ enm->enm_addrlo[
+ (sc->sc_flags & XEF_MOHAWK) ? 5 - i : i]);
+
+ if (++pos > 15) {
+ pos = IA;
+ page++;
+ PAGE(sc, page);
+ }
+ }
+ }
+ }
+}
+
+void
+xe_cycle_power (sc)
+ struct xe_softc *sc;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+
+ PAGE(sc, 4);
+ DELAY(1);
+ bus_space_write_1(bst, bsh, offset + GP1, 0);
+ DELAY(40000);
+ if (sc->sc_flags & XEF_MOHAWK)
+ bus_space_write_1(bst, bsh, offset + GP1, POWER_UP);
+ else
+ /* XXX What is bit 2 (aka AIC)? */
+ bus_space_write_1(bst, bsh, offset + GP1, POWER_UP | 4);
+ DELAY(20000);
+}
+
+void
+xe_full_reset (sc)
+ struct xe_softc *sc;
+{
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+
+ /* Do an as extensive reset as possible on all functions. */
+ xe_cycle_power (sc);
+ bus_space_write_1(bst, bsh, offset + CR, SOFT_RESET);
+ DELAY(20000);
+ bus_space_write_1(bst, bsh, offset + CR, 0);
+ DELAY(20000);
+ if (sc->sc_flags & XEF_MOHAWK) {
+ PAGE(sc, 4);
+ /*
+ * Drive GP1 low to power up ML6692 and GP2 high to power up
+ * the 10Mhz chip. XXX What chip is that? The phy?
+ */
+ bus_space_write_1(bst, bsh, offset + GP0,
+ GP1_OUT | GP2_OUT | GP2_WR);
+ }
+ DELAY(500000);
+
+ /* Get revision information. XXX Symbolic constants. */
+ sc->sc_rev = bus_space_read_1(bst, bsh, offset + BV) &
+ ((sc->sc_flags & XEF_MOHAWK) ? 0x70 : 0x30) >> 4;
+
+ /* Media selection. XXX Maybe manual overriding too? */
+ if (!(sc->sc_flags & XEF_MOHAWK)) {
+ PAGE(sc, 4);
+ /*
+ * XXX I have no idea what this really does, it is from the
+ * Linux driver.
+ */
+ bus_space_write_1(bst, bsh, offset + GP0, GP1_OUT);
+ }
+ DELAY(40000);
+
+ /* Setup the ethernet interrupt mask. */
+ PAGE(sc, 1);
+ bus_space_write_1(bst, bsh, offset + IMR0,
+ ISR_TX_OFLOW | ISR_PKT_TX | ISR_MAC_INT | /* ISR_RX_EARLY | */
+ ISR_RX_FULL | ISR_RX_PKT_REJ | ISR_FORCED_INT);
+#if 0
+ bus_space_write_1(bst, bsh, offset + IMR0, 0xff);
+#endif
+ if (!(sc->sc_flags & XEF_DINGO))
+ /* XXX What is this? Not for Dingo at least. */
+ bus_space_write_1(bst, bsh, offset + IMR1, 1);
+
+ /*
+ * Disable source insertion.
+ * XXX Dingo does not have this bit, but Linux does it unconditionally.
+ */
+ if (!(sc->sc_flags & XEF_DINGO)) {
+ PAGE(sc, 0x42);
+ bus_space_write_1(bst, bsh, offset + SWC0, 0x20);
+ }
+
+ /* Set the local memory dividing line. */
+ if (sc->sc_rev != 1) {
+ PAGE(sc, 2);
+ /* XXX Symbolic constant preferrable. */
+ bus_space_write_2(bst, bsh, offset + RBS0, 0x2000);
+ }
+
+ xe_set_address(sc);
+
+ /*
+ * Apparently the receive byte pointer can be bad after a reset, so
+ * we hardwire it correctly.
+ */
+ PAGE(sc, 0);
+ bus_space_write_2(bst, bsh, offset + DO0, DO_CHG_OFFSET);
+
+ /* Setup ethernet MAC registers. XXX Symbolic constants. */
+ PAGE(sc, 0x40);
+ bus_space_write_1(bst, bsh, offset + RX0MSK,
+ PKT_TOO_LONG | CRC_ERR | RX_OVERRUN | RX_ABORT | RX_OK);
+ bus_space_write_1(bst, bsh, offset + TX0MSK,
+ CARRIER_LOST | EXCESSIVE_COLL | TX_UNDERRUN | LATE_COLLISION |
+ SQE | TX_ABORT | TX_OK);
+ if (!(sc->sc_flags & XEF_DINGO))
+ /* XXX From Linux, dunno what 0xb0 means. */
+ bus_space_write_1(bst, bsh, offset + TX1MSK, 0xb0);
+ bus_space_write_1(bst, bsh, offset + RXST0, 0);
+ bus_space_write_1(bst, bsh, offset + TXST0, 0);
+ bus_space_write_1(bst, bsh, offset + TXST1, 0);
+
+ /* Enable MII function if available. */
+ if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
+ PAGE(sc, 2);
+ bus_space_write_1(bst, bsh, offset + MSR,
+ bus_space_read_1(bst, bsh, offset + MSR) | SELECT_MII);
+ DELAY(20000);
+ } else {
+ PAGE(sc, 0);
+
+ /* XXX Do we need to do this? */
+ PAGE(sc, 0x42);
+ bus_space_write_1(bst, bsh, offset + SWC1, SWC1_AUTO_MEDIA);
+ DELAY(50000);
+
+ /* XXX Linux probes the media here. */
+ }
+
+ /* Configure the LED registers. */
+ PAGE(sc, 2);
+
+ /* XXX This is not good for 10base2. */
+ bus_space_write_1(bst, bsh, offset + LED,
+ LED_TX_ACT << LED1_SHIFT | LED_10MB_LINK << LED0_SHIFT);
+ if (sc->sc_flags & XEF_DINGO)
+ bus_space_write_1(bst, bsh, offset + LED3,
+ LED_100MB_LINK << LED3_SHIFT);
+
+ /* Enable receiver and go online. */
+ PAGE(sc, 0x40);
+ bus_space_write_1(bst, bsh, offset + CMD0, ENABLE_RX | ONLINE);
+
+#if 0
+ /* XXX Linux does this here - is it necessary? */
+ PAGE(sc, 1);
+ bus_space_write_1(bst, bsh, offset + IMR0, 0xff);
+ if (!(sc->sc_flags & XEF_DINGO))
+ /* XXX What is this? Not for Dingo at least. */
+ bus_space_write_1(bst, bsh, offset + IMR1, 1);
+#endif
+
+ /* Enable interrupts. */
+ PAGE(sc, 0);
+ bus_space_write_1(bst, bsh, offset + CR, ENABLE_INT);
+
+ /* XXX This is pure magic for me, found in the Linux driver. */
+ if ((sc->sc_flags & (XEF_DINGO | XEF_MODEM)) == XEF_MODEM) {
+ if ((bus_space_read_1(bst, bsh, offset + 0x10) & 0x01) == 0)
+ /* Unmask the master interrupt bit. */
+ bus_space_write_1(bst, bsh, offset + 0x10, 0x11);
+ }
+
+ /*
+ * The Linux driver says this:
+ * We should switch back to page 0 to avoid a bug in revision 0
+ * where regs with offset below 8 can't be read after an access
+ * to the MAC registers.
+ */
+ PAGE(sc, 0);
+}
+
+#ifdef XEDEBUG
+void
+xe_reg_dump (sc)
+ struct xe_softc *sc;
+{
+ int page, i;
+ bus_space_tag_t bst = sc->sc_bst;
+ bus_space_handle_t bsh = sc->sc_bsh;
+ bus_addr_t offset = sc->sc_offset;
+
+ printf("%x: Common registers: ", sc->sc_dev.dv_xname);
+ for (i = 0; i < 8; i++) {
+ printf(" %2.2x", bus_space_read_1(bst, bsh, offset + i));
+ }
+ printf("\n");
+
+ for (page = 0; page < 8; page++) {
+ printf("%s: Register page %2.2x: ", sc->sc_dev.dv_xname, page);
+ PAGE(sc, page);
+ for (i = 8; i < 16; i++) {
+ printf(" %2.2x",
+ bus_space_read_1(bst, bsh, offset + i));
+ }
+ printf("\n");
+ }
+
+ for (page = 0x40; page < 0x5f; page++) {
+ if (page == 0x43 || (page >= 0x46 && page <= 0x4f) ||
+ (page >= 0x51 && page <= 0x5e))
+ continue;
+ printf("%s: Register page %2.2x: ", sc->sc_dev.dv_xname, page);
+ PAGE(sc, page);
+ for (i = 8; i < 16; i++) {
+ printf(" %2.2x",
+ bus_space_read_1(bst, bsh, offset + i));
+ }
+ printf("\n");
+ }
+}
+#endif /* XEDEBUG */
diff --git a/sys/dev/pcmcia/if_xereg.h b/sys/dev/pcmcia/if_xereg.h
new file mode 100644
index 00000000000..96582544244
--- /dev/null
+++ b/sys/dev/pcmcia/if_xereg.h
@@ -0,0 +1,305 @@
+/* $OpenBSD: if_xereg.h,v 1.1 1999/05/18 19:18:21 niklas Exp $ */
+
+/*
+ * Copyright (c) 1999 Niklas Hallqvist, C Stone, Job de Haas
+ * 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.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niklas Hallqvist,
+ * C Stone and Job de Haas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/* Additional Card Configuration Registers on Dingo */
+
+#define PCMCIA_CCR_DCOR0 0x20
+#define PCMCIA_CCR_DCOR0_MRST_SFRST 0x80
+#define PCMCIA_CCR_DCOR0_MRST_SFPWDN 0x40
+#define PCMCIA_CCR_DCOR0_LED3_SFRST 0x20
+#define PCMCIA_CCR_DCOR0_LED3_SFPWDN 0x10
+#define PCMCIA_CCR_DCOR0_BUS 0x08
+#define PCMCIA_CCR_DCOR0_DECODE 0x04
+#define PCMCIA_CCR_DCOR0_SFINT 0x01
+#define PCMCIA_CCR_DCOR1 0x22
+#define PCMCIA_CCR_DCOR1_SFCSR_WAIT 0xC0
+#define PCMCIA_CCR_DCOR1_SHADOW_SFIOB 0x20
+#define PCMCIA_CCR_DCOR1_SHADOW_SFCSR 0x10
+#define PCMCIA_CCR_DCOR1_FORCE_LEVIREQ 0x08
+#define PCMCIA_CCR_DCOR1_D6 0x04
+#define PCMCIA_CCR_DCOR1_SF_STSCHG 0x02
+#define PCMCIA_CCR_DCOR1_SF_IREQ 0x01
+#define PCMCIA_CCR_DCOR2 0x24
+#define PCMCIA_CCR_DCOR2_SHADOW_SFCOR 0x10
+#define PCMCIA_CCR_DCOR2_SMEM_BASE 0x0F
+#define PCMCIA_CCR_DCOR3 0x26
+#define PCMCIA_CCR_DCOR4 0x28
+#define PCMCIA_CCR_SFCOR 0x40
+#define PCMCIA_CCR_SFCOR_SRESET 0x80
+#define PCMCIA_CCR_SFCOR_LEVIREQ 0x40
+#define PCMCIA_CCR_SFCOR_IRQ_STSCHG 0x20
+#define PCMCIA_CCR_SFCOR_CFINDEX 0x18
+#define PCMCIA_CCR_SFCOR_IREQ_ENABLE 0x04
+#define PCMCIA_CCR_SFCOR_ADDR_DECODE 0x02
+#define PCMCIA_CCR_SFCOR_FUNC_ENABLE 0x01
+#define PCMCIA_CCR_SFCSR 0x42
+#define PCMCIA_CCR_SFCSR_IOIS8 0x20
+#define PCMCIA_CCR_SFCSR_AUDIO 0x08
+#define PCMCIA_CCR_SFCSR_PWRDWN 0x04
+#define PCMCIA_CCR_SFCSR_INTR 0x02
+#define PCMCIA_CCR_SFCSR_INTRACK 0x01
+#define PCMCIA_CCR_SFIOBASE0 0x4A
+#define PCMCIA_CCR_SFIOBASE1 0x4C
+#define PCMCIA_CCR_SFILR 0x52
+
+#define PCMCIA_CCR_SIZE_DINGO 0x54
+
+/* All pages */
+#define CR 0x0 /* W - Command register */
+#define ESR 0x0 /* R - Ethernet status register */
+#define PR 0x1 /* RW - Page register select */
+#define EDP 0x2 /* RW - Ethernet data port, 4 registers */
+#define ISR0 0x6 /* R - Etherenet interrupt status register */
+#define GIR 0x7 /* RW - Global interrupt register */
+#define PTR 0xd /* R - Packets Transmitted register */
+
+/* Page 0 */
+#define TSO0 0x8 /* R - Transmit space open, 3 registers */
+#define TSO1 0x9
+#define TSO2 0xa
+#define DO0 0xc /* W - Data offset, 2 registers */
+#define DO1 0xd
+#define RSR 0xc /* R - Rx status register */
+#define TPR 0xd /* R - Tx packets register */
+#define RBC0 0xe /* R - Rx byte count, 2 registers */
+#define RBC1 0xf
+
+/* Page 1 */
+#define IMR0 0xc /* RW - Interrupt mask, 2 registers */
+#define IMR1 0xd
+#define ECR 0xe /* RW - Ethernet config register */
+
+/* Page 2 */
+#define RBS0 0x8 /* RW - Receive buffer start, 2 registers */
+#define RBS1 0x9
+#define LED 0xa /* RW - LED control register */
+#define LED3 0xb /* RW - LED3 control register */
+#define MSR 0xc /* RW - Misc. setup register */
+#define GP2 0xd /* RW - General purpose register 2 */
+
+/* Page 3 */
+#define TPT0 0xa /* RW - Tx packet threshold, 2 registers */
+#define TPT1 0xb
+
+/* Page 4 */
+#define GP0 0x8 /* RW - General purpose register 0 */
+#define GP1 0x9 /* RW - General purpose register 1 */
+#define BV 0xa /* R - Bonding version register */
+#define EES 0xb /* RW - EEPROM control register */
+
+/* Page 5 */
+#define RHSA0 0xa /* RX host start address */
+
+/* Page 6 */
+
+/* Page 7 */
+
+/* Page 8 */
+
+/* Page 16 */
+
+/* Page 0x40 */
+#define CMD0 0x8 /* W - Receive status register */
+#define RXST0 0x9 /* RW - Receive status register */
+#define TXST0 0xb /* RW - Transmit status, 2 registers */
+#define TXST1 0xc
+#define RX0MSK 0xd /* RW - Receive status mask register */
+#define TX0MSK 0xe /* RW - Transmit status mask, 2 registers */
+#define TX1MSK 0xf /* RW - Dingo does not define this register */
+
+/* Page 0x42 */
+#define SWC0 0x8 /* RW - Software configuration, 2 registers */
+#define SWC1 0x9
+
+/* Page 0x50-0x57 */
+#define IA 0x8 /* RW - Individual address */
+
+/* CR register bits */
+#define TX_PKT 0x01 /* Transmit packet. */
+#define SOFT_RESET 0x02 /* Software reset. */
+#define ENABLE_INT 0x04 /* Enable interrupt. */
+#define FORCE_INT 0x08 /* Force interrupt. */
+#define CLR_TX_FIFO 0x10 /* Clear transmit FIFO. */
+#define CLR_RX_OVERRUN 0x20 /* Clear receive overrun. */
+#define RESTART_TX 0x40 /* Restart transmit process. */
+
+/* ESR register bits */
+#define FULL_PKT_RCV 0x01 /* Full packet received. */
+#define PKT_REJECTED 0x04 /* A packet was rejected. */
+#define TX_PKT_PEND 0x08 /* TX Packet Pending. */
+#define INCOR_POLARITY 0x10 /* XXX from linux driver, but not used there */
+#define MEDIA_SELECT 0x20 /* set if TP, clear if AUI */
+
+/* DO register bits */
+#define DO_OFF_MASK 0x1fff /* Mask for offset value. */
+#define DO_CHG_OFFSET 0x2000 /* Change offset command. */
+#define DO_SHM_MODE 0x4000 /* Shared memory mode. */
+#define DO_SKIP_RX_PKT 0x8000 /* Skip Rx packet. */
+
+/* RBC register bits */
+#define RBC_COUNT_MASK 0x1fff /* Mask for byte count. */
+#define RBC_RX_FULL 0x2000 /* Receive full packet. */
+#define RBC_RX_PARTIAL 0x4000 /* Receive partial packet. */
+#define RBC_RX_PKT_REJ 0x8000 /* Receive packet rejected. */
+
+/* ISR0(/IMR0) register bits */
+#define ISR_TX_OFLOW 0x01 /* Transmit buffer overflow. */
+#define ISR_PKT_TX 0x02 /* Packet transmitted. */
+#define ISR_MAC_INT 0x04 /* MAC interrupt. */
+#define ISR_RX_EARLY 0x10 /* Receive early packet. */
+#define ISR_RX_FULL 0x20 /* Receive full packet. */
+#define ISR_RX_PKT_REJ 0x40 /* Receive packet rejected. */
+#define ISR_FORCED_INT 0x80 /* Forced interrupt. */
+
+/* ECR register bits */
+#define ECR_EARLY_TX 0x01 /* Early transmit mode. */
+#define ECR_EARLY_RX 0x02 /* Early receive mode. */
+#define ECR_FULL_DUPLEX 0x04 /* Full duplex select. */
+#define ECR_LNK_PLS_DIS 0x20 /* Link pulse disable. */
+#define ECR_SW_COMPAT 0x80 /* Software compatibility switch. */
+
+/* GP0 register bits */
+#define GP1_WR 0x01 /* GP1 pin output value. */
+#define GP2_WR 0x02 /* GP2 pin output value. */
+#define GP1_OUT 0x04 /* GP1 pin output select. */
+#define GP2_OUT 0x08 /* GP2 pin output select. */
+#define GP1_RD 0x10 /* GP1 pin input value. */
+#define GP2_RD 0x20 /* GP2 pin input value. */
+
+/* GP1 register bits */
+#define POWER_UP 0x01 /* When 0, power down analogue part of chip. */
+
+/* LED register bits */
+#define LED0_SHIFT 0 /* LED0 Output shift & mask */
+#define LED0_MASK 0x7
+#define LED1_SHIFT 3 /* LED1 Output shift & mask */
+#define LED1_MASK 0x38
+#define LED0_RX_ENA 0x40 /* LED0 - receive enable */
+#define LED1_RX_ENA 0x80 /* LED1 - receive enable */
+
+/* LED3 register bits */
+#define LED3_SHIFT 0 /* LED0 output shift & mask */
+#define LED3_MASK 0x7
+#define LED3_RX_ENA 0x40 /* LED0 - receive enable */
+
+/* LED output values */
+#define LED_DISABLE 0 /* LED disabled */
+#define LED_COLL_ACT 1 /* Collision activity */
+#define LED_COLL_INACT 2 /* (NOT) Collision activity */
+#define LED_10MB_LINK 3 /* 10 Mb link detected */
+#define LED_100MB_LINK 4 /* 100 Mb link detected */
+#define LED_LINK 5 /* 10 Mb or 100 Mb link detected */
+#define LED_AUTO 6 /* Automatic assertion */
+#define LED_TX_ACT 7 /* Transmit activity */
+
+/* MSR register bits */
+#define SRAM_128K_EXT 0x01 /* 128K SRAM extension */
+#define RBS_BIT16 0x02 /* RBS bit 16 */
+#define SELECT_MII 0x08 /* Select MII */
+#define HASH_TBL_ENA 0x20 /* Hash table enable */
+
+/* GP2 register bits */
+#define GP3_WR 0x01 /* GP3 pin output value. */
+#define GP4_WR 0x02 /* GP4 pin output value. */
+#define GP3_OUT 0x04 /* GP3 pin output select. */
+#define GP4_OUT 0x08 /* GP4 pin output select. */
+#define GP3_RD 0x10 /* GP3 pin input value. */
+#define GP4_RD 0x20 /* GP4 pin input value. */
+
+/* RSR register bits */
+#define RSR_NOTMCAST 0x01 /* clear when multicast packet */
+#define RSR_BCAST 0x02 /* set when broadcast packet */
+#define RSR_TOO_LONG 0x04 /* set if packet is longer than 1518 octets */
+#define RSR_ALIGNERR 0x10 /* incorrect CRC and last octet not complete */
+#define RSR_CRCERR 0x20 /* incorrect CRC and last octet complete */
+#define RSR_RX_OK 0x80 /* packet received okay */
+
+/* CMD0 register bits */
+#define ONLINE 0x04 /* Online */
+#define OFFLINE 0x08 /* Online */
+#define ENABLE_RX 0x20 /* Enable reciever */
+#define DISABLE_RX 0x80 /* Disable receiver */
+
+/* RX0Msk register bits */
+#define PKT_TOO_LONG 0x02 /* Packet too long mask. */
+#define CRC_ERR 0x08 /* CRC error mask. */
+#define RX_OVERRUN 0x10 /* Receive overrun mask. */
+#define RX_ABORT 0x40 /* Receive abort mask. */
+#define RX_OK 0x80 /* Receive OK mask. */
+
+/* TX0Msk register bits */
+#define CARRIER_LOST 0x01 /* Carrier sense lost. */
+#define EXCESSIVE_COLL 0x02 /* Excessive collisions mask. */
+#define TX_UNDERRUN 0x08 /* Transmit underrun mask. */
+#define LATE_COLLISION 0x10 /* Late collision mask. */
+#define SQE 0x20 /* Signal quality error mask.. */
+#define TX_ABORT 0x40 /* Transmit abort mask. */
+#define TX_OK 0x80 /* Transmit OK mask. */
+
+/* SWC1 register bits */
+#define SWC1_IND_ADDR 0x01 /* Individual address enable. */
+#define SWC1_MCAST_PROM 0x02 /* Multicast promiscuous enable. */
+#define SWC1_PROMISC 0x04 /* Promiscuous mode enable. */
+#define SWC1_BCAST_DIS 0x08 /* Broadcast disable. */
+#define SWC1_MEDIA_SEL 0x40 /* Media select (Mohawk). */
+#define SWC1_AUTO_MEDIA 0x80 /* Automatic media select (Mohawk). */
+
+/* Misc. defines. */
+
+#define PAGE(sc, page) \
+ bus_space_write_1((sc->sc_bst), (sc->sc_bsh), (sc->sc_offset) + PR, (page))
+
+/*
+ * GP3 is connected to the MDC pin of the NS DP83840A PHY, GP4 is
+ * connected to the MDIO pin. These are utility macros to enhance
+ * readability of the code.
+ */
+#define MDC_LOW GP3_OUT
+#define MDC_HIGH (GP3_OUT | GP3_WR)
+#define MDIO_LOW GP4_OUT
+#define MDIO_HIGH (GP4_OUT | GP4_WR)
+#define MDIO GP4_RD
+
+/* Values found in MANFID. */
+#define XEMEDIA_ETHER 0x01
+#define XEMEDIA_TOKEN 0x02
+#define XEMEDIA_ARC 0x04
+#define XEMEDIA_WIRELESS 0x08
+#define XEMEDIA_MODEM 0x10
+#define XEMEDIA_GSM 0x20
+
+#define XEPROD_IDMASK 0x0f
+#define XEPROD_POCKET 0x10
+#define XEPROD_EXTERNAL 0x20
+#define XEPROD_CREDITCARD 0x40
+#define XEPROD_CARDBUS 0x80