diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-05-18 19:18:22 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-05-18 19:18:22 +0000 |
commit | e4bdc87c54aaf38993d4fb762bbf0b2eaa1f386f (patch) | |
tree | b43ac8b9c9186fd588663c342042c3e117978872 | |
parent | e24f1614a91d071845f6dab13b9115e56e342414 (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.c | 1526 | ||||
-rw-r--r-- | sys/dev/pcmcia/if_xereg.h | 305 |
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 = ⊤ + + 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 |