diff options
author | Jason Wright <jason@cvs.openbsd.org> | 2000-02-11 14:51:51 +0000 |
---|---|---|
committer | Jason Wright <jason@cvs.openbsd.org> | 2000-02-11 14:51:51 +0000 |
commit | 2c59e8bb062bad25fc115176aedafdea6ccd8da1 (patch) | |
tree | 3537f0fc36975d6625cac01420592953e907c93b /sys/dev | |
parent | 73e27c34052e023e34bf368c4d3aa63e8e0ad7fb (diff) |
Intel 82452 (Pro/1000) driver from mjacob@feral.com
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rw-r--r-- | sys/dev/pci/if_wx.c | 1991 | ||||
-rw-r--r-- | sys/dev/pci/if_wxreg.h | 317 | ||||
-rw-r--r-- | sys/dev/pci/if_wxvar.h | 407 |
4 files changed, 2721 insertions, 1 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 9bdf34912f7..0f2731a08f5 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.63 2000/02/07 00:33:03 niklas Exp $ +# $OpenBSD: files.pci,v 1.64 2000/02/11 14:51:50 jason Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -238,6 +238,11 @@ device sk: ether, ifnet, ifmedia attach sk at skc file dev/pci/if_sk.c skc | sk +# Intel 82452 (Pro/1000 gigabit server adapter) +device wx: ether, ifnet, ifmedia +attach wx at pci +file dev/pci/if_wx.c wx + # PCI "universal" communication device driver, for PCI com, lpt, etc. ports # (see documentation in the driver for what, exactly, should be supported) device puc { port = -1 } diff --git a/sys/dev/pci/if_wx.c b/sys/dev/pci/if_wx.c new file mode 100644 index 00000000000..4984c0da2b9 --- /dev/null +++ b/sys/dev/pci/if_wx.c @@ -0,0 +1,1991 @@ +/* $OpenBSD: if_wx.c,v 1.1 2000/02/11 14:51:50 jason Exp $ */ + +/* + * Copyright (c) 1999, Traakan Software + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_wx.c,v 1.5 2000/01/25 06:09:53 mjacob Exp $ + */ + +/* + * Intel Gigabit Ethernet (82452) Driver. + * Inspired by fxp driver by David Greenman for FreeBSD, and by + * Bill Paul's work in other FreeBSD network drivers. + */ + +/* + * Options + */ + +/* + * Use only every other 16 byte receive descriptor, leaving the ones + * in between empty. This card is most efficient at reading/writing + * 32 byte cache lines, so avoid all the (not working for early rev + * cards) MWI and/or READ/MODIFY/WRITE cycles updating one descriptor + * would have you do. + * + * This isn't debugged yet. + */ +/* #define PADDED_CELL 1 */ +/* + * Enable JumboGrams. This seems to work. + */ +/* #define WX_JUMBO 1 */ + +/* + * Since the includes are a mess, they'll all be in if_wxvar.h + */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/pci/if_wxvar.h> +#elif defined(__FreeBSD__) +#include <pci/if_wxvar.h> +#endif + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va)) +#endif /* __alpha__ */ + +/* + * Function Prototpes, yadda yadda... + */ + +static int wx_intr __P((void *)); +static void wx_handle_link_intr __P((wx_softc_t *)); +static void wx_handle_rxint __P((wx_softc_t *)); +static void wx_gc __P((wx_softc_t *)); +static void wx_start __P((struct ifnet *)); +static int wx_ioctl __P((struct ifnet *, IOCTL_CMD_TYPE, caddr_t)); +static int wx_ifmedia_upd __P((struct ifnet *)); +static void wx_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); +static int wx_init __P((void *)); +static void wx_hw_stop __P((wx_softc_t *)); +static void wx_set_addr __P((wx_softc_t *, int, u_int8_t *)); +static int wx_hw_initialize __P((wx_softc_t *)); +static void wx_stop __P((wx_softc_t *)); +static void wx_watchdog __P((struct ifnet *)); +static int wx_get_rbuf __P((wx_softc_t *, rxpkt_t *)); +static void wx_rxdma_map __P((wx_softc_t *, rxpkt_t *, struct mbuf *)); + +static INLINE void wx_eeprom_raise_clk __P((wx_softc_t *, u_int32_t)); +static INLINE void wx_eeprom_lower_clk __P((wx_softc_t *, u_int32_t)); +static INLINE void wx_eeprom_sobits __P((wx_softc_t *, u_int16_t, u_int16_t)); +static INLINE u_int16_t wx_eeprom_sibits __P((wx_softc_t *)); +static INLINE void wx_eeprom_cleanup __P((wx_softc_t *)); +static INLINE u_int16_t wx_read_eeprom_word __P((wx_softc_t *, int)); +static void wx_read_eeprom __P((wx_softc_t *, u_int16_t *, int, int)); + +static int wx_attach_common __P((wx_softc_t *)); +static void wx_stats_update __P((void *)); + +static INLINE void wx_mwi_whackon __P((wx_softc_t *)); +static INLINE void wx_mwi_unwhack __P((wx_softc_t *)); +static int wx_dring_setup __P((wx_softc_t *)); +static void wx_dring_teardown __P((wx_softc_t *)); + +#define WX_DISABLE_INT(sc) WRITE_CSR(sc, WXREG_IMCLR, WXDISABLE) +#define WX_ENABLE_INT(sc) WRITE_CSR(sc, WXREG_IMASK, sc->wx_ienable) + +#define JUMBOMTU (WX_MAX_PKT_SIZE_JUMBO - sizeof (struct ether_header)) +#ifdef WX_JUMBO +#define WX_MAXMTU JUMBOMTU +#else +#define WX_MAXMTU ETHERMTU +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__BROKEN_INDIRECT_CONFIG) || defined(__OpenBSD__) +#define MATCHARG void * +#else +#define MATCHARG struct cfdata * +#endif + +static int wx_match __P((struct device *, MATCHARG, void *)); +static void wx_attach __P((struct device *, struct device *, void *)); +static void wx_shutdown __P((void *)); +static int wx_ether_ioctl __P((struct ifnet *, IOCTL_CMD_TYPE, caddr_t)); +static int wx_mc_setup __P((wx_softc_t *)); +#define ether_ioctl wx_ether_ioctl + +/* + * Life *should* be simple- we only read/write 32 bit values in registers. + * Unfortunately, some platforms define bus_space functions in a fashion + * such that they cannot be used as part of a for loop, for example. + */ + +static INLINE u_int32_t _read_csr __P((struct wx_softc *, u_int32_t)); +static INLINE void _write_csr __P((struct wx_softc *, u_int32_t, u_int32_t)); + +static INLINE u_int32_t _read_csr(sc, reg) + struct wx_softc *sc; + u_int32_t reg; +{ + return bus_space_read_4(sc->w.st, sc->w.sh, reg); +} + +static INLINE void _write_csr(sc, reg, val) + struct wx_softc *sc; + u_int32_t reg; + u_int32_t val; +{ + bus_space_write_4(sc->w.st, sc->w.sh, reg, val); +} + + + +static wx_softc_t *wxlist; + +struct cfattach wx_ca = { + sizeof (wx_softc_t), wx_match, wx_attach +}; + +#ifdef __OpenBSD__ +struct cfdriver wx_cd = { + 0, "wx", DV_IFNET +}; +#endif + +/* + * Check if a device is an 82452. + */ +static int +wx_match(parent, match, aux) + struct device *parent; + MATCHARG match; + void *aux; +{ + struct pci_attach_args *pa = aux; + if (PCI_VENDOR(pa->pa_id) != WX_VENDOR_INTEL) { + return (0); + } + if (PCI_PRODUCT(pa->pa_id) != WX_PRODUCT_82452) { + return (0); + } + return (1); +} + +static void +wx_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + wx_softc_t *tmp, *sc = (wx_softc_t *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + u_int32_t data; + struct ifnet *ifp; + + sc->w.pci_pc = pa->pa_pc; + sc->w.pci_tag = pa->pa_tag; + + /* + * Map control/status registers. + */ + if (pci_mapreg_map(pa, WX_MMBA, PCI_MAPREG_TYPE_MEM, 0, + &sc->w.st, &sc->w.sh, NULL, NULL)) { + printf(": can't map registers\n"); + return; + } + printf(": Intel GigaBit Ethernet\n"); + + /* + * Allocate our interrupt. + */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->wx_name); + return; + } + intrstr = pci_intr_string(pc, ih); +#if defined(__OpenBSD__) + sc->w.ih = pci_intr_establish(pc, ih, IPL_NET, wx_intr, sc, + self->dv_xname); +#else + sc->w.ih = pci_intr_establish(pc, ih, IPL_NET, wx_intr, sc); +#endif + if (sc->w.ih == NULL) { + printf("%s: couldn't establish interrupt", sc->wx_name); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->wx_name, intrstr); + sc->revision = + pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CLASS_REG) & 0xff; + + data = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); + data &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT); + data |= (WX_CACHELINE_SIZE << PCI_CACHELINE_SHIFT); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, data); + + if (wx_attach_common(sc)) { + return; + } + + printf("%s: Ethernet address %s\n", + sc->wx_name, ether_sprintf(sc->wx_enaddr)); + + ifp = &sc->wx_if; + bcopy(sc->wx_name, ifp->if_xname, IFNAMSIZ); + ifp->if_mtu = WX_MAXMTU; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = wx_ioctl; + ifp->if_start = wx_start; + ifp->if_watchdog = wx_watchdog; + + /* + * Attach the interface. + */ + if_attach(ifp); +#ifdef __OpenBSD__ + ether_ifattach(ifp); +#else + ether_ifattach(ifp, sc->wx_enaddr); +#endif + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, + sizeof (struct ether_header)); +#endif + /* + * Add shutdown hook so that DMA is disabled prior to reboot. Not + * doing do could allow DMA to corrupt kernel memory during the + * reboot before the driver initializes. + */ + shutdownhook_establish(wx_shutdown, sc); + + tmp = wxlist; + if (tmp) { + while (tmp->wx_next) + tmp = tmp->wx_next; + tmp->wx_next = sc; + } else { + wxlist = sc; + } +} + +/* + * Device shutdown routine. Called at system shutdown after sync. The + * main purpose of this routine is to shut off receiver DMA so that + * kernel memory doesn't get clobbered during warmboot. + */ +static void +wx_shutdown(sc) + void *sc; +{ + wx_hw_stop((wx_softc_t *) sc); +} + +static int +wx_ether_ioctl(ifp, cmd, data) + struct ifnet *ifp; + IOCTL_CMD_TYPE cmd; + caddr_t data; +{ + struct ifaddr *ifa = (struct ifaddr *) data; + int error = 0; + wx_softc_t *sc = SOFTC_IFP(ifp); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + error = wx_init(sc); + if (error) { + ifp->if_flags &= ~IFF_UP; + break; + } + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: +#ifdef __OpenBSD__ + arp_ifinit(&sc->w.arpcom, ifa); +#else + arp_ifinit(ifp, ifa); +#endif + break; +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + if (ns_nullhost(*ina)) + ina->x_host = *(union ns_host *) + LLADDR(ifp->if_sadl); + else + bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl), + ifp->if_addrlen); + break; + } +#endif + default: + break; + } + + break; + + default: + error = EINVAL; + break; + } + + return (0); +} + +/* + * Program multicast addresses. + * + * This function must be called at splimp, but it may sleep. + */ +static int +wx_mc_setup(sc) + wx_softc_t *sc; +{ + struct ifnet *ifp = &sc->wx_if; + struct ether_multistep step; + struct ether_multi *enm; + + /* + * XXX: drain TX queue- use a tsleep/wakeup until done. + */ + if (sc->tactive) { + return (EBUSY); + } + + wx_stop(sc); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + sc->all_mcasts = 1; + return (wx_init(sc)); + } + +#ifdef __OpenBSD__ + ETHER_FIRST_MULTI(step, &sc->w.arpcom, enm); +#else + ETHER_FIRST_MULTI(step, &sc->w.ethercom, enm); +#endif + + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) + continue; + if (sc->wx_nmca >= WX_RAL_TAB_SIZE-1) { + sc->wx_nmca = 0; + sc->all_mcasts = 1; + break; + } + bcopy(enm->enm_addrlo, + (void *) &sc->wx_mcaddr[sc->wx_nmca++][0], 6); + ETHER_NEXT_MULTI(step, enm); + } + return (wx_init(sc)); +} + +static INLINE void +wx_mwi_whackon(sc) + wx_softc_t *sc; +{ + sc->wx_cmdw = + pci_conf_read(sc->w.pci_pc, sc->w.pci_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(sc->w.pci_pc, sc->w.pci_tag, + PCI_COMMAND_STATUS_REG, sc->wx_cmdw & ~MWI); +} + +static INLINE void +wx_mwi_unwhack(sc) + wx_softc_t *sc; +{ + if (sc->wx_cmdw & MWI) { + pci_conf_write(sc->w.pci_pc, sc->w.pci_tag, + PCI_COMMAND_STATUS_REG, sc->wx_cmdw & ~MWI); + } +} + +static int +wx_dring_setup(sc) + wx_softc_t *sc; +{ + size_t len; + + len = sizeof (wxrd_t) * WX_MAX_RDESC; + if (len > NBPG) { + printf("%s: len (%lx) over a page for the receive ring\n", + sc->wx_name, len); + return (-1); + } + len = NBPG; + sc->rdescriptors = (wxrd_t *) WXMALLOC(len); + if (sc->rdescriptors == NULL) { + printf("%s: could not allocate rcv descriptors\n", sc->wx_name); + return (-1); + } + + if (((u_long)sc->rdescriptors) & 0xfff) { + printf("%s: rcv descriptors not 4KB aligned\n", sc->wx_name); + return (-1); + } + bzero(sc->rdescriptors, len); + + len = sizeof (wxtd_t) * WX_MAX_TDESC; + if (len > NBPG) { + printf("%s: len (%lx) over a page for the xmit ring\n", + sc->wx_name, len); + return (-1); + } + len = NBPG; + sc->tdescriptors = (wxtd_t *) WXMALLOC(len); + if (sc->tdescriptors == NULL) { + printf("%s: could not allocate xmt descriptors\n", sc->wx_name); + return (-1); + } + if (((u_long)sc->tdescriptors) & 0xfff) { + printf("%s: xmt descriptors not 4KB aligned\n", sc->wx_name); + return (-1); + } + bzero(sc->tdescriptors, len); + return (0); +} + +static void +wx_dring_teardown(sc) + wx_softc_t *sc; +{ + if (sc->rdescriptors) { + WXFREE(sc->rdescriptors); + sc->rdescriptors = NULL; + } + if (sc->tdescriptors) { + WXFREE(sc->tdescriptors); + sc->tdescriptors = NULL; + } +} + +#elif defined(__FreeBSD__) +static int wx_mc_setup __P((wx_softc_t *)); +/* + * Program multicast addresses. + * + * This function must be called at splimp, but it may sleep. + */ +static int +wx_mc_setup(sc) + wx_softc_t *sc; +{ + struct ifnet *ifp = &sc->wx_if; + struct ifmultiaddr *ifma; + + /* + * XXX: drain TX queue- use a tsleep/wakeup until done. + */ + if (sc->tactive) { + return (EBUSY); + } + + wx_stop(sc); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + sc->all_mcasts = 1; + return (wx_init(sc)); + } + + for (ifma = ifp->if_multiaddrs.lh_first, sc->wx_nmca = 0; + ifma != NULL; ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) { + continue; + } + if (sc->wx_nmca >= WX_RAL_TAB_SIZE-1) { + sc->wx_nmca = 0; + sc->all_mcasts = 1; + break; + } + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + (void *) &sc->wx_mcaddr[sc->wx_nmca++][0], 6); + } + return (wx_init(sc)); +} + +/* + * Return identification string if this is device is ours. + */ +static wx_softc_t *wxlist; +static int +wx_probe(device_t dev) +{ + if ((pci_get_vendor(dev) == WX_VENDOR_INTEL) && + (pci_get_device(dev) == WX_PRODUCT_82452)) { + device_set_desc(dev, "Intel GigaBit Ethernet"); + return 0; + } + return (ENXIO); +} + +static int +wx_attach(device_t dev) +{ + int error = 0; + wx_softc_t *tmp, *sc = device_get_softc(dev); + struct ifnet *ifp; + int s; + u_long val; + int rid; + + bzero(sc, sizeof (wx_softc_t)); + + callout_handle_init(&sc->w.sch); + sc->w.dev = dev; + + if (getenv_int("wx_debug", &rid)) { + if (rid & (1 << device_get_unit(dev))) { + sc->wx_debug = 1; + } + } + + if (getenv_int("wx_no_ilos", &rid)) { + if (rid & (1 << device_get_unit(dev))) { + sc->wx_no_ilos = 1; + } + } + + if (getenv_int("wx_ilos", &rid)) { + if (rid & (1 << device_get_unit(dev))) { + sc->wx_ilos = 1; + } + } + + if (getenv_int("wx_no_flow", &rid)) { + if (rid & (1 << device_get_unit(dev))) { + sc->wx_no_flow = 1; + } + } + + s = splimp(); + /* + * Enable bus mastering, make sure that the cache line size is right. + */ + val = pci_read_config(dev, PCIR_COMMAND, 2); + val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, val, 2); + + val = pci_read_config(dev, PCIR_CACHELNSZ, 1); + if (val != 0x10) { + pci_write_config(dev, PCIR_CACHELNSZ, 0x10, 1); + } + + /* + * get revision + */ + sc->revision = pci_read_config(dev, PCIR_CLASS, 1); + + /* + * Map control/status registers. + */ + rid = WX_MMBA; + sc->w.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, 0, ~0, 1, RF_ACTIVE); + if (!sc->w.mem) { + device_printf(dev, "could not map memory\n"); + error = ENXIO; + goto out; + } + sc->w.st = rman_get_bustag(sc->w.mem); + sc->w.sh = rman_get_bushandle(sc->w.mem); + + rid = 0; + sc->w.irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (sc->w.irq == NULL) { + device_printf(dev, "could not map interrupt\n"); + error = ENXIO; + goto out; + } + error = bus_setup_intr(dev, sc->w.irq, INTR_TYPE_NET, + (void (*)(void *))wx_intr, sc, &sc->w.ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + goto out; + } + if (wx_attach_common(sc)) { + bus_teardown_intr(dev, sc->w.irq, sc->w.ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->w.irq); + bus_release_resource(dev, SYS_RES_MEMORY, WX_MMBA, sc->w.mem); + error = ENXIO; + goto out; + } + device_printf(dev, "Ethernet address %02x:%02x:%02x:%02x:%02x:%02x\n", + sc->w.arpcom.ac_enaddr[0], sc->w.arpcom.ac_enaddr[1], + sc->w.arpcom.ac_enaddr[2], sc->w.arpcom.ac_enaddr[3], + sc->w.arpcom.ac_enaddr[4], sc->w.arpcom.ac_enaddr[5]); + (void) snprintf(sc->wx_name, sizeof (sc->wx_name) - 1, "wx%d", + device_get_unit(dev)); + ifp = &sc->w.arpcom.ac_if; + ifp->if_unit = device_get_unit(dev); + ifp->if_name = "wx"; + ifp->if_mtu = WX_MAXMTU; + ifp->if_output = ether_output; + ifp->if_baudrate = 1000000000; + ifp->if_init = (void (*)(void *))wx_init; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = wx_ioctl; + ifp->if_start = wx_start; + ifp->if_watchdog = wx_watchdog; + if_attach(ifp); + ifp->if_snd.ifq_maxlen = WX_MAX_TDESC - 1; + ether_ifattach(ifp); + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + tmp = wxlist; + if (tmp) { + while (tmp->wx_next) + tmp = tmp->wx_next; + tmp->wx_next = sc; + } else { + wxlist = sc; + } +out: + splx(s); + return (error); +} + +static int +wx_detach(device_t dev) +{ + wx_softc_t *sc = device_get_softc(dev); + int s = splimp(); + if_detach(&sc->w.arpcom.ac_if); + wx_stop(sc); + bus_teardown_intr(dev, sc->w.irq, sc->w.ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->w.irq); + bus_release_resource(dev, SYS_RES_MEMORY, WX_MMBA, sc->w.mem); + splx(s); + return (0); +} + +static int +wx_shutdown(device_t dev) +{ + wx_hw_stop((wx_softc_t *) device_get_softc(dev)); + return (0); +} + +static INLINE void +wx_mwi_whackon(sc) + wx_softc_t *sc; +{ + sc->wx_cmdw = pci_read_config(sc->w.dev, PCIR_COMMAND, 2); + pci_write_config(sc->w.dev, PCIR_COMMAND, sc->wx_cmdw & ~MWI, 2); +} + +static INLINE void +wx_mwi_unwhack(sc) + wx_softc_t *sc; +{ + if (sc->wx_cmdw & MWI) { + pci_write_config(sc->w.dev, PCIR_COMMAND, sc->wx_cmdw, 2); + } +} + +static int +wx_dring_setup(sc) + wx_softc_t *sc; +{ + size_t len; + + len = sizeof (wxrd_t) * WX_MAX_RDESC; + sc->rdescriptors = (wxrd_t *) + contigmalloc(len, M_DEVBUF, M_NOWAIT, 0, ~0, 4096, 0); + if (sc->rdescriptors == NULL) { + printf("%s: could not allocate rcv descriptors\n", sc->wx_name); + return (-1); + } + if (((u_long)sc->rdescriptors) & 0xfff) { + printf("%s: rcv descriptors not 4KB aligned\n", sc->wx_name); + return (-1); + } + bzero(sc->rdescriptors, len); + + len = sizeof (wxtd_t) * WX_MAX_TDESC; + sc->tdescriptors = (wxtd_t *) + contigmalloc(len, M_DEVBUF, M_NOWAIT, 0, ~0, 4096, 0); + if (sc->tdescriptors == NULL) { + printf("%s: could not allocate xmt descriptors\n", sc->wx_name); + return (-1); + } + if (((u_long)sc->tdescriptors) & 0xfff) { + printf("%s: xmt descriptors not 4KB aligned\n", sc->wx_name); + return (-1); + } + bzero(sc->tdescriptors, len); + return (0); +} + +static void +wx_dring_teardown(sc) + wx_softc_t *sc; +{ + if (sc->rdescriptors) { + WXFREE(sc->rdescriptors); + sc->rdescriptors = NULL; + } + if (sc->tdescriptors) { + WXFREE(sc->tdescriptors); + sc->tdescriptors = NULL; + } +} + +static device_method_t wx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, wx_probe), + DEVMETHOD(device_attach, wx_attach), + DEVMETHOD(device_detach, wx_detach), + DEVMETHOD(device_shutdown, wx_shutdown), + { 0, 0 } +}; + +static driver_t wx_driver = { + "wx", wx_methods, sizeof(wx_softc_t), +}; +static devclass_t wx_devclass; +DRIVER_MODULE(if_wx, pci, wx_driver, wx_devclass, 0, 0); + +#endif + +/* + * Do generic parts of attach. Our registers have been mapped + * and our interrupt registered. + */ +static int +wx_attach_common(sc) + wx_softc_t *sc; +{ + size_t len; + u_int32_t tmp; + int ll = 0; + + /* + * First, reset the chip. + */ + wx_hw_stop(sc); + + /* + * Second, validate our EEPROM. + */ + + /* TBD */ + + /* + * Third, read eeprom for our MAC address and other things. + */ + wx_read_eeprom(sc, (u_int16_t *)sc->wx_enaddr, WX_EEPROM_MAC_OFF, 3); + + /* + * Fourth, establish some adapter parameters. + */ + sc->wx_txint_delay = 128; + ifmedia_init(&sc->wx_media, IFM_IMASK, wx_ifmedia_upd, wx_ifmedia_sts); + ifmedia_add(&sc->wx_media, IFM_ETHER|IFM_1000_SX, 0, NULL); + ifmedia_add(&sc->wx_media, IFM_ETHER|IFM_1000_SX|IFM_FDX, 0, NULL); + ifmedia_set(&sc->wx_media, IFM_ETHER|IFM_1000_SX|IFM_FDX); + sc->wx_media.ifm_media = sc->wx_media.ifm_cur->ifm_media; + ll += 1; + + /* + * Fifth, establish a default device control register word. + */ + sc->wx_dcr = 0; + if (sc->wx_cfg1 & WX_EEPROM_CTLR1_FD) + sc->wx_dcr |= WXDCR_FD; + if (sc->wx_cfg1 & WX_EEPROM_CTLR1_ILOS) + sc->wx_dcr |= WXDCR_ILOS; + + tmp = (sc->wx_cfg1 >> WX_EEPROM_CTLR1_SWDPIO_SHIFT) & WXDCR_SWDPIO_MASK; + sc->wx_dcr |= (tmp << WXDCR_SWDPIO_SHIFT); + + if (sc->wx_no_ilos) + sc->wx_dcr &= ~WXDCR_ILOS; + if (sc->wx_ilos) + sc->wx_dcr |= WXDCR_ILOS; + if (sc->wx_no_flow == 0) + sc->wx_dcr |= WXDCR_RFCE | WXDCR_TFCE; + + + /* + * Sixth, allocate various sw structures... + */ + len = sizeof (rxpkt_t) * WX_MAX_RDESC; + sc->rbase = (rxpkt_t *) WXMALLOC(len); + if (sc->rbase == NULL) { + goto fail; + } + bzero(sc->rbase, len); + ll += 1; + + len = sizeof (txpkt_t) * WX_MAX_TDESC; + sc->tbase = (txpkt_t *) WXMALLOC(len); + if (sc->tbase == NULL) { + goto fail; + } + bzero(sc->tbase, len); + ll += 1; + + /* + * Seventh, allocate and dma map (platform dependent) descriptor rings. + * They have to be aligned on a 4KB boundary. + */ + if (wx_dring_setup(sc) == 0) { + return (0); + } + +fail: + printf("%s: failed to do common attach (%d)\n", sc->wx_name, ll); + wx_dring_teardown(sc); + if (sc->rbase) { + WXFREE(sc->rbase); + sc->rbase = NULL; + } + if (sc->tbase) { + WXFREE(sc->tbase); + sc->tbase = NULL; + } + return (ENOMEM); +} + +/* + * EEPROM functions. + */ + +static INLINE void +wx_eeprom_raise_clk(sc, regval) + wx_softc_t *sc; + u_int32_t regval; +{ + WRITE_CSR(sc, WXREG_EECDR, regval | WXEECD_SK); + DELAY(50); +} + +static INLINE void +wx_eeprom_lower_clk(sc, regval) + wx_softc_t *sc; + u_int32_t regval; +{ + WRITE_CSR(sc, WXREG_EECDR, regval & ~WXEECD_SK); + DELAY(50); +} + +static INLINE void +wx_eeprom_sobits(sc, data, count) + wx_softc_t *sc; + u_int16_t data; + u_int16_t count; +{ + u_int32_t regval, mask; + + mask = 1 << (count - 1); + regval = READ_CSR(sc, WXREG_EECDR) & ~(WXEECD_DI|WXEECD_DO); + + do { + if (data & mask) + regval |= WXEECD_DI; + else + regval &= ~WXEECD_DI; + WRITE_CSR(sc, WXREG_EECDR, regval); DELAY(50); + wx_eeprom_raise_clk(sc, regval); + wx_eeprom_lower_clk(sc, regval); + mask >>= 1; + } while (mask != 0); + WRITE_CSR(sc, WXREG_EECDR, regval & ~WXEECD_DI); +} + + +static INLINE u_int16_t +wx_eeprom_sibits(sc) + wx_softc_t *sc; +{ + unsigned int regval, i; + u_int16_t data; + + data = 0; + regval = READ_CSR(sc, WXREG_EECDR) & ~(WXEECD_DI|WXEECD_DO); + for (i = 0; i != 16; i++) { + data <<= 1; + wx_eeprom_raise_clk(sc, regval); + regval = READ_CSR(sc, WXREG_EECDR) & ~WXEECD_DI; + if (regval & WXEECD_DO) { + data |= 1; + } + wx_eeprom_lower_clk(sc, regval); + } + return (data); +} + + + +static INLINE void +wx_eeprom_cleanup(sc) + wx_softc_t *sc; +{ + u_int32_t regval; + regval = READ_CSR(sc, WXREG_EECDR) & ~(WXEECD_DI|WXEECD_CS); + WRITE_CSR(sc, WXREG_EECDR, regval); DELAY(50); + wx_eeprom_raise_clk(sc, regval); + wx_eeprom_lower_clk(sc, regval); +} + +static u_int16_t INLINE +wx_read_eeprom_word(sc, offset) + wx_softc_t *sc; + int offset; +{ + u_int16_t data; + WRITE_CSR(sc, WXREG_EECDR, WXEECD_CS); + wx_eeprom_sobits(sc, EEPROM_READ_OPCODE, 3); + wx_eeprom_sobits(sc, offset, 6); + data = wx_eeprom_sibits(sc); + wx_eeprom_cleanup(sc); + return (data); +} + +static void +wx_read_eeprom(sc, data, offset, words) + wx_softc_t *sc; + u_int16_t *data; + int offset; + int words; +{ + int i; + for (i = 0; i < words; i++) { + *data++ = wx_read_eeprom_word(sc, offset++); + } + sc->wx_cfg1 = wx_read_eeprom_word(sc, WX_EEPROM_CTLR1_OFF); +} + +/* + * Start packet transmission on the interface. + */ + +static void +wx_start(ifp) + struct ifnet *ifp; +{ + wx_softc_t *sc = SOFTC_IFP(ifp); + u_int16_t cidx, nactv; + + nactv = sc->tactive; + while (nactv < WX_MAX_TDESC) { + int ndesc; + int gctried = 0; + struct mbuf *m, *mb_head; + + IF_DEQUEUE(&ifp->if_snd, mb_head); + if (mb_head == NULL) { + break; + } + sc->wx_xmitwanted++; + + /* + * If we have a packet less than ethermin, pad it out. + */ + if (mb_head->m_pkthdr.len < WX_MIN_RPKT_SIZE) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + m_freem(mb_head); + break; + } + m_copydata(mb_head, 0, mb_head->m_pkthdr.len, + mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = WX_MIN_RPKT_SIZE; + bzero(mtod(m, char *) + mb_head->m_pkthdr.len, + WX_MIN_RPKT_SIZE - mb_head->m_pkthdr.len); + sc->wx_xmitpullup++; + m_freem(mb_head); + mb_head = m; + } +again: + cidx = sc->tnxtfree; + nactv = sc->tactive; + + + /* + * Go through each of the mbufs in the chain and initialize + * the transmit buffer descriptors with the physical address + * and size of that mbuf. If we have a length less than our + * minimum transmit size, we bail (to do a pullup). If we run + * out of descriptors, we also bail and try and do a pullup. + */ + for (ndesc = 0, m = mb_head; m != NULL; m = m->m_next) { + vm_offset_t vptr; + wxtd_t *td; + + /* + * If this mbuf has no data, skip it. + */ + if (m->m_len == 0) { + continue; + } + + /* + * If this packet is too small for the chip's minimum, + * break out to to cluster it. + */ + if (m->m_len < WX_MIN_RPKT_SIZE) { + sc->wx_xmitrunt++; + break; + } + + /* + * Do we have a descriptor available for this mbuf? + */ + if (++nactv == WX_MAX_TDESC) { + if (gctried++ == 0) { + sc->wx_xmitgc++; + wx_gc(sc); + goto again; + } + break; + } + sc->tbase[cidx].dptr = m; + td = &sc->tdescriptors[cidx]; + td->length = m->m_len; + + vptr = mtod(m, vm_offset_t); + td->address.highpart = 0; + td->address.lowpart = vtophys(vptr); + + td->cso = 0; + td->status = 0; + td->special = 0; + td->cmd = 0; + td->css = 0; + + if (sc->wx_debug) { + printf("%s: XMIT[%d] %p vptr %lx (length %d " + "DMA addr %x) idx %d\n", sc->wx_name, + ndesc, m, (long) vptr, td->length, + td->address.lowpart, cidx); + } + ndesc++; + cidx = T_NXT_IDX(cidx); + } + + /* + * If we get here and m is NULL, we can send + * the the packet chain described by mb_head. + */ + if (m == NULL) { + /* + * Mark the last descriptor with EOP and tell the + * chip to insert a final checksum. + */ + wxtd_t *td = &sc->tdescriptors[T_PREV_IDX(cidx)]; + td->cmd = TXCMD_EOP|TXCMD_IFCS; + + sc->tbase[sc->tnxtfree].sidx = sc->tnxtfree; + sc->tbase[sc->tnxtfree].eidx = cidx; + sc->tbase[sc->tnxtfree].next = NULL; + if (sc->tbsyf) { + sc->tbsyl->next = &sc->tbase[sc->tnxtfree]; + } else { + sc->tbsyf = &sc->tbase[sc->tnxtfree]; + } + sc->tbsyl = &sc->tbase[sc->tnxtfree]; + sc->tnxtfree = cidx; + sc->tactive = nactv; + ifp->if_timer = 10; + WRITE_CSR(sc, WXREG_TDT, cidx); + if (ifp->if_bpf) + bpf_mtap(WX_BPFTAP_ARG(ifp), mb_head); + continue; + } + + /* + * Otherwise, we couldn't send this packet for some reason. + * + * If don't have a descriptor available, and this is a + * single mbuf packet, freeze output so that later we + * can restart when we have more room. Otherwise, we'll + * try and cluster the request. We've already tried to + * garbage collect completed descriptors. + */ + if (nactv == WX_MAX_TDESC && mb_head->m_next == NULL) { + sc->wx_xmitputback++; + ifp->if_flags |= IFF_OACTIVE; + IF_PREPEND(&ifp->if_snd, mb_head); + break; + } + + /* + * Otherwise, it's either a fragment length somewhere in the + * chain that isn't at least WX_MIN_XPKT_SIZE in length or + * the number of fragments exceeds the number of descriptors + * available. + * + * We could try a variety of strategies here- if this is + * a length problem for single mbuf packet or a length problem + * for the last mbuf in a chain (we could just try and adjust + * it), but it's just simpler to try and cluster it. + */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + m_freem(mb_head); + break; + } + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + m_freem(mb_head); + break; + } + m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = mb_head->m_pkthdr.len; + m_freem(mb_head); + mb_head = m; + sc->wx_xmitcluster++; + goto again; + } + + if (sc->tactive == WX_MAX_TDESC) { + sc->wx_xmitblocked++; + ifp->if_flags |= IFF_OACTIVE; + } +} + +/* + * Process interface interrupts. + */ +static int +wx_intr(arg) + void *arg; +{ + wx_softc_t *sc = arg; + int claimed = 0; + + /* + * Read interrupt cause register. Reading it clears bits. + */ + sc->wx_icr = READ_CSR(sc, WXREG_ICR); + if (sc->wx_icr) { + claimed++; + WX_DISABLE_INT(sc); + sc->wx_intr++; + if (sc->wx_icr & (WXISR_LSC|WXISR_RXSEQ)) { + wx_handle_link_intr(sc); + } + wx_handle_rxint(sc); + if (sc->tactive) { + wx_gc(sc); + } + if (sc->wx_if.if_snd.ifq_head != NULL) { + wx_start(&sc->wx_if); + } + WX_ENABLE_INT(sc); + } + return (claimed); +} + +static void +wx_handle_link_intr(sc) + wx_softc_t *sc; +{ + sc->wx_linkintr++; + printf("%s: link intr 0x%x\n", sc->wx_name, sc->wx_icr); +} + +static void +wx_handle_rxint(sc) + wx_softc_t *sc; +{ + struct ether_header *eh; + struct mbuf *m0, *mb, *pending[WX_MAX_RDESC]; + struct ifnet *ifp = &sc->wx_if; + int npkts, ndesc, lidx, idx, tlen; + + for (m0 = sc->rpending, tlen = ndesc = npkts = 0, idx = sc->rnxt, + lidx = R_PREV_IDX(idx); ndesc < WX_MAX_RDESC; + ndesc++, lidx = idx, idx = R_NXT_IDX(idx)) { + wxrd_t *rd; + rxpkt_t *rxpkt; + int length, offset, lastframe; + + rd = &sc->rdescriptors[idx]; + if ((rd->status & RDSTAT_DD) == 0) { + if (m0) { + if (sc->rpending == NULL) { + m0->m_pkthdr.len = tlen; + sc->rpending = m0; + } else { + m_freem(m0); + } + m0 = NULL; + } + if (sc->wx_debug) { + printf("WXRX: ndesc %d idx %d lidx %d\n", + ndesc, idx, lidx); + } + break; + } + + if (rd->errors != 0) { + printf("%s: packet with errors (%x)\n", + sc->wx_name, rd->errors); + rd->status = 0; + ifp->if_ierrors++; + if (m0) { + m_freem(m0); + m0 = NULL; + if (sc->rpending) { + m_freem(sc->rpending); + sc->rpending = NULL; + } + } + continue; + } + + + rxpkt = &sc->rbase[idx]; + mb = rxpkt->dptr; + if (mb == NULL) { + printf("%s: receive descriptor with no mbuf\n", + sc->wx_name); + (void) wx_get_rbuf(sc, rxpkt); + rd->status = 0; + ifp->if_ierrors++; + if (m0) { + m_freem(m0); + m0 = NULL; + if (sc->rpending) { + m_freem(sc->rpending); + sc->rpending = NULL; + } + } + continue; + } + + /* XXX: Flush DMA for rxpkt */ + + if (wx_get_rbuf(sc, rxpkt)) { + sc->wx_rxnobuf++; + wx_rxdma_map(sc, rxpkt, mb); + ifp->if_ierrors++; + rd->status = 0; + if (m0) { + m_freem(m0); + m0 = NULL; + if (sc->rpending) { + m_freem(sc->rpending); + sc->rpending = NULL; + } + } + continue; + } + + /* + * Save the completing packet's offset value and length + * and install the new one into the descriptor. + */ + lastframe = (rd->status & RDSTAT_EOP) != 0; + length = rd->length; + offset = rd->address.lowpart & 0xff; + bzero (rd, sizeof (*rd)); + rd->address.lowpart = rxpkt->dma_addr + WX_RX_OFFSET_VALUE; + + mb->m_len = length; + mb->m_data += offset; + mb->m_next = NULL; + if (m0 == NULL) { + m0 = mb; + tlen = length; + } else if (m0 == sc->rpending) { + /* + * Pick up where we left off before. If + * we have an offset (we're assuming the + * first frame has an offset), then we've + * lost sync somewhere along the line. + */ + if (offset) { + printf("%s: lost sync with partial packet\n", + sc->wx_name); + m_freem(sc->rpending); + sc->rpending = NULL; + m0 = mb; + tlen = length; + } else { + sc->rpending = NULL; + tlen = m0->m_pkthdr.len; + } + } else { + tlen += length; + } + + if (sc->wx_debug) { + printf("%s: RDESC[%d] len %d off %d lastframe %d\n", + sc->wx_name, idx, mb->m_len, offset, lastframe); + } + if (m0 != mb) + m_cat(m0, mb); + if (lastframe == 0) { + continue; + } + m0->m_pkthdr.rcvif = ifp; + m0->m_pkthdr.len = tlen - WX_CRC_LENGTH; + mb->m_len -= WX_CRC_LENGTH; + +#ifdef __OpenBSD__ + pending[npkts++] = m0; +#else + eh = mtod(m0, struct ether_header *); + if ((ifp->if_flags & IFF_PROMISC) && + (bcmp(eh->ether_dhost, sc->wx_enaddr, ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0)) { + m_freem(m0); + if (sc->rpending) { + m_freem(sc->rpending); + sc->rpending = NULL; + } + } else { + pending[npkts++] = m0; + } +#endif + m0 = NULL; + tlen = 0; + } + + if (ndesc) { + WRITE_CSR(sc, WXREG_RDT0, lidx); + sc->rnxt = idx; + } + + if (npkts) { + sc->wx_rxintr++; + } + + for (idx = 0; idx < npkts; idx++) { + mb = pending[idx]; + if (ifp->if_bpf) { + bpf_mtap(WX_BPFTAP_ARG(ifp), mb); + } + ifp->if_ipackets++; + if (sc->wx_debug) { + printf("%s: RECV packet length %d\n", + sc->wx_name, mb->m_pkthdr.len); + } +#if defined(__FreeBSD__) || defined(__OpenBSD__) + eh = mtod(mb, struct ether_header *); + m_adj(mb, sizeof (struct ether_header)); + ether_input(ifp, eh, mb); +#else + (*ifp->if_input)(ifp, mb); +#endif + } +} + +static void +wx_gc(sc) + wx_softc_t *sc; +{ + struct ifnet *ifp = &sc->wx_if; + txpkt_t *txpkt = sc->tbsyf; + u_int32_t tdh = READ_CSR(sc, WXREG_TDH); + int s; + + s = splimp(); + while (txpkt != NULL) { + u_int32_t end = txpkt->eidx, cidx = tdh; + + /* + * Normalize start..end indices to 2 * + * WX_MAX_TDESC range to eliminate wrap. + */ + if (txpkt->eidx < txpkt->sidx) { + end += WX_MAX_TDESC; + } + + /* + * Normalize current chip index to 2 * + * WX_MAX_TDESC range to eliminate wrap. + */ + if (cidx < txpkt->sidx) { + cidx += WX_MAX_TDESC; + } + + /* + * If the current chip index is between low and + * high indices for this packet, it's not finished + * transmitting yet. Because transmits are done FIFO, + * this means we're done garbage collecting too. + */ + + if (txpkt->sidx <= cidx && cidx < txpkt->eidx) { + if (sc->wx_debug) { + printf("%s: TXGC %d..%d TDH %d\n", sc->wx_name, + txpkt->sidx, txpkt->eidx, tdh); + } + break; + } + ifp->if_opackets++; + + if (txpkt->dptr) { + (void) m_freem(txpkt->dptr); + } else { + printf("%s: null mbuf in gc\n", sc->wx_name); + } + + for (cidx = txpkt->sidx; cidx != txpkt->eidx; + cidx = T_NXT_IDX(cidx)) { + txpkt_t *tmp; + wxtd_t *td; + + td = &sc->tdescriptors[cidx]; + if (td->status & TXSTS_EC) { + printf("%s: excess collisions\n", sc->wx_name); + ifp->if_collisions++; + ifp->if_oerrors++; + } + if (td->status & TXSTS_LC) { + printf("%s: lost carrier\n", sc->wx_name); + ifp->if_oerrors++; + } + tmp = &sc->tbase[cidx]; + if (sc->wx_debug) { + printf("%s: TXGC[%d] %p %d..%d done nact %d " + "TDH %d\n", sc->wx_name, cidx, tmp->dptr, + txpkt->sidx, txpkt->eidx, sc->tactive, tdh); + } + tmp->dptr = NULL; + if (sc->tactive == 0) { + printf("%s: nactive < 0?\n", sc->wx_name); + } else { + sc->tactive -= 1; + } + bzero(td, sizeof (*td)); + } + sc->tbsyf = txpkt->next; + txpkt = sc->tbsyf; + } + if (sc->tactive < WX_MAX_TDESC) { + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + } + splx(s); +} + +/* + * Update packet in/out/collision statistics. + */ +static void +wx_stats_update(arg) + void *arg; +{ + wx_softc_t *sc = arg; + int s; + + s = splimp(); + wx_gc(sc); + splx(s); + + /* + * Schedule another timeout one second from now. + */ + TIMEOUT(sc, wx_stats_update, sc, hz); +} + +/* + * Stop and reinitialize the hardware + */ +static void +wx_hw_stop(sc) + wx_softc_t *sc; +{ + u_int32_t icr; + if (sc->revision == 2) { + wx_mwi_whackon(sc); + } + WRITE_CSR(sc, WXREG_DCR, WXDCR_RST); + DELAY(20 * 1000); + WRITE_CSR(sc, WXREG_IMASK, ~0); + icr = READ_CSR(sc, WXREG_ICR); + if (sc->revision == 2) { + wx_mwi_unwhack(sc); + } + WX_DISABLE_INT(sc); +} + +static void +wx_set_addr(sc, idx, mac) + wx_softc_t *sc; + int idx; + u_int8_t *mac; +{ + u_int32_t t0, t1; + t0 = (mac[0]) | (mac[1] << 8) | (mac[2] << 16) | (mac[3] << 24); + t1 = (mac[4] << 0) | (mac[5] << 8); + t1 |= WX_RAL_AV; + WRITE_CSR(sc, WXREG_RAL_LO(idx), t0); + WRITE_CSR(sc, WXREG_RAL_HI(idx), t1); +} + +static int +wx_hw_initialize(sc) + wx_softc_t *sc; +{ + int i; + + WRITE_CSR(sc, WXREG_VET, 0); + for (i = 0; i < (WX_VLAN_TAB_SIZE << 2); i += 4) { + WRITE_CSR(sc, (WXREG_VFTA + i), 0); + } + if (sc->revision == 2) { + wx_mwi_whackon(sc); + WRITE_CSR(sc, WXREG_RCTL, WXRCTL_RST); + DELAY(5 * 1000); + } + /* + * Load the first receiver address with our MAC address, + * and load as many multicast addresses as can fit into + * the receive address array. + */ + wx_set_addr(sc, 0, sc->wx_enaddr); + for (i = 1; i <= sc->wx_nmca; i++) { + if (i >= WX_RAL_TAB_SIZE) { + break; + } else { + wx_set_addr(sc, i, sc->wx_mcaddr[i-1]); + } + } + + while (i < WX_RAL_TAB_SIZE) { + WRITE_CSR(sc, WXREG_RAL_LO(i), 0); + WRITE_CSR(sc, WXREG_RAL_HI(i), 0); + i++; + } + + if (sc->revision == 2) { + WRITE_CSR(sc, WXREG_RCTL, 0); + DELAY(1 * 1000); + wx_mwi_unwhack(sc); + } + + /* + * Clear out the hashed multicast table array. + */ + for (i = 0; i < WX_MC_TAB_SIZE; i++) { + WRITE_CSR(sc, WXREG_MTA + (sizeof (u_int32_t) * 4), 0); + } + if (sc->wx_dcr & (WXDCR_RFCE|WXDCR_TFCE)) { + WRITE_CSR(sc, WXREG_FCAL, FC_FRM_CONST_LO); + WRITE_CSR(sc, WXREG_FCAH, FC_FRM_CONST_HI); + WRITE_CSR(sc, WXREG_FCT, FC_TYP_CONST); + } else { + WRITE_CSR(sc, WXREG_FCAL, 0); + WRITE_CSR(sc, WXREG_FCAH, 0); + WRITE_CSR(sc, WXREG_FCT, 0); + } + WRITE_CSR(sc, WXREG_FLOW_XTIMER, WX_XTIMER_DFLT); + if (sc->revision == 2) { + WRITE_CSR(sc, WXREG_FLOW_RCV_HI, 0); + WRITE_CSR(sc, WXREG_FLOW_RCV_LO, 0); + } else { + WRITE_CSR(sc, WXREG_FLOW_RCV_HI, WX_RCV_FLOW_HI_DFLT); + WRITE_CSR(sc, WXREG_FLOW_RCV_LO, WX_RCV_FLOW_LO_DFLT); + } + WRITE_CSR(sc, WXREG_XMIT_CFGW, WXTXCW_DEFAULT); + WRITE_CSR(sc, WXREG_DCR, sc->wx_dcr); + DELAY(50 * 1000); + + /* + * The pin stuff is all FM from the Linux driver. + */ + if ((READ_CSR(sc, WXREG_DCR) & WXDCR_SWDPIN1) == 0) { + for (i = 0; i < 500; i++) { + DELAY(10 * 1000); + if (READ_CSR(sc, WXREG_DSR) & WXDSR_LU) { + sc->linkup = 1; + break; + } + } + } else { + printf("%s: swdpio did not clear\n", sc->wx_name); + return (-1); + } + if (sc->linkup == 0) { + printf("%s: link never came up\n", sc->wx_name); + return (-1); + } + sc->wx_ienable = WXIENABLE_DEFAULT; + return (0); +} + +/* + * Stop the interface. Cancels the statistics updater and resets the interface. + */ +static void +wx_stop(sc) + wx_softc_t *sc; +{ + txpkt_t *txp; + rxpkt_t *rxp; + struct ifnet *ifp = &sc->wx_if; + + /* + * Cancel stats updater. + */ + UNTIMEOUT(wx_stats_update, sc, sc); + + /* + * Reset the chip + */ + wx_hw_stop(sc); + + /* + * Release any xmit buffers. + */ + for (txp = sc->tbase; txp && txp < &sc->tbase[WX_MAX_TDESC]; txp++) { + if (txp->dptr) { + m_free(txp->dptr); + txp->dptr = NULL; + } + } + + /* + * Free all the receive buffers. + */ + for (rxp = sc->rbase; rxp && rxp < &sc->rbase[WX_MAX_RDESC]; rxp++) { + if (rxp->dptr) { + m_free(rxp->dptr); + rxp->dptr = NULL; + } + } + + if (sc->rpending) { + m_freem(sc->rpending); + sc->rpending = NULL; + } + + /* + * And we're outta here... + */ + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; +} + +/* + * Watchdog/transmission transmit timeout handler. + */ +static void +wx_watchdog(ifp) + struct ifnet *ifp; +{ + wx_softc_t *sc = SOFTC_IFP(ifp); + printf("%s: device timeout\n", sc->wx_name); + ifp->if_oerrors++; + if (wx_init(sc)) { + printf("%s: could not re-init device\n", sc->wx_name); + VTIMEOUT(sc, (void (*)(void *))wx_init, sc, hz); + } +} + +static int +wx_init(xsc) + void *xsc; +{ + struct ifmedia *ifm; + wx_softc_t *sc = xsc; + struct ifnet *ifp = &sc->wx_if; + rxpkt_t *rxpkt; + wxrd_t *rd; + size_t len; + int s, i, bflags; + + s = splimp(); + + /* + * Cancel any pending I/O by resetting things. + * wx_stop will free any allocated mbufs. + */ + wx_stop(sc); + + /* + * Reset the hardware. All network addresses loaded here, but + * neither the receiver nor the transmitter are enabled. + */ + + if (wx_hw_initialize(sc)) { + return (EIO); + } + + /* + * Set up the receive ring stuff. + */ + len = sizeof (wxrd_t) * WX_MAX_RDESC; + bzero(sc->rdescriptors, len); + for (rxpkt = sc->rbase, i = 0; rxpkt != NULL && i < WX_MAX_RDESC; + i += RXINCR, rxpkt++) { + rd = &sc->rdescriptors[i]; + if (wx_get_rbuf(sc, rxpkt)) { + break; + } + rd->address.lowpart = rxpkt->dma_addr + WX_RX_OFFSET_VALUE; + } + if (i != WX_MAX_RDESC) { + printf("%s: could not set up rbufs\n", sc->wx_name); + wx_stop(sc); + return (ENOMEM); + } + + /* + * Set up transmit parameters and enable the transmitter. + */ + sc->tnxtfree = sc->tactive = 0; + sc->tbsyf = sc->tbsyl = NULL; + WRITE_CSR(sc, WXREG_TCTL, 0); + DELAY(5 * 1000); + WRITE_CSR(sc, WXREG_TDBA_LO, + vtophys((vm_offset_t)&sc->tdescriptors[0])); + WRITE_CSR(sc, WXREG_TDBA_HI, 0); + WRITE_CSR(sc, WXREG_TDLEN, WX_MAX_TDESC * sizeof (wxtd_t)); + WRITE_CSR(sc, WXREG_TDH, 0); + WRITE_CSR(sc, WXREG_TDT, 0); + WRITE_CSR(sc, WXREG_TQSA_HI, 0); + WRITE_CSR(sc, WXREG_TQSA_LO, 0); + WRITE_CSR(sc, WXREG_TIPG, WX_TIPG_DFLT); + WRITE_CSR(sc, WXREG_TIDV, sc->wx_txint_delay); + WRITE_CSR(sc, WXREG_TCTL, (WXTCTL_CT(WX_COLLISION_THRESHOLD) | + WXTCTL_COLD(WX_FDX_COLLISION_DX) | WXTCTL_EN)); + /* + * Set up receive parameters and enable the receiver. + */ + + sc->rnxt = 0; + WRITE_CSR(sc, WXREG_RCTL, 0); + DELAY(5 * 1000); + WRITE_CSR(sc, WXREG_RDTR0, WXRDTR_FPD); + WRITE_CSR(sc, WXREG_RDBA0_LO, + vtophys((vm_offset_t)&sc->rdescriptors[0])); + WRITE_CSR(sc, WXREG_RDBA0_HI, 0); + WRITE_CSR(sc, WXREG_RDLEN0, WX_MAX_RDESC * sizeof (wxrd_t)); + WRITE_CSR(sc, WXREG_RDH0, 0); + WRITE_CSR(sc, WXREG_RDT0, (WX_MAX_RDESC - RXINCR)); + WRITE_CSR(sc, WXREG_RDTR1, 0); + WRITE_CSR(sc, WXREG_RDBA1_LO, 0); + WRITE_CSR(sc, WXREG_RDBA1_HI, 0); + WRITE_CSR(sc, WXREG_RDLEN1, 0); + WRITE_CSR(sc, WXREG_RDH1, 0); + WRITE_CSR(sc, WXREG_RDT1, 0); + + if (ifp->if_mtu > ETHERMTU) { + printf("%s: enabling for jumbo packets\n", sc->wx_name); + bflags = WXRCTL_EN | WXRCTL_LPE | WXRCTL_2KRBUF; + } else { + bflags = WXRCTL_EN | WXRCTL_2KRBUF; + } + + WRITE_CSR(sc, WXREG_RCTL, bflags | + ((ifp->if_flags & IFF_BROADCAST) ? WXRCTL_BAM : 0) | + ((ifp->if_flags & IFF_PROMISC) ? WXRCTL_UPE : 0) | + ((sc->all_mcasts) ? WXRCTL_MPE : 0)); + + /* + * Enable Interrupts + */ + WX_ENABLE_INT(sc); + + /* + * Mark that we're up and running... + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + ifm = &sc->wx_media; + i = ifm->ifm_media; + ifm->ifm_media = ifm->ifm_cur->ifm_media; + wx_ifmedia_upd(ifp); + ifm->ifm_media = i; + splx(s); + + /* + * Start stats updater. + */ + TIMEOUT(sc, wx_stats_update, sc, hz); + + /* + * And we're outta here... + */ + return (0); +} + +/* + * Get a receive buffer for our use (and dma map the data area). + * + * This chip can have buffers be 256, 512, 1024 or 2048 bytes in size. + * It wants them aligned on 256 byte boundaries, but can actually cope + * with an offset in the first 255 bytes of the head of a receive frame. + * + * We'll allocate a MCLBYTE sized cluster but *not* adjust the data pointer + * by any alignment value. Instead, we'll tell the chip to offset by any + * alignment and we'll catch the alignment on the backend at interrupt time. + */ +static void +wx_rxdma_map(sc, rxpkt, mb) + wx_softc_t *sc; + rxpkt_t *rxpkt; + struct mbuf *mb; +{ + rxpkt->dptr = mb; + rxpkt->dma_addr = vtophys(mtod(mb, vm_offset_t)); +} + +static int +wx_get_rbuf(sc, rxpkt) + wx_softc_t *sc; + rxpkt_t *rxpkt; +{ + struct mbuf *mb; + MGETHDR(mb, M_DONTWAIT, MT_DATA); + if (mb == NULL) { + rxpkt->dptr = NULL; + return (-1); + } + MCLGET(mb, M_DONTWAIT); + if ((mb->m_flags & M_EXT) == 0) { + m_freem(mb); + rxpkt->dptr = NULL; + return (-1); + } + wx_rxdma_map(sc, rxpkt, mb); + return (0); +} + +static int +wx_ioctl(ifp, command, data) + struct ifnet *ifp; + IOCTL_CMD_TYPE command; + caddr_t data; +{ + wx_softc_t *sc = SOFTC_IFP(ifp); + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + + s = splimp(); + switch (command) { + case SIOCSIFADDR: +#if !defined(__NetBSD__) && !defined(__OpenBSD__) + case SIOCGIFADDR: +#endif + error = ether_ioctl(ifp, command, data); + break; + +#if defined(SIOCSIFMTU) && !defined(__OpenBSD__) + case SIOCSIFMTU: + if (ifr->ifr_mtu > WX_MAXMTU || ifr->ifr_mtu < ETHERMIN) { + error = EINVAL; + } else if (ifp->if_mtu != ifr->ifr_mtu) { + ifp->if_mtu = ifr->ifr_mtu; + error = wx_init(sc); + } + break; +#endif + + case SIOCSIFFLAGS: + sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; + + /* + * If interface is marked up and not running, then start it. + * If it is marked down and running, stop it. + * If it's up then re-initialize it. This is so flags + * such as IFF_PROMISC are handled. + */ + if (ifp->if_flags & IFF_UP) { + error = wx_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + wx_stop(sc); + } + } + break; + +#ifdef SIOCADDMULTI + case SIOCADDMULTI: + case SIOCDELMULTI: +#if defined(__NetBSD__) + { + int all_mc_change = (sc->all_mcasts == + ((ifp->if_flags & IFF_ALLMULTI) ? 1 : 0)); + error = (command == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->w.ethercom) : + ether_delmulti(ifr, &sc->w.ethercom); + if (error != ENETRESET && all_mc_change == 0) { + break; + } + } +#endif + sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0; + error = wx_mc_setup(sc); + break; +#endif +#ifdef SIOCGIFMEDIA + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->wx_media, command); + break; +#endif + default: + error = EINVAL; + } + + (void) splx(s); + return (error); +} + +static int +wx_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct wx_softc *sc = SOFTC_IFP(ifp); + struct ifmedia *ifm = &sc->wx_media; + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + return (0); +} + +static void +wx_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct wx_softc *sc = SOFTC_IFP(ifp); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (sc->linkup == 0) + return; + + ifmr->ifm_status |= IFM_ACTIVE|IFM_1000_SX; + if (READ_CSR(sc, WXREG_DSR) & WXDSR_FD) + ifmr->ifm_active |= IFM_FDX; +} diff --git a/sys/dev/pci/if_wxreg.h b/sys/dev/pci/if_wxreg.h new file mode 100644 index 00000000000..a19c855fd1d --- /dev/null +++ b/sys/dev/pci/if_wxreg.h @@ -0,0 +1,317 @@ +/* $OpenBSD: if_wxreg.h,v 1.1 2000/02/11 14:51:50 jason Exp $ */ + +/* + * Copyright (c) 1999, Traakan Software + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_wxreg.h,v 1.3 2000/01/25 04:11:33 mjacob Exp $ + */ + +#define WX_VENDOR_INTEL 0x8086 +#define WX_PRODUCT_82452 0x1000 +#define WX_MMBA 0x10 +#define MWI 0x10 /* Memory Write Invalidate */ +#define WX_CACHELINE_SIZE 0x20 + +/* + * Information about this chipset gathered from a released Intel Linux driver, + * which was clearly a port of an NT driver. + */ + +/* + * Various Descriptor Structures. + * These are all in little endian format (for now). + */ + +typedef struct { + u_int32_t lowpart; + u_int32_t highpart; +} wxpa_t, wxrp_t; + +/* + * Receive Descriptor. + * The base address of a receive descriptor ring must be on a 4KB boundary, + * and they must be allocated in multiples of 8. + */ +typedef struct { + wxpa_t address; /* physical address of buffer */ + u_int16_t length; + u_int16_t csum; + u_int8_t status; + u_int8_t errors; + u_int16_t special; +} wxrd_t; + +#define RDSTAT_DD 0x1 /* descriptor done */ +#define RDSTAT_EOP 0x2 /* end of packet */ +#define RDSTAT_RSVD 0x74 /* reserved bits */ + +#define RDERR_CRC 0x1 /* CRC Error */ +#define RDERR_SE 0x2 /* Symbol Error */ +#define RDERR_SEQ 0x4 /* Sequence Error */ + +/* + * Transmit Descriptor + * The base address of a transmit descriptor ring must be on a 4KB boundary, + * and they must be allocated in multiples of 8. + */ +typedef struct { + wxpa_t address; + u_int16_t length; + u_int8_t cso; /* checksum offset */ + u_int8_t cmd; /* cmd */ + u_int8_t status; /* status */ + u_int8_t css; /* checksum start */ + u_int16_t special; +} wxtd_t; + +#define TXCMD_EOP 0x1 /* last packet */ +#define TXCMD_IFCS 0x2 /* insert FCS */ +#define TXCMD_IC 0x4 /* insert checksum */ +#define TXCMD_RS 0x8 /* report status */ +#define TXCMD_RPS 0x10 /* report packet sent */ +#define TXCMD_SM 0x20 /* symbol mode */ +#define TXCMD_IDE 0x80 /* interrupt delay enable */ + +#define TXSTS_DD 0x1 /* descriptor done */ +#define TXSTS_EC 0x2 /* excess collisions */ +#define TXSTS_LC 0x4 /* late collision */ + +/* + * This device can only be accessed via memory space. + */ +/* + * Register access via offsets. + */ + +#define WXREG_DCR 0x00000000 +#define WXREG_DSR 0x00000008 +#define WXREG_EECDR 0x00000010 +#define WXREG_FCAL 0x00000028 +#define WXREG_FCAH 0x0000002C +#define WXREG_FCT 0x00000030 +#define WXREG_VET 0x00000038 +#define WXREG_RAL_BASE 0x00000040 +#define WXREG_RAL_LO(x) (WXREG_RAL_BASE + ((x) << 3)) +#define WXREG_RAL_HI(x) (WXREG_RAL_LO(x) + 4) +#define WXREG_ICR 0x000000c0 +#define WXREG_ICS 0x000000c8 +#define WXREG_IMASK 0x000000d0 +#define WXREG_IMCLR 0x000000d8 +#define WXREG_RCTL 0x00000100 +#define WXREG_RDTR0 0x00000108 +#define WXREG_RDBA0_LO 0x00000110 +#define WXREG_RDBA0_HI 0x00000114 +#define WXREG_RDLEN0 0x00000118 +#define WXREG_RDH0 0x00000120 +#define WXREG_RDT0 0x00000128 +#define WXREG_RDTR1 0x00000130 +#define WXREG_RDBA1_LO 0x00000138 +#define WXREG_RDBA1_HI 0x0000013C +#define WXREG_RDLEN1 0x00000140 +#define WXREG_RDH1 0x00000148 +#define WXREG_RDT1 0x00000150 +#define WXREG_FLOW_RCV_HI 0x00000160 +#define WXREG_FLOW_RCV_LO 0x00000168 +#define WXREG_FLOW_XTIMER 0x00000170 +#define WXREG_XMIT_CFGW 0x00000178 +#define WXREG_RECV_CFGW 0x00000180 +#define WXREG_MTA 0x00000200 +#define WXREG_TCTL 0x00000400 +#define WXREG_TQSA_LO 0x00000408 +#define WXREG_TQSA_HI 0x0000040C +#define WXREG_TIPG 0x00000410 +#define WXREG_TQC 0x00000418 +#define WXREG_TDBA_LO 0x00000420 +#define WXREG_TDBA_HI 0x00000424 +#define WXREG_TDLEN 0x00000428 +#define WXREG_TDH 0x00000430 +#define WXREG_TDT 0x00000438 +#define WXREG_TIDV 0x00000440 +#define WXREG_VFTA 0x00000600 + +#define WX_RAL_TAB_SIZE 16 +#define WX_RAL_AV 0x80000000 + +#define WX_MC_TAB_SIZE 128 +#define WX_VLAN_TAB_SIZE 128 + +/* + * Device Control Register Defines + */ +#define WXDCR_FD 0x1 /* full duplex */ +#define WXDCR_BEM 0x2 /* big endian mode */ +#define WXDCR_FAIR 0x4 /* 1->Fairness, 0->Receive Priority */ +#define WXDCR_LRST 0x8 /* Link Reset */ +#define WXDCR_SLU 0x40 /* Set Link Up */ +#define WXDCR_ILOS 0x80 /* Invert Loss-of-Signal */ + +#define WXDCR_SWDPINS_SHIFT 18 +#define WXDCR_SWDPINS_MASK 0xf +#define WXDCR_SWDPIN0 (1 << 18) +#define WXDCR_SWDPIN1 (1 << 19) +#define WXDCR_SWDPIN2 (1 << 20) +#define WXDCR_SWDPIN3 (1 << 21) +#define WXDCR_SWDPIO_SHIFT 22 +#define WXDCR_SWDPIO_MASK 0xf +#define WXDCR_SWDPIO0 (1 << 22) +#define WXDCR_SWDPIO1 (1 << 23) +#define WXDCR_SWDPIO2 (1 << 24) +#define WXDCR_SWDPIO3 (1 << 25) + +#define WXDCR_RST 0x04000000 /* Device Reset (self clearing) */ +#define WXDCR_RFCE 0x08000000 /* Receive Flow Control Enable */ +#define WXDCR_TFCE 0x10000000 /* Transmit Flow Control Enable */ +#define WXDCR_RTE 0x20000000 /* Routing Tag Enable */ +#define WXDCR_VME 0x40000000 /* VLAN Mode Enable */ + +/* + * Device Status Register Defines + */ +#define WXDSR_FD 0x1 /* full duplex */ +#define WXDSR_LU 0x2 /* link up */ +#define WXDSR_TXCLK 0x4 /* transmit clock running */ +#define WXDSR_RBCLK 0x8 /* receive clock running */ +#define WXDSR_TXOFF 0x10 /* transmit paused */ + +/* + * EEPROM Register Defines + */ +#define WXEECD_SK 0x1 /* enable clock */ +#define WXEECD_CS 0x2 /* chip select */ +#define WXEECD_DI 0x4 /* data input */ +#define WXEECD_DO 0x8 /* data output */ + +#define EEPROM_READ_OPCODE 0x6 + +/* + * Constant Flow Control Frame MAC Address and Type values. + */ +#define FC_FRM_CONST_LO 0x00C28001 +#define FC_FRM_CONST_HI 0x0100 +#define FC_TYP_CONST 0x8808 + +/* + * Bits pertinent for the Receive Address register pairs. The low address + * is the low 32 bits of a 48 bit MAC address. The high address contains + * bits 32-47 of the 48 bit MAC address. The top bit in the high address + * is a 'valid' bit. + */ +#define WXRAH_RDR1 0x40000000 /* second receive descriptor ring */ +#define WXRAH_VALID 0x80000000 + +/* + * Interrupt Cause Bits + */ +#define WXISR_TXDW 0x1 /* transmit descriptor written back */ +#define WXISR_TXQE 0x2 /* transmit queue empty */ +#define WXISR_LSC 0x4 /* link status change */ +#define WXISR_RXSEQ 0x8 /* receive sequence error */ +#define WXISR_RXDMT0 0x10 /* receiver ring 0 getting empty */ +#define WXISR_RXO 0x40 /* receiver overrun */ +#define WXISR_RXT0 0x80 /* ring 0 receiver timer interrupt */ +#define WXISR_PCIE 0x200 /* ?? Probably PCI interface error... */ + +#define WXIENABLE_DEFAULT \ + (WXISR_RXO|WXISR_RXT0|WXISR_RXDMT0|WXISR_RXSEQ|WXISR_LSC|WXISR_PCIE) + +#define WXDISABLE 0xffffffff + +/* + * Receive Control Register bits. + */ + +#define WXRCTL_RST 0x1 /* receiver reset */ +#define WXRCTL_EN 0x2 /* receiver enable */ +#define WXRCTL_SBP 0x4 /* store bad packets */ +#define WXRCTL_UPE 0x8 /* unicast promiscuos mode */ +#define WXRCTL_MPE 0x10 /* multicast promiscuous mode */ +#define WXRCTL_LPE 0x20 /* large packet enable */ +#define WXRCTL_BAM 0x8000 /* broadcast accept mode */ + +#define WXRCTL_2KRBUF (0 << 16) /* 2-Kbyte Receive Buffers */ +#define WXRCTL_1KRBUF (1 << 16) /* 1-Kbyte Receive Buffers */ +#define WXRCTL_512BRBUF (2 << 16) /* 512 Byte Receive Buffers */ +#define WXRCTL_256BRBUF (3 << 16) /* 256 Byte Receive Buffers */ + + +/* + * Receive Delay Timer Register bits. + */ +#define WXRDTR_FPD 0x80000000 /* flush partial descriptor */ + +/* + * Transmit Configuration Word defines + */ +#define WXTXCW_FD 0x00000020 /* Full Duplex */ +#define WXTXCW_PMASK 0x00000180 /* pause mask */ +#define WXTXCW_ANE 0x80000000 /* AutoNegotiate */ +#define WXTXCW_DEFAULT 0x800001A0 + +/* + * Transmit Control Register defines. + */ +#define WXTCTL_RST 0x1 /* transmitter reset */ +#define WXTCTL_EN 0x2 /* transmitter enable */ +#define WXTCTL_PSP 0x8 /* pad short packets */ +#define WXTCTL_CT(x) (((x) & 0xff) << 4) /* 4:11 - Collision Threshold */ +#define WXTCTL_COLD(x) (((x) & 0x3ff) << 12) /* 12:21 - Collision Distance */ +#define WXTCTL_SWXOFF (1 << 22) /* Software XOFF */ + +#define WX_COLLISION_THRESHOLD 15 +#define WX_FDX_COLLISION_DX 64 +#define WX_HDX_COLLISION_DX 512 + +/* + * Miscellaneous + */ +#define WX_EEPROM_MAC_OFF 0 + +/* + * Offset for Initialization Control Word #1 + */ +#define WX_EEPROM_CTLR1_OFF 0xA +#define WX_EEPROM_CTLR1_FD (1 << 10) +#define WX_EEPROM_CTLR1_SWDPIO_SHIFT 5 +#define WX_EEPROM_CTLR1_ILOS (1 << 4) + + +#define WX_XTIMER_DFLT 0x100 +#define WX_RCV_FLOW_HI_DFLT 0x8000 +#define WX_RCV_FLOW_LO_DFLT 0x4000 + +#define WX_TIPG_DFLT (10 | (2 << 10) | (10 << 20)) + +#define WX_CRC_LENGTH 4 + + +/* + * Hardware cannot transmit less than 16 bytes. It also cannot + * successfully receive less than 60 bytes. + */ +#define WX_MIN_XPKT_SIZE 16 +#define WX_MIN_RPKT_SIZE 60 +#define WX_MAX_PKT_SIZE 1514 +#define WX_MAX_PKT_SIZE_JUMBO 9014 diff --git a/sys/dev/pci/if_wxvar.h b/sys/dev/pci/if_wxvar.h new file mode 100644 index 00000000000..ece97e9eb73 --- /dev/null +++ b/sys/dev/pci/if_wxvar.h @@ -0,0 +1,407 @@ +/* $OpenBSD: if_wxvar.h,v 1.1 2000/02/11 14:51:50 jason Exp $ */ + +/* + * Copyright (c) 1999, Traakan Software + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_wxvar.h,v 1.4 2000/01/23 03:18:14 mjacob Exp $ + */ + +/* + * Softc definitions for the Intel Gigabit Ethernet driver. + * + * Guidance and inspiration from David Greenman's + * if_fxp driver gratefully acknowledged here. + */ + +/* + * Platform specific defines and inline functions go here. + * Look further below for more generic structures. + */ + +#if defined(__NetBSD__) +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <net/if.h> +#if defined(SIOCSIFMEDIA) +#include <net/if_media.h> +#endif +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <net/netisr.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> +#include <net/if_ether.h> +#if defined(INET) +#include <netinet/if_inarp.h> +#endif +#include <machine/bus.h> +#include <machine/intr.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/if_wxreg.h> + +struct wxmdvar { + struct device dev; /* generic device structures */ + void * ih; /* interrupt handler cookie */ + struct ethercom ethercom; /* ethernet common part */ + pci_chipset_tag_t pci_pc; + pcitag_t pci_tag; + u_int8_t enaddr[6]; /* our mac address */ + u_int32_t cmdw; + bus_space_tag_t st; /* bus space tag */ + bus_space_handle_t sh; /* bus space handle */ + struct ifmedia ifm; + struct wx_softc * next; +}; +#define wx_dev w.dev +#define wx_enaddr w.enaddr +#define wx_cmdw w.cmdw +#define wx_media w.ifm +#define wx_next w.next + +#define wx_if w.ethercom.ec_if +#define wx_name w.dev.dv_xname + +#define IOCTL_CMD_TYPE u_long +#define WXMALLOC(len) malloc(len, M_DEVBUF, M_NOWAIT) +#define WXFREE(ptr) free(ptr, M_DEVBUF) +#define SOFTC_IFP(ifp) ifp->if_softc +#define WX_BPFTAP_ARG(ifp) (ifp)->if_bpf +#define TIMEOUT(sc, func, arg, time) timeout(func, arg, time) +#define VTIMEOUT(sc, func, arg, time) timeout(func, arg, time) +#define UNTIMEOUT(f, arg, sc) untimeout(f, arg) +#define INLINE inline + +#define vm_offset_t vaddr_t +#ifndef IFM_1000_SX +#define IFM_1000_SX IFM_1000_FX +#endif +#define READ_CSR _read_csr +#define WRITE_CSR _write_csr + +#elif defined(__FreeBSD__) +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif +#include <net/bpf.h> +#include <sys/sockio.h> +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> +#include <net/ethernet.h> +#include <net/if_arp.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/clock.h> +#include <pci/pcivar.h> +#include <pci/pcireg.h> +#include <pci/if_wxreg.h> + +#include "opt_bdg.h" +#ifdef BRIDGE +#include <net/if_types.h> +#include <net/bridge.h> +#endif + +struct wxmdvar { + struct device * dev; /* backpointer to device */ + struct arpcom arpcom; /* per-interface network data */ + struct resource * mem; /* resource descriptor for registers */ + struct resource * irq; /* resource descriptor for interrupt */ + void * ih; /* interrupt handler cookie */ + u_int16_t cmdw; + struct callout_handle sch; /* handle for timeouts */ + char name[8]; + bus_space_tag_t st; /* bus space tag */ + bus_space_handle_t sh; /* bus space handle */ + struct ifmedia ifm; + struct wx_softc * next; +}; +#define wx_dev w.dev +#define wx_enaddr w.arpcom.ac_enaddr +#define wx_cmdw w.cmdw +#define wx_media w.ifm +#define wx_next w.next + +#define wx_if w.arpcom.ac_if +#define wx_name w.name + +#define IOCTL_CMD_TYPE u_long +#define WXMALLOC(len) malloc(len, M_DEVBUF, M_NOWAIT) +#define WXFREE(ptr) free(ptr, M_DEVBUF) +#define SOFTC_IFP(ifp) ifp->if_softc +#define WX_BPFTAP_ARG(ifp) ifp +#define VTIMEOUT(sc, func, arg, time) (void) timeout(func, arg, time) +#define TIMEOUT(sc, func, arg, time) (sc)->w.sch = timeout(func, arg, time) +#define UNTIMEOUT(f, arg, sc) untimeout(f, arg, (sc)->w.sch) +#define INLINE __inline + +#define READ_CSR(sc, reg) \ + bus_space_read_4((sc)->w.st, (sc)->w.sh, (reg)) +#define WRITE_CSR(sc, reg, val) \ + bus_space_write_4((sc)->w.st, (sc)->w.sh, (reg), (val)) + +#elif defined(__OpenBSD__) +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.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 NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> +#include <machine/bus.h> +#include <machine/intr.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/if_wxreg.h> + +struct wxmdvar { + struct device dev; /* generic device structures */ + void * ih; /* interrupt handler cookie */ + struct arpcom arpcom; /* ethernet common part */ + pci_chipset_tag_t pci_pc; + pcitag_t pci_tag; + u_int32_t cmdw; + bus_space_tag_t st; /* bus space tag */ + bus_space_handle_t sh; /* bus space handle */ + struct ifmedia ifm; + struct wx_softc * next; +}; +#define wx_dev w.dev +#define wx_enaddr w.arpcom.ac_enaddr +#define wx_cmdw w.cmdw +#define wx_media w.ifm +#define wx_next w.next + +#define wx_if w.arpcom.ac_if +#define wx_name w.dev.dv_xname + +#define IOCTL_CMD_TYPE u_long +#define WXMALLOC(len) malloc(len, M_DEVBUF, M_NOWAIT) +#define WXFREE(ptr) free(ptr, M_DEVBUF) +#define SOFTC_IFP(ifp) ifp->if_softc +#define WX_BPFTAP_ARG(ifp) (ifp)->if_bpf +#define TIMEOUT(sc, func, arg, time) timeout(func, arg, time) +#define VTIMEOUT(sc, func, arg, time) timeout(func, arg, time) +#define UNTIMEOUT(f, arg, sc) untimeout(f, arg) +#define INLINE inline + +#define vm_offset_t vaddr_t +#define READ_CSR _read_csr +#define WRITE_CSR _write_csr + +#endif + + +/* + * Transmit soft descriptor, used to manage packets as they come in. + */ +typedef struct rxpkt { + struct mbuf *dptr; /* pointer to receive frame */ + u_int32_t dma_addr; /* dma address */ +} rxpkt_t; + + +/* + * Transmit soft descriptor, used to manage packets as they are transmitted. + */ +typedef struct txpkt { + struct txpkt *next; /* next in a chain */ + struct mbuf *dptr; /* pointer to mbuf being sent */ + u_int32_t sidx; /* start index */ + u_int32_t eidx; /* end index */ +} txpkt_t; + + +typedef struct wx_softc { + /* + * OS dependent storage... must be first... + */ + struct wxmdvar w; + + /* + * misc goodies + */ + u_int32_t : 18, + wx_no_flow : 1, + wx_ilos : 1, + wx_no_ilos : 1, + wx_debug : 1, + linkup : 1, + all_mcasts : 1, + revision : 8; /* chip revision */ + + u_int16_t wx_cfg1; + u_int16_t wx_txint_delay; + u_int32_t wx_ienable; /* current ienable to use */ + u_int32_t wx_dcr; /* dcr used */ + u_int32_t wx_icr; /* last icr */ + + /* + * Statistics, soft && hard + */ + u_int32_t wx_intr; + u_int32_t wx_linkintr; + u_int32_t wx_rxintr; + u_int32_t wx_xmitgc; + u_int32_t wx_xmitpullup; + u_int32_t wx_xmitcluster; + u_int32_t wx_xmitputback; + u_int32_t wx_xmitwanted; + u_int32_t wx_xmitblocked; + u_int32_t wx_xmitblocked1; + u_int32_t wx_xmitrunt; + u_int32_t wx_rxnobuf; + + /* + * Soft copies of multicast addresses. We're only + * using (right now) the rest of the receive address + * registers- not the hashed multicast table. + */ + u_int8_t wx_mcaddr[WX_RAL_TAB_SIZE-1][6]; + u_int8_t wx_nmca; /* # active multicast addrs */ + + + /* + * Receive Management + * We have software and shared memory rings in a buddy store format. + */ + wxrd_t *rdescriptors; /* receive descriptor ring */ + rxpkt_t *rbase; /* base of soft rdesc list */ + u_int16_t rnxt; /* next descriptor to check */ + u_int16_t _pad; + struct mbuf *rpending; /* pending partial packet */ + + /* + * Transmit Management + * We have software and shared memory rings in a buddy store format. + */ + txpkt_t *tbase; /* base of soft soft management */ + txpkt_t *tbsyf, *tbsyl; /* linked busy list */ + wxtd_t *tdescriptors; /* transmit descriptor ring */ + u_int16_t tnxtfree; /* next free index (circular) */ + u_int16_t tactive; /* # active */ +} wx_softc_t; + +/* + * We offset the the receive frame header by two bytes so that the actual + * payload is 32 bit aligned. On platforms that require strict structure + * alignment, this means that the ethernet frame header may have to be shifted + * to align it at interrupt time, but because it's such a small amount + * (fourteen bytes) and processors have gotten pretty fast, that's okay. + * It may even turn out on some platforms that this doesn't have to happen. + */ +#define WX_RX_OFFSET_VALUE 2 + +/* + * Tunable Parameters. + * + * Descriptor lengths must be in multiples of 8. + */ +#define WX_MAX_TDESC 256 /* number of transmit descriptors */ +#define T_NXT_IDX(x) ((x + 1) & (WX_MAX_TDESC - 1)) +#define T_PREV_IDX(x) ((x - 1) & (WX_MAX_TDESC - 1)) +#define WX_MAX_RDESC 64 /* number of receive descriptors */ +#ifdef PADDED_CELL +#define RXINCR 2 +#else +#define RXINCR 1 +#endif +#define R_NXT_IDX(x) ((x + RXINCR) & (WX_MAX_RDESC - 1)) +#define R_PREV_IDX(x) ((x - RXINCR) & (WX_MAX_RDESC - 1)) |