diff options
| author | jdc <jdc@NetBSD.org> | 2010-01-07 09:19:55 +0000 |
|---|---|---|
| committer | jdc <jdc@NetBSD.org> | 2010-01-07 09:19:55 +0000 |
| commit | 28537da09d66e7a0d3d4797ed87bf544ca45b6ff (patch) | |
| tree | 6fbb5f8b47249b376a18348742c2fe7a0da81378 /sys/dev | |
| parent | c6ed7679c09307d8e643bd703d2db64681d46d78 (diff) | |
Driver for Sun Cassini/Cassini+ (GigaSwift) Ethernet (also known as
National Semiconductor Saturn).
Based on the OpenBSD driver written by Mark Kettenis:
detach support added
MAC address lookup modified (only tested on sparc64)
Tested on single GigaSwift (UTP and MMF) and quad GigaSwift PCI cards.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/pci/if_cas.c | 2090 | ||||
| -rw-r--r-- | sys/dev/pci/if_casreg.h | 617 | ||||
| -rw-r--r-- | sys/dev/pci/if_casvar.h | 261 |
3 files changed, 2968 insertions, 0 deletions
diff --git a/sys/dev/pci/if_cas.c b/sys/dev/pci/if_cas.c new file mode 100644 index 00000000000..038e0f9b95f --- /dev/null +++ b/sys/dev/pci/if_cas.c @@ -0,0 +1,2090 @@ +/* $NetBSD: if_cas.c,v 1.1 2010/01/07 09:19:55 jdc Exp $ */ +/* $OpenBSD: if_cas.c,v 1.29 2009/11/29 16:19:38 kettenis Exp $ */ + +/* + * + * Copyright (C) 2007 Mark Kettenis. + * Copyright (C) 2001 Eduardo Horvath. + * 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. + * + * 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. + * + */ + +/* + * Driver for Sun Cassini ethernet controllers. + * + * There are basically two variants of this chip: Cassini and + * Cassini+. We can distinguish between the two by revision: 0x10 and + * up are Cassini+. The most important difference is that Cassini+ + * has a second RX descriptor ring. Cassini+ will not work without + * configuring that second ring. However, since we don't use it we + * don't actually fill the descriptors, and only hand off the first + * four to the chip. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: if_cas.c,v 1.1 2010/01/07 09:19:55 jdc Exp $"); + +#include "opt_inet.h" +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/callout.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <machine/endian.h> + +#include <uvm/uvm_extern.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_ether.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <sys/bus.h> +#include <sys/intr.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/mii_bitbang.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <dev/pci/if_casreg.h> +#include <dev/pci/if_casvar.h> + +/* XXX Should use Properties when that's fleshed out. */ +#ifdef macppc +#include <dev/ofw/openfirm.h> +#endif /* macppc */ +#ifdef __sparc__ +#include <machine/promlib.h> +#endif + +#ifndef CAS_USE_LOCAL_MAC_ADDRESS +#if defined (macppc) || defined (__sparc__) +#define CAS_USE_LOCAL_MAC_ADDRESS 0 /* use system-wide address */ +#else +#define CAS_USE_LOCAL_MAC_ADDRESS 1 +#endif +#endif + +#define TRIES 10000 + +static bool cas_estintr(struct cas_softc *sc); +bool cas_shutdown(device_t, int); +static bool cas_suspend(device_t PMF_FN_PROTO); +static bool cas_resume(device_t PMF_FN_PROTO); +static int cas_detach(device_t, int); +static void cas_partial_detach(struct cas_softc *, enum cas_attach_stage); + +int cas_match(device_t, cfdata_t, void *); +void cas_attach(device_t, device_t, void *); + + +CFATTACH_DECL3_NEW(cas, sizeof(struct cas_softc), + cas_match, cas_attach, cas_detach, NULL, NULL, NULL, + DVF_DETACH_SHUTDOWN); + +#if CAS_USE_LOCAL_MAC_ADDRESS +int cas_pci_enaddr(struct cas_softc *, struct pci_attach_args *, uint8_t *); +#endif + +void cas_config(struct cas_softc *, const uint8_t *); +void cas_start(struct ifnet *); +void cas_stop(struct ifnet *, int); +int cas_ioctl(struct ifnet *, u_long, void *); +void cas_tick(void *); +void cas_watchdog(struct ifnet *); +int cas_init(struct ifnet *); +void cas_init_regs(struct cas_softc *); +int cas_ringsize(int); +int cas_cringsize(int); +int cas_meminit(struct cas_softc *); +void cas_mifinit(struct cas_softc *); +int cas_bitwait(struct cas_softc *, bus_space_handle_t, int, + u_int32_t, u_int32_t); +void cas_reset(struct cas_softc *); +int cas_reset_rx(struct cas_softc *); +int cas_reset_tx(struct cas_softc *); +int cas_disable_rx(struct cas_softc *); +int cas_disable_tx(struct cas_softc *); +void cas_rxdrain(struct cas_softc *); +int cas_add_rxbuf(struct cas_softc *, int idx); +void cas_iff(struct cas_softc *); +int cas_encap(struct cas_softc *, struct mbuf *, u_int32_t *); + +/* MII methods & callbacks */ +int cas_mii_readreg(device_t, int, int); +void cas_mii_writereg(device_t, int, int, int); +void cas_mii_statchg(device_t); +int cas_pcs_readreg(device_t, int, int); +void cas_pcs_writereg(device_t, int, int, int); + +int cas_mediachange(struct ifnet *); +void cas_mediastatus(struct ifnet *, struct ifmediareq *); + +int cas_eint(struct cas_softc *, u_int); +int cas_rint(struct cas_softc *); +int cas_tint(struct cas_softc *, u_int32_t); +int cas_pint(struct cas_softc *); +int cas_intr(void *); + +#ifdef CAS_DEBUG +#define DPRINTF(sc, x) if ((sc)->sc_ethercom.ec_if.if_flags & IFF_DEBUG) \ + printf x +#else +#define DPRINTF(sc, x) /* nothing */ +#endif + +int +cas_match(device_t parent, cfdata_t cf, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN && + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_CASSINI)) + return 1; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS && + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_SATURN)) + return 1; + + return 0; +} + +#if CAS_USE_LOCAL_MAC_ADDRESS +#define PROMHDR_PTR_DATA 0x18 +#define PROMDATA_PTR_VPD 0x08 +#define PROMDATA_DATA2 0x0a + +static const u_int8_t cas_promhdr[] = { 0x55, 0xaa }; +static const u_int8_t cas_promdat[] = { + 'P', 'C', 'I', 'R', + PCI_VENDOR_SUN & 0xff, PCI_VENDOR_SUN >> 8, + PCI_PRODUCT_SUN_CASSINI & 0xff, PCI_PRODUCT_SUN_CASSINI >> 8 +}; + +static const u_int8_t cas_promdat2[] = { + 0x18, 0x00, /* structure length */ + 0x00, /* structure revision */ + 0x00, /* interface revision */ + PCI_SUBCLASS_NETWORK_ETHERNET, /* subclass code */ + PCI_CLASS_NETWORK /* class code */ +}; + +int +cas_pci_enaddr(struct cas_softc *sc, struct pci_attach_args *pa, + uint8_t *enaddr) +{ + struct pci_vpd_largeres *res; + struct pci_vpd *vpd; + bus_space_handle_t romh; + bus_space_tag_t romt; + bus_size_t romsize = 0; + u_int8_t buf[32], *desc; + pcireg_t address; + int dataoff, vpdoff, len; + int rv = -1; + + if (pci_mapreg_map(pa, PCI_MAPREG_ROM, PCI_MAPREG_TYPE_MEM, 0, + &romt, &romh, NULL, &romsize)) + return (-1); + + address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); + address |= PCI_MAPREG_ROM_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START, address); + + bus_space_read_region_1(romt, romh, 0, buf, sizeof(buf)); + if (bcmp(buf, cas_promhdr, sizeof(cas_promhdr))) + goto fail; + + dataoff = buf[PROMHDR_PTR_DATA] | (buf[PROMHDR_PTR_DATA + 1] << 8); + if (dataoff < 0x1c) + goto fail; + + bus_space_read_region_1(romt, romh, dataoff, buf, sizeof(buf)); + if (bcmp(buf, cas_promdat, sizeof(cas_promdat)) || + bcmp(buf + PROMDATA_DATA2, cas_promdat2, sizeof(cas_promdat2))) + goto fail; + + vpdoff = buf[PROMDATA_PTR_VPD] | (buf[PROMDATA_PTR_VPD + 1] << 8); + if (vpdoff < 0x1c) + goto fail; + +next: + bus_space_read_region_1(romt, romh, vpdoff, buf, sizeof(buf)); + if (!PCI_VPDRES_ISLARGE(buf[0])) + goto fail; + + res = (struct pci_vpd_largeres *)buf; + vpdoff += sizeof(*res); + + len = ((res->vpdres_len_msb << 8) + res->vpdres_len_lsb); + switch(PCI_VPDRES_LARGE_NAME(res->vpdres_byte0)) { + case PCI_VPDRES_TYPE_IDENTIFIER_STRING: + /* Skip identifier string. */ + vpdoff += len; + goto next; + + case PCI_VPDRES_TYPE_VPD: + while (len > 0) { + bus_space_read_region_1(romt, romh, vpdoff, + buf, sizeof(buf)); + + vpd = (struct pci_vpd *)buf; + vpdoff += sizeof(*vpd) + vpd->vpd_len; + len -= sizeof(*vpd) + vpd->vpd_len; + + /* + * We're looking for an "Enhanced" VPD... + */ + if (vpd->vpd_key0 != 'Z') + continue; + + desc = buf + sizeof(*vpd); + + /* + * ...which is an instance property... + */ + if (desc[0] != 'I') + continue; + desc += 3; + + /* + * ...that's a byte array with the proper + * length for a MAC address... + */ + if (desc[0] != 'B' || desc[1] != ETHER_ADDR_LEN) + continue; + desc += 2; + + /* + * ...named "local-mac-address". + */ + if (strcmp(desc, "local-mac-address") != 0) + continue; + desc += strlen("local-mac-address") + 1; + + memcpy(enaddr, enp, ETHER_ADDR_LEN); + rv = 0; + } + break; + + default: + goto fail; + } + + fail: + if (romsize != 0) + bus_space_unmap(romt, romh, romsize); + + address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM); + address &= ~PCI_MAPREG_ROM_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address); + + return (rv); +} +#endif /* CAS_USE_LOCAL_MAC_ADDRESS */ + +void +cas_attach(device_t parent, device_t self, void *aux) +{ + struct pci_attach_args *pa = aux; + struct cas_softc *sc = device_private(self); + char devinfo[256]; + uint8_t enaddr[ETHER_ADDR_LEN]; + + sc->sc_dev = self; + pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo)); + sc->sc_rev = PCI_REVISION(pa->pa_class); + aprint_normal(": %s (rev. 0x%02x)\n", devinfo, sc->sc_rev); + sc->sc_dmatag = pa->pa_dmat; + +#define PCI_CAS_BASEADDR 0x10 + if (pci_mapreg_map(pa, PCI_CAS_BASEADDR, PCI_MAPREG_TYPE_MEM, 0, + &sc->sc_memt, &sc->sc_memh, NULL, &sc->sc_size) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to map device registers\n"); + return; + } + +#if CAS_USE_LOCAL_MAC_ADDRESS + if (cas_pci_enaddr(sc, pa, enaddr) != 0) + aprint_error_dev(sc->sc_dev, "no Ethernet address found\n"); +#endif +#ifdef __sparc64__ + prom_getether(PCITAG_NODE(pa->pa_tag), enaddr); +#else +#ifdef macppc + { + int node; + + node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag); + if (node == 0) { + aprint_error_dev(sc->sc_dev, "unable to locate OpenFirmware node\n"); + return; + } + + OF_getprop(node, "local-mac-address", enaddr, sizeof(enaddr)); + } +#endif /* macppc */ +#endif /* __sparc__ */ + + sc->sc_burst = 16; /* XXX */ + + sc->sc_att_stage = CAS_ATT_BACKEND_0; + + if (pci_intr_map(pa, &sc->sc_handle) != 0) { + aprint_error_dev(sc->sc_dev, "unable to map interrupt\n"); + bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size); + return; + } + sc->sc_pc = pa->pa_pc; + if (!cas_estintr(sc)) { + bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size); + aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n"); + return; + } + + sc->sc_att_stage = CAS_ATT_BACKEND_1; + + /* + * call the main configure + */ + cas_config(sc, enaddr); + + if (pmf_device_register1(sc->sc_dev, + cas_suspend, cas_resume, cas_shutdown)) + pmf_class_network_register(sc->sc_dev, &sc->sc_ethercom.ec_if); + else + aprint_error_dev(sc->sc_dev, + "could not establish power handlers\n"); + + sc->sc_att_stage = CAS_ATT_FINISHED; + /*FALLTHROUGH*/ +} + +/* + * cas_config: + * + * Attach a Cassini interface to the system. + */ +void +cas_config(struct cas_softc *sc, const uint8_t *enaddr) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct mii_data *mii = &sc->sc_mii; + struct mii_softc *child; + int i, error; + + /* Make sure the chip is stopped. */ + ifp->if_softc = sc; + cas_reset(sc); + + /* + * Allocate the control data structures, and create and load the + * DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->sc_dmatag, + sizeof(struct cas_control_data), CAS_PAGE_SIZE, 0, &sc->sc_cdseg, + 1, &sc->sc_cdnseg, 0)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to allocate control data, error = %d\n", + error); + cas_partial_detach(sc, CAS_ATT_0); + } + + /* XXX should map this in with correct endianness */ + if ((error = bus_dmamem_map(sc->sc_dmatag, &sc->sc_cdseg, sc->sc_cdnseg, + sizeof(struct cas_control_data), (void **)&sc->sc_control_data, + BUS_DMA_COHERENT)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to map control data, error = %d\n", error); + cas_partial_detach(sc, CAS_ATT_1); + } + + if ((error = bus_dmamap_create(sc->sc_dmatag, + sizeof(struct cas_control_data), 1, + sizeof(struct cas_control_data), 0, 0, &sc->sc_cddmamap)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to create control data DMA map, error = %d\n", error); + cas_partial_detach(sc, CAS_ATT_2); + } + + if ((error = bus_dmamap_load(sc->sc_dmatag, sc->sc_cddmamap, + sc->sc_control_data, sizeof(struct cas_control_data), NULL, + 0)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to load control data DMA map, error = %d\n", + error); + cas_partial_detach(sc, CAS_ATT_3); + } + + memset(sc->sc_control_data, 0, sizeof(struct cas_control_data)); + + /* + * Create the receive buffer DMA maps. + */ + for (i = 0; i < CAS_NRXDESC; i++) { + bus_dma_segment_t seg; + char *kva; + int rseg; + + if ((error = bus_dmamem_alloc(sc->sc_dmatag, CAS_PAGE_SIZE, + CAS_PAGE_SIZE, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to alloc rx DMA mem %d, error = %d\n", + i, error); + cas_partial_detach(sc, CAS_ATT_5); + } + sc->sc_rxsoft[i].rxs_dmaseg = seg; + + if ((error = bus_dmamem_map(sc->sc_dmatag, &seg, rseg, + CAS_PAGE_SIZE, (void **)&kva, BUS_DMA_NOWAIT)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to alloc rx DMA mem %d, error = %d\n", + i, error); + cas_partial_detach(sc, CAS_ATT_5); + } + sc->sc_rxsoft[i].rxs_kva = kva; + + if ((error = bus_dmamap_create(sc->sc_dmatag, CAS_PAGE_SIZE, 1, + CAS_PAGE_SIZE, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to create rx DMA map %d, error = %d\n", + i, error); + cas_partial_detach(sc, CAS_ATT_5); + } + + if ((error = bus_dmamap_load(sc->sc_dmatag, + sc->sc_rxsoft[i].rxs_dmamap, kva, CAS_PAGE_SIZE, NULL, + BUS_DMA_NOWAIT)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to load rx DMA map %d, error = %d\n", + i, error); + cas_partial_detach(sc, CAS_ATT_5); + } + } + + /* + * Create the transmit buffer DMA maps. + */ + for (i = 0; i < CAS_NTXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmatag, MCLBYTES, + CAS_NTXSEGS, MCLBYTES, 0, BUS_DMA_NOWAIT, + &sc->sc_txd[i].sd_map)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to create tx DMA map %d, error = %d\n", + i, error); + cas_partial_detach(sc, CAS_ATT_6); + } + sc->sc_txd[i].sd_mbuf = NULL; + } + + /* + * From this point forward, the attachment cannot fail. A failure + * before this point releases all resources that may have been + * allocated. + */ + + /* Announce ourselves. */ + aprint_normal_dev(sc->sc_dev, "Ethernet address %s\n", + ether_sprintf(enaddr)); + + /* Get RX FIFO size */ + sc->sc_rxfifosize = 16 * 1024; + + /* Initialize ifnet structure. */ + strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + ifp->if_start = cas_start; + ifp->if_ioctl = cas_ioctl; + ifp->if_watchdog = cas_watchdog; + ifp->if_stop = cas_stop; + ifp->if_init = cas_init; + IFQ_SET_MAXLEN(&ifp->if_snd, CAS_NTXDESC - 1); + IFQ_SET_READY(&ifp->if_snd); + + /* Initialize ifmedia structures and MII info */ + mii->mii_ifp = ifp; + mii->mii_readreg = cas_mii_readreg; + mii->mii_writereg = cas_mii_writereg; + mii->mii_statchg = cas_mii_statchg; + + ifmedia_init(&mii->mii_media, 0, cas_mediachange, cas_mediastatus); + sc->sc_ethercom.ec_mii = mii; + + bus_space_write_4(sc->sc_memt, sc->sc_memh, CAS_MII_DATAPATH_MODE, 0); + + cas_mifinit(sc); + + if (sc->sc_mif_config & CAS_MIF_CONFIG_MDI1) { + sc->sc_mif_config |= CAS_MIF_CONFIG_PHY_SEL; + bus_space_write_4(sc->sc_memt, sc->sc_memh, + CAS_MIF_CONFIG, sc->sc_mif_config); + } + + mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, 0); + + child = LIST_FIRST(&mii->mii_phys); + if (child == NULL && + sc->sc_mif_config & (CAS_MIF_CONFIG_MDI0|CAS_MIF_CONFIG_MDI1)) { + /* + * Try the external PCS SERDES if we didn't find any + * MII devices. + */ + bus_space_write_4(sc->sc_memt, sc->sc_memh, + CAS_MII_DATAPATH_MODE, CAS_MII_DATAPATH_SERDES); + + bus_space_write_4(sc->sc_memt, sc->sc_memh, + CAS_MII_CONFIG, CAS_MII_CONFIG_ENABLE); + + mii->mii_readreg = cas_pcs_readreg; + mii->mii_writereg = cas_pcs_writereg; + + mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, MIIF_NOISOLATE); + } + + child = LIST_FIRST(&mii->mii_phys); + if (child == NULL) { + /* No PHY attached */ + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); + ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); + } else { + /* + * Walk along the list of attached MII devices and + * establish an `MII instance' to `phy number' + * mapping. We'll use this mapping in media change + * requests to determine which phy to use to program + * the MIF configuration register. + */ + for (; child != NULL; child = LIST_NEXT(child, mii_list)) { + /* + * Note: we support just two PHYs: the built-in + * internal device and an external on the MII + * connector. + */ + if (child->mii_phy > 1 || child->mii_inst > 1) { + aprint_error_dev(sc->sc_dev, + "cannot accommodate MII device %s" + " at phy %d, instance %d\n", + device_xname(child->mii_dev), + child->mii_phy, child->mii_inst); + continue; + } + + sc->sc_phys[child->mii_inst] = child->mii_phy; + } + + /* + * XXX - we can really do the following ONLY if the + * phy indeed has the auto negotiation capability!! + */ + ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO); + } + + /* claim 802.1q capability */ + sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU; + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp, enaddr); + +#if NRND > 0 + rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev), + RND_TYPE_NET, 0); +#endif + + evcnt_attach_dynamic(&sc->sc_ev_intr, EVCNT_TYPE_INTR, + NULL, device_xname(sc->sc_dev), "interrupts"); + + callout_init(&sc->sc_tick_ch, 0); + + return; +} + +int +cas_detach(device_t self, int flags) +{ + int i; + struct cas_softc *sc = device_private(self); + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + switch (sc->sc_att_stage) { + case CAS_ATT_FINISHED: + pmf_device_deregister(self); + cas_stop(&sc->sc_ethercom.ec_if, 1); + evcnt_detach(&sc->sc_ev_intr); + +#if NRND > 0 + rnd_detach_source(&sc->rnd_source); +#endif + + ether_ifdetach(ifp); + if_detach(ifp); + ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); + + callout_destroy(&sc->sc_tick_ch); + + mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); + + /*FALLTHROUGH*/ + case CAS_ATT_MII: + case CAS_ATT_7: + case CAS_ATT_6: + for (i = 0; i < CAS_NTXDESC; i++) { + if (sc->sc_txd[i].sd_map != NULL) + bus_dmamap_destroy(sc->sc_dmatag, + sc->sc_txd[i].sd_map); + } + /*FALLTHROUGH*/ + case CAS_ATT_5: + for (i = 0; i < CAS_NRXDESC; i++) { + if (sc->sc_rxsoft[i].rxs_dmamap != NULL) + bus_dmamap_unload(sc->sc_dmatag, + sc->sc_rxsoft[i].rxs_dmamap); + if (sc->sc_rxsoft[i].rxs_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmatag, + sc->sc_rxsoft[i].rxs_dmamap); + if (sc->sc_rxsoft[i].rxs_kva != NULL) + bus_dmamem_unmap(sc->sc_dmatag, + sc->sc_rxsoft[i].rxs_kva, CAS_PAGE_SIZE); + /* XXX need to check that bus_dmamem_alloc suceeded + if (sc->sc_rxsoft[i].rxs_dmaseg != NULL) + */ + bus_dmamem_free(sc->sc_dmatag, + &(sc->sc_rxsoft[i].rxs_dmaseg), 1); + } + bus_dmamap_unload(sc->sc_dmatag, sc->sc_cddmamap); + /*FALLTHROUGH*/ + case CAS_ATT_4: + case CAS_ATT_3: + bus_dmamap_destroy(sc->sc_dmatag, sc->sc_cddmamap); + /*FALLTHROUGH*/ + case CAS_ATT_2: + bus_dmamem_unmap(sc->sc_dmatag, sc->sc_control_data, + sizeof(struct cas_control_data)); + /*FALLTHROUGH*/ + case CAS_ATT_1: + bus_dmamem_free(sc->sc_dmatag, &sc->sc_cdseg, sc->sc_cdnseg); + /*FALLTHROUGH*/ + case CAS_ATT_0: + sc->sc_att_stage = CAS_ATT_0; + /*FALLTHROUGH*/ + case CAS_ATT_BACKEND_2: + case CAS_ATT_BACKEND_1: + if (sc->sc_ih != NULL) { + pci_intr_disestablish(sc->sc_pc, sc->sc_ih); + sc->sc_ih = NULL; + } + bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size); + /*FALLTHROUGH*/ + case CAS_ATT_BACKEND_0: + break; + } + return 0; +} + +static void +cas_partial_detach(struct cas_softc *sc, enum cas_attach_stage stage) +{ + cfattach_t ca = device_cfattach(sc->sc_dev); + + sc->sc_att_stage = stage; + (*ca->ca_detach)(sc->sc_dev, 0); +} + +void +cas_tick(void *arg) +{ + struct cas_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t mac = sc->sc_memh; + int s; + u_int32_t v; + + /* unload collisions counters */ + v = bus_space_read_4(t, mac, CAS_MAC_EXCESS_COLL_CNT) + + bus_space_read_4(t, mac, CAS_MAC_LATE_COLL_CNT); + ifp->if_collisions += v + + bus_space_read_4(t, mac, CAS_MAC_NORM_COLL_CNT) + + bus_space_read_4(t, mac, CAS_MAC_FIRST_COLL_CNT); + ifp->if_oerrors += v; + + /* read error counters */ + ifp->if_ierrors += + bus_space_read_4(t, mac, CAS_MAC_RX_LEN_ERR_CNT) + + bus_space_read_4(t, mac, CAS_MAC_RX_ALIGN_ERR) + + bus_space_read_4(t, mac, CAS_MAC_RX_CRC_ERR_CNT) + + bus_space_read_4(t, mac, CAS_MAC_RX_CODE_VIOL); + + /* clear the hardware counters */ + bus_space_write_4(t, mac, CAS_MAC_NORM_COLL_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_FIRST_COLL_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_EXCESS_COLL_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_LATE_COLL_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_RX_LEN_ERR_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_RX_ALIGN_ERR, 0); + bus_space_write_4(t, mac, CAS_MAC_RX_CRC_ERR_CNT, 0); + bus_space_write_4(t, mac, CAS_MAC_RX_CODE_VIOL, 0); + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc); +} + +int +cas_bitwait(struct cas_softc *sc, bus_space_handle_t h, int r, + u_int32_t clr, u_int32_t set) +{ + int i; + u_int32_t reg; + + for (i = TRIES; i--; DELAY(100)) { + reg = bus_space_read_4(sc->sc_memt, h, r); + if ((reg & clr) == 0 && (reg & set) == set) + return (1); + } + + return (0); +} + +void +cas_reset(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + int s; + + s = splnet(); + DPRINTF(sc, ("%s: cas_reset\n", device_xname(sc->sc_dev))); + cas_reset_rx(sc); + cas_reset_tx(sc); + + /* Do a full reset */ + bus_space_write_4(t, h, CAS_RESET, + CAS_RESET_RX | CAS_RESET_TX | CAS_RESET_BLOCK_PCS); + if (!cas_bitwait(sc, h, CAS_RESET, CAS_RESET_RX | CAS_RESET_TX, 0)) + aprint_error_dev(sc->sc_dev, "cannot reset device\n"); + splx(s); +} + + +/* + * cas_rxdrain: + * + * Drain the receive queue. + */ +void +cas_rxdrain(struct cas_softc *sc) +{ + /* Nothing to do yet. */ +} + +/* + * Reset the whole thing. + */ +void +cas_stop(struct ifnet *ifp, int disable) +{ + struct cas_softc *sc = (struct cas_softc *)ifp->if_softc; + struct cas_sxd *sd; + u_int32_t i; + + DPRINTF(sc, ("%s: cas_stop\n", device_xname(sc->sc_dev))); + + callout_stop(&sc->sc_tick_ch); + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + mii_down(&sc->sc_mii); + + cas_reset_rx(sc); + cas_reset_tx(sc); + + /* + * Release any queued transmit buffers. + */ + for (i = 0; i < CAS_NTXDESC; i++) { + sd = &sc->sc_txd[i]; + if (sd->sd_mbuf != NULL) { + bus_dmamap_sync(sc->sc_dmatag, sd->sd_map, 0, + sd->sd_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmatag, sd->sd_map); + m_freem(sd->sd_mbuf); + sd->sd_mbuf = NULL; + } + } + sc->sc_tx_cnt = sc->sc_tx_prod = sc->sc_tx_cons = 0; + + if (disable) + cas_rxdrain(sc); +} + + +/* + * Reset the receiver + */ +int +cas_reset_rx(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + + /* + * Resetting while DMA is in progress can cause a bus hang, so we + * disable DMA first. + */ + cas_disable_rx(sc); + bus_space_write_4(t, h, CAS_RX_CONFIG, 0); + /* Wait till it finishes */ + if (!cas_bitwait(sc, h, CAS_RX_CONFIG, 1, 0)) + aprint_error_dev(sc->sc_dev, "cannot disable rx dma\n"); + /* Wait 5ms extra. */ + delay(5000); + + /* Finally, reset the ERX */ + bus_space_write_4(t, h, CAS_RESET, CAS_RESET_RX); + /* Wait till it finishes */ + if (!cas_bitwait(sc, h, CAS_RESET, CAS_RESET_RX, 0)) { + aprint_error_dev(sc->sc_dev, "cannot reset receiver\n"); + return (1); + } + return (0); +} + + +/* + * Reset the transmitter + */ +int +cas_reset_tx(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + + /* + * Resetting while DMA is in progress can cause a bus hang, so we + * disable DMA first. + */ + cas_disable_tx(sc); + bus_space_write_4(t, h, CAS_TX_CONFIG, 0); + /* Wait till it finishes */ + if (!cas_bitwait(sc, h, CAS_TX_CONFIG, 1, 0)) + aprint_error_dev(sc->sc_dev, "cannot disable tx dma\n"); + /* Wait 5ms extra. */ + delay(5000); + + /* Finally, reset the ETX */ + bus_space_write_4(t, h, CAS_RESET, CAS_RESET_TX); + /* Wait till it finishes */ + if (!cas_bitwait(sc, h, CAS_RESET, CAS_RESET_TX, 0)) { + aprint_error_dev(sc->sc_dev, "cannot reset transmitter\n"); + return (1); + } + return (0); +} + +/* + * Disable receiver. + */ +int +cas_disable_rx(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + u_int32_t cfg; + + /* Flip the enable bit */ + cfg = bus_space_read_4(t, h, CAS_MAC_RX_CONFIG); + cfg &= ~CAS_MAC_RX_ENABLE; + bus_space_write_4(t, h, CAS_MAC_RX_CONFIG, cfg); + + /* Wait for it to finish */ + return (cas_bitwait(sc, h, CAS_MAC_RX_CONFIG, CAS_MAC_RX_ENABLE, 0)); +} + +/* + * Disable transmitter. + */ +int +cas_disable_tx(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + u_int32_t cfg; + + /* Flip the enable bit */ + cfg = bus_space_read_4(t, h, CAS_MAC_TX_CONFIG); + cfg &= ~CAS_MAC_TX_ENABLE; + bus_space_write_4(t, h, CAS_MAC_TX_CONFIG, cfg); + + /* Wait for it to finish */ + return (cas_bitwait(sc, h, CAS_MAC_TX_CONFIG, CAS_MAC_TX_ENABLE, 0)); +} + +/* + * Initialize interface. + */ +int +cas_meminit(struct cas_softc *sc) +{ + struct cas_rxsoft *rxs; + int i, error; + + rxs = (void *)&error; + + /* + * Initialize the transmit descriptor ring. + */ + for (i = 0; i < CAS_NTXDESC; i++) { + sc->sc_txdescs[i].cd_flags = 0; + sc->sc_txdescs[i].cd_addr = 0; + } + CAS_CDTXSYNC(sc, 0, CAS_NTXDESC, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* + * Initialize the receive descriptor and receive job + * descriptor rings. + */ + for (i = 0; i < CAS_NRXDESC; i++) + CAS_INIT_RXDESC(sc, i, i); + sc->sc_rxdptr = 0; + sc->sc_rxptr = 0; + + /* + * Initialize the receive completion ring. + */ + for (i = 0; i < CAS_NRXCOMP; i++) { + sc->sc_rxcomps[i].cc_word[0] = 0; + sc->sc_rxcomps[i].cc_word[1] = 0; + sc->sc_rxcomps[i].cc_word[2] = 0; + sc->sc_rxcomps[i].cc_word[3] = CAS_DMA_WRITE(CAS_RC3_OWN); + CAS_CDRXCSYNC(sc, i, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + } + + return (0); +} + +int +cas_ringsize(int sz) +{ + switch (sz) { + case 32: + return CAS_RING_SZ_32; + case 64: + return CAS_RING_SZ_64; + case 128: + return CAS_RING_SZ_128; + case 256: + return CAS_RING_SZ_256; + case 512: + return CAS_RING_SZ_512; + case 1024: + return CAS_RING_SZ_1024; + case 2048: + return CAS_RING_SZ_2048; + case 4096: + return CAS_RING_SZ_4096; + case 8192: + return CAS_RING_SZ_8192; + default: + aprint_error("cas: invalid Receive Descriptor ring size %d\n", + sz); + return CAS_RING_SZ_32; + } +} + +int +cas_cringsize(int sz) +{ + int i; + + for (i = 0; i < 9; i++) + if (sz == (128 << i)) + return i; + + aprint_error("cas: invalid completion ring size %d\n", sz); + return 128; +} + +/* + * Initialization of interface; set up initialization block + * and transmit/receive descriptor rings. + */ +int +cas_init(struct ifnet *ifp) +{ + struct cas_softc *sc = (struct cas_softc *)ifp->if_softc; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + int s; + u_int max_frame_size; + u_int32_t v; + + s = splnet(); + + DPRINTF(sc, ("%s: cas_init: calling stop\n", device_xname(sc->sc_dev))); + /* + * Initialization sequence. The numbered steps below correspond + * to the sequence outlined in section 6.3.5.1 in the Ethernet + * Channel Engine manual (part of the PCIO manual). + * See also the STP2002-STQ document from Sun Microsystems. + */ + + /* step 1 & 2. Reset the Ethernet Channel */ + cas_stop(ifp, 0); + cas_reset(sc); + DPRINTF(sc, ("%s: cas_init: restarting\n", device_xname(sc->sc_dev))); + + /* Re-initialize the MIF */ + cas_mifinit(sc); + + /* step 3. Setup data structures in host memory */ + cas_meminit(sc); + + /* step 4. TX MAC registers & counters */ + cas_init_regs(sc); + max_frame_size = ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN; + v = (max_frame_size) | (0x2000 << 16) /* Burst size */; + bus_space_write_4(t, h, CAS_MAC_MAC_MAX_FRAME, v); + + /* step 5. RX MAC registers & counters */ + cas_iff(sc); + + /* step 6 & 7. Program Descriptor Ring Base Addresses */ + KASSERT((CAS_CDTXADDR(sc, 0) & 0x1fff) == 0); + bus_space_write_4(t, h, CAS_TX_RING_PTR_HI, + (((uint64_t)CAS_CDTXADDR(sc,0)) >> 32)); + bus_space_write_4(t, h, CAS_TX_RING_PTR_LO, CAS_CDTXADDR(sc, 0)); + + KASSERT((CAS_CDRXADDR(sc, 0) & 0x1fff) == 0); + bus_space_write_4(t, h, CAS_RX_DRING_PTR_HI, + (((uint64_t)CAS_CDRXADDR(sc,0)) >> 32)); + bus_space_write_4(t, h, CAS_RX_DRING_PTR_LO, CAS_CDRXADDR(sc, 0)); + + KASSERT((CAS_CDRXCADDR(sc, 0) & 0x1fff) == 0); + bus_space_write_4(t, h, CAS_RX_CRING_PTR_HI, + (((uint64_t)CAS_CDRXCADDR(sc,0)) >> 32)); + bus_space_write_4(t, h, CAS_RX_CRING_PTR_LO, CAS_CDRXCADDR(sc, 0)); + + if (CAS_PLUS(sc)) { + KASSERT((CAS_CDRXADDR2(sc, 0) & 0x1fff) == 0); + bus_space_write_4(t, h, CAS_RX_DRING_PTR_HI2, + (((uint64_t)CAS_CDRXADDR2(sc,0)) >> 32)); + bus_space_write_4(t, h, CAS_RX_DRING_PTR_LO2, + CAS_CDRXADDR2(sc, 0)); + } + + /* step 8. Global Configuration & Interrupt Mask */ + bus_space_write_4(t, h, CAS_INTMASK, + ~(CAS_INTR_TX_INTME|CAS_INTR_TX_EMPTY| + CAS_INTR_TX_TAG_ERR| + CAS_INTR_RX_DONE|CAS_INTR_RX_NOBUF| + CAS_INTR_RX_TAG_ERR| + CAS_INTR_RX_COMP_FULL|CAS_INTR_PCS| + CAS_INTR_MAC_CONTROL|CAS_INTR_MIF| + CAS_INTR_BERR)); + bus_space_write_4(t, h, CAS_MAC_RX_MASK, + CAS_MAC_RX_DONE|CAS_MAC_RX_FRAME_CNT); + bus_space_write_4(t, h, CAS_MAC_TX_MASK, CAS_MAC_TX_XMIT_DONE); + bus_space_write_4(t, h, CAS_MAC_CONTROL_MASK, 0); /* XXXX */ + + /* step 9. ETX Configuration: use mostly default values */ + + /* Enable DMA */ + v = cas_ringsize(CAS_NTXDESC /*XXX*/) << 10; + bus_space_write_4(t, h, CAS_TX_CONFIG, + v|CAS_TX_CONFIG_TXDMA_EN|(1<<24)|(1<<29)); + bus_space_write_4(t, h, CAS_TX_KICK, 0); + + /* step 10. ERX Configuration */ + + /* Encode Receive Descriptor ring size */ + v = cas_ringsize(CAS_NRXDESC) << CAS_RX_CONFIG_RXDRNG_SZ_SHIFT; + if (CAS_PLUS(sc)) + v |= cas_ringsize(32) << CAS_RX_CONFIG_RXDRNG2_SZ_SHIFT; + + /* Encode Receive Completion ring size */ + v |= cas_cringsize(CAS_NRXCOMP) << CAS_RX_CONFIG_RXCRNG_SZ_SHIFT; + + /* Enable DMA */ + bus_space_write_4(t, h, CAS_RX_CONFIG, + v|(2<<CAS_RX_CONFIG_FBOFF_SHFT)|CAS_RX_CONFIG_RXDMA_EN); + + /* + * The following value is for an OFF Threshold of about 3/4 full + * and an ON Threshold of 1/4 full. + */ + bus_space_write_4(t, h, CAS_RX_PAUSE_THRESH, + (3 * sc->sc_rxfifosize / 256) | + ((sc->sc_rxfifosize / 256) << 12)); + bus_space_write_4(t, h, CAS_RX_BLANKING, (6 << 12) | 6); + + /* step 11. Configure Media */ + mii_ifmedia_change(&sc->sc_mii); + + /* step 12. RX_MAC Configuration Register */ + v = bus_space_read_4(t, h, CAS_MAC_RX_CONFIG); + v |= CAS_MAC_RX_ENABLE | CAS_MAC_RX_STRIP_CRC; + bus_space_write_4(t, h, CAS_MAC_RX_CONFIG, v); + + /* step 14. Issue Transmit Pending command */ + + /* step 15. Give the receiver a swift kick */ + bus_space_write_4(t, h, CAS_RX_KICK, CAS_NRXDESC-4); + if (CAS_PLUS(sc)) + bus_space_write_4(t, h, CAS_RX_KICK2, 4); + + /* Start the one second timer. */ + callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + splx(s); + + return (0); +} + +void +cas_init_regs(struct cas_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + const u_char *laddr = CLLADDR(ifp->if_sadl); + u_int32_t v, r; + + /* These regs are not cleared on reset */ + sc->sc_inited = 0; + if (!sc->sc_inited) { + /* Load recommended values */ + bus_space_write_4(t, h, CAS_MAC_IPG0, 0x00); + bus_space_write_4(t, h, CAS_MAC_IPG1, 0x08); + bus_space_write_4(t, h, CAS_MAC_IPG2, 0x04); + + bus_space_write_4(t, h, CAS_MAC_MAC_MIN_FRAME, ETHER_MIN_LEN); + /* Max frame and max burst size */ + v = ETHER_MAX_LEN | (0x2000 << 16) /* Burst size */; + bus_space_write_4(t, h, CAS_MAC_MAC_MAX_FRAME, v); + + bus_space_write_4(t, h, CAS_MAC_PREAMBLE_LEN, 0x07); + bus_space_write_4(t, h, CAS_MAC_JAM_SIZE, 0x04); + bus_space_write_4(t, h, CAS_MAC_ATTEMPT_LIMIT, 0x10); + bus_space_write_4(t, h, CAS_MAC_CONTROL_TYPE, 0x8088); + bus_space_write_4(t, h, CAS_MAC_RANDOM_SEED, + ((laddr[5]<<8)|laddr[4])&0x3ff); + + /* Secondary MAC addresses set to 0:0:0:0:0:0 */ + for (r = CAS_MAC_ADDR3; r < CAS_MAC_ADDR42; r += 4) + bus_space_write_4(t, h, r, 0); + + /* MAC control addr set to 0:1:c2:0:1:80 */ + bus_space_write_4(t, h, CAS_MAC_ADDR42, 0x0001); + bus_space_write_4(t, h, CAS_MAC_ADDR43, 0xc200); + bus_space_write_4(t, h, CAS_MAC_ADDR44, 0x0180); + + /* MAC filter addr set to 0:0:0:0:0:0 */ + bus_space_write_4(t, h, CAS_MAC_ADDR_FILTER0, 0); + bus_space_write_4(t, h, CAS_MAC_ADDR_FILTER1, 0); + bus_space_write_4(t, h, CAS_MAC_ADDR_FILTER2, 0); + + bus_space_write_4(t, h, CAS_MAC_ADR_FLT_MASK1_2, 0); + bus_space_write_4(t, h, CAS_MAC_ADR_FLT_MASK0, 0); + + /* Hash table initialized to 0 */ + for (r = CAS_MAC_HASH0; r <= CAS_MAC_HASH15; r += 4) + bus_space_write_4(t, h, r, 0); + + sc->sc_inited = 1; + } + + /* Counters need to be zeroed */ + bus_space_write_4(t, h, CAS_MAC_NORM_COLL_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_FIRST_COLL_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_EXCESS_COLL_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_LATE_COLL_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_DEFER_TMR_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_PEAK_ATTEMPTS, 0); + bus_space_write_4(t, h, CAS_MAC_RX_FRAME_COUNT, 0); + bus_space_write_4(t, h, CAS_MAC_RX_LEN_ERR_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_RX_ALIGN_ERR, 0); + bus_space_write_4(t, h, CAS_MAC_RX_CRC_ERR_CNT, 0); + bus_space_write_4(t, h, CAS_MAC_RX_CODE_VIOL, 0); + + /* Un-pause stuff */ + bus_space_write_4(t, h, CAS_MAC_SEND_PAUSE_CMD, 0); + + /* + * Set the station address. + */ + bus_space_write_4(t, h, CAS_MAC_ADDR0, (laddr[4]<<8) | laddr[5]); + bus_space_write_4(t, h, CAS_MAC_ADDR1, (laddr[2]<<8) | laddr[3]); + bus_space_write_4(t, h, CAS_MAC_ADDR2, (laddr[0]<<8) | laddr[1]); +} + +/* + * Receive interrupt. + */ +int +cas_rint(struct cas_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + struct cas_rxsoft *rxs; + struct mbuf *m; + u_int64_t word[4]; + int len, off, idx; + int i, skip; + void *cp; + + for (i = sc->sc_rxptr;; i = CAS_NEXTRX(i + skip)) { + CAS_CDRXCSYNC(sc, i, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + word[0] = CAS_DMA_READ(sc->sc_rxcomps[i].cc_word[0]); + word[1] = CAS_DMA_READ(sc->sc_rxcomps[i].cc_word[1]); + word[2] = CAS_DMA_READ(sc->sc_rxcomps[i].cc_word[2]); + word[3] = CAS_DMA_READ(sc->sc_rxcomps[i].cc_word[3]); + + /* Stop if the hardware still owns the descriptor. */ + if ((word[0] & CAS_RC0_TYPE) == 0 || word[3] & CAS_RC3_OWN) + break; + + len = CAS_RC1_HDR_LEN(word[1]); + if (len > 0) { + off = CAS_RC1_HDR_OFF(word[1]); + idx = CAS_RC1_HDR_IDX(word[1]); + rxs = &sc->sc_rxsoft[idx]; + + DPRINTF(sc, ("hdr at idx %d, off %d, len %d\n", + idx, off, len)); + + bus_dmamap_sync(sc->sc_dmatag, rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + cp = rxs->rxs_kva + off * 256 + ETHER_ALIGN; + m = m_devget(cp, len, 0, ifp, NULL); + + if (word[0] & CAS_RC0_RELEASE_HDR) + cas_add_rxbuf(sc, idx); + + if (m != NULL) { + +#if NBPFILTER > 0 + /* + * Pass this up to any BPF listeners, but only + * pass it up the stack if its for us. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif /* NBPFILTER > 0 */ + + ifp->if_ipackets++; + m->m_pkthdr.csum_flags = 0; + (*ifp->if_input)(ifp, m); + } else + ifp->if_ierrors++; + } + + len = CAS_RC0_DATA_LEN(word[0]); + if (len > 0) { + off = CAS_RC0_DATA_OFF(word[0]); + idx = CAS_RC0_DATA_IDX(word[0]); + rxs = &sc->sc_rxsoft[idx]; + + DPRINTF(sc, ("data at idx %d, off %d, len %d\n", + idx, off, len)); + + bus_dmamap_sync(sc->sc_dmatag, rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + /* XXX We should not be copying the packet here. */ + cp = rxs->rxs_kva + off + ETHER_ALIGN; + m = m_devget(cp, len, 0, ifp, NULL); + + if (word[0] & CAS_RC0_RELEASE_DATA) + cas_add_rxbuf(sc, idx); + + if (m != NULL) { +#if NBPFILTER > 0 + /* + * Pass this up to any BPF listeners, but only + * pass it up the stack if its for us. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif /* NBPFILTER > 0 */ + + ifp->if_ipackets++; + m->m_pkthdr.csum_flags = 0; + (*ifp->if_input)(ifp, m); + } else + ifp->if_ierrors++; + } + + if (word[0] & CAS_RC0_SPLIT) + aprint_error_dev(sc->sc_dev, "split packet\n"); + + skip = CAS_RC0_SKIP(word[0]); + } + + while (sc->sc_rxptr != i) { + sc->sc_rxcomps[sc->sc_rxptr].cc_word[0] = 0; + sc->sc_rxcomps[sc->sc_rxptr].cc_word[1] = 0; + sc->sc_rxcomps[sc->sc_rxptr].cc_word[2] = 0; + sc->sc_rxcomps[sc->sc_rxptr].cc_word[3] = + CAS_DMA_WRITE(CAS_RC3_OWN); + CAS_CDRXCSYNC(sc, sc->sc_rxptr, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + sc->sc_rxptr = CAS_NEXTRX(sc->sc_rxptr); + } + + bus_space_write_4(t, h, CAS_RX_COMP_TAIL, sc->sc_rxptr); + + DPRINTF(sc, ("cas_rint: done sc->rxptr %d, complete %d\n", + sc->sc_rxptr, bus_space_read_4(t, h, CAS_RX_COMPLETION))); + + return (1); +} + +/* + * cas_add_rxbuf: + * + * Add a receive buffer to the indicated descriptor. + */ +int +cas_add_rxbuf(struct cas_softc *sc, int idx) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + + CAS_INIT_RXDESC(sc, sc->sc_rxdptr, idx); + + if ((sc->sc_rxdptr % 4) == 0) + bus_space_write_4(t, h, CAS_RX_KICK, sc->sc_rxdptr); + + if (++sc->sc_rxdptr == CAS_NRXDESC) + sc->sc_rxdptr = 0; + + return (0); +} + +int +cas_eint(struct cas_softc *sc, u_int status) +{ + char bits[128]; + if ((status & CAS_INTR_MIF) != 0) { + DPRINTF(sc, ("%s: link status changed\n", + device_xname(sc->sc_dev))); + return (1); + } + + snprintb(bits, sizeof(bits), CAS_INTR_BITS, status); + printf("%s: status=%s\n", device_xname(sc->sc_dev), bits); + return (1); +} + +int +cas_pint(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t seb = sc->sc_memh; + u_int32_t status; + + status = bus_space_read_4(t, seb, CAS_MII_INTERRUP_STATUS); + status |= bus_space_read_4(t, seb, CAS_MII_INTERRUP_STATUS); +#ifdef CAS_DEBUG + if (status) + printf("%s: link status changed\n", device_xname(sc->sc_dev)); +#endif + return (1); +} + +int +cas_intr(void *v) +{ + struct cas_softc *sc = (struct cas_softc *)v; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t seb = sc->sc_memh; + u_int32_t status; + int r = 0; +#ifdef CAS_DEBUG + char bits[128]; +#endif + + sc->sc_ev_intr.ev_count++; + + status = bus_space_read_4(t, seb, CAS_STATUS); +#ifdef CAS_DEBUG + snprintb(bits, sizeof(bits), CAS_INTR_BITS, status); +#endif + DPRINTF(sc, ("%s: cas_intr: cplt %x status %s\n", + device_xname(sc->sc_dev), (status>>19), bits)); + + if ((status & CAS_INTR_PCS) != 0) + r |= cas_pint(sc); + + if ((status & (CAS_INTR_TX_TAG_ERR | CAS_INTR_RX_TAG_ERR | + CAS_INTR_RX_COMP_FULL | CAS_INTR_BERR)) != 0) + r |= cas_eint(sc, status); + + if ((status & (CAS_INTR_TX_EMPTY | CAS_INTR_TX_INTME)) != 0) + r |= cas_tint(sc, status); + + if ((status & (CAS_INTR_RX_DONE | CAS_INTR_RX_NOBUF)) != 0) + r |= cas_rint(sc); + + /* We should eventually do more than just print out error stats. */ + if (status & CAS_INTR_TX_MAC) { + int txstat = bus_space_read_4(t, seb, CAS_MAC_TX_STATUS); +#ifdef CAS_DEBUG + if (txstat & ~CAS_MAC_TX_XMIT_DONE) + printf("%s: MAC tx fault, status %x\n", + device_xname(sc->sc_dev), txstat); +#endif + if (txstat & (CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_PKT_TOO_LONG)) + cas_init(ifp); + } + if (status & CAS_INTR_RX_MAC) { + int rxstat = bus_space_read_4(t, seb, CAS_MAC_RX_STATUS); +#ifdef CAS_DEBUG + if (rxstat & ~CAS_MAC_RX_DONE) + printf("%s: MAC rx fault, status %x\n", + device_xname(sc->sc_dev), rxstat); +#endif + /* + * On some chip revisions CAS_MAC_RX_OVERFLOW happen often + * due to a silicon bug so handle them silently. + */ + if (rxstat & CAS_MAC_RX_OVERFLOW) { + ifp->if_ierrors++; + cas_init(ifp); + } +#ifdef CAS_DEBUG + else if (rxstat & ~(CAS_MAC_RX_DONE | CAS_MAC_RX_FRAME_CNT)) + printf("%s: MAC rx fault, status %x\n", + device_xname(sc->sc_dev), rxstat); +#endif + } +#if NRND > 0 + rnd_add_uint32(&sc->rnd_source, status); +#endif + return (r); +} + + +void +cas_watchdog(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + + DPRINTF(sc, ("cas_watchdog: CAS_RX_CONFIG %x CAS_MAC_RX_STATUS %x " + "CAS_MAC_RX_CONFIG %x\n", + bus_space_read_4(sc->sc_memt, sc->sc_memh, CAS_RX_CONFIG), + bus_space_read_4(sc->sc_memt, sc->sc_memh, CAS_MAC_RX_STATUS), + bus_space_read_4(sc->sc_memt, sc->sc_memh, CAS_MAC_RX_CONFIG))); + + log(LOG_ERR, "%s: device timeout\n", device_xname(sc->sc_dev)); + ++ifp->if_oerrors; + + /* Try to get more packets going. */ + cas_init(ifp); +} + +/* + * Initialize the MII Management Interface + */ +void +cas_mifinit(struct cas_softc *sc) +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t mif = sc->sc_memh; + + /* Configure the MIF in frame mode */ + sc->sc_mif_config = bus_space_read_4(t, mif, CAS_MIF_CONFIG); + sc->sc_mif_config &= ~CAS_MIF_CONFIG_BB_ENA; + bus_space_write_4(t, mif, CAS_MIF_CONFIG, sc->sc_mif_config); +} + +/* + * MII interface + * + * The Cassini MII interface supports at least three different operating modes: + * + * Bitbang mode is implemented using data, clock and output enable registers. + * + * Frame mode is implemented by loading a complete frame into the frame + * register and polling the valid bit for completion. + * + * Polling mode uses the frame register but completion is indicated by + * an interrupt. + * + */ +int +cas_mii_readreg(device_t self, int phy, int reg) +{ + struct cas_softc *sc = device_private(self); + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t mif = sc->sc_memh; + int n; + u_int32_t v; + +#ifdef CAS_DEBUG + if (sc->sc_debug) + printf("cas_mii_readreg: phy %d reg %d\n", phy, reg); +#endif + + /* Construct the frame command */ + v = (reg << CAS_MIF_REG_SHIFT) | (phy << CAS_MIF_PHY_SHIFT) | + CAS_MIF_FRAME_READ; + + bus_space_write_4(t, mif, CAS_MIF_FRAME, v); + for (n = 0; n < 100; n++) { + DELAY(1); + v = bus_space_read_4(t, mif, CAS_MIF_FRAME); + if (v & CAS_MIF_FRAME_TA0) + return (v & CAS_MIF_FRAME_DATA); + } + + printf("%s: mii_read timeout\n", device_xname(sc->sc_dev)); + return (0); +} + +void +cas_mii_writereg(device_t self, int phy, int reg, int val) +{ + struct cas_softc *sc = device_private(self); + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t mif = sc->sc_memh; + int n; + u_int32_t v; + +#ifdef CAS_DEBUG + if (sc->sc_debug) + printf("cas_mii_writereg: phy %d reg %d val %x\n", + phy, reg, val); +#endif + + /* Construct the frame command */ + v = CAS_MIF_FRAME_WRITE | + (phy << CAS_MIF_PHY_SHIFT) | + (reg << CAS_MIF_REG_SHIFT) | + (val & CAS_MIF_FRAME_DATA); + + bus_space_write_4(t, mif, CAS_MIF_FRAME, v); + for (n = 0; n < 100; n++) { + DELAY(1); + v = bus_space_read_4(t, mif, CAS_MIF_FRAME); + if (v & CAS_MIF_FRAME_TA0) + return; + } + + printf("%s: mii_write timeout\n", device_xname(sc->sc_dev)); +} + +void +cas_mii_statchg(device_t self) +{ + struct cas_softc *sc = device_private(self); +#ifdef CAS_DEBUG + int instance = IFM_INST(sc->sc_media.ifm_cur->ifm_media); +#endif + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t mac = sc->sc_memh; + u_int32_t v; + +#ifdef CAS_DEBUG + if (sc->sc_debug) + printf("cas_mii_statchg: status change: phy = %d\n", + sc->sc_phys[instance]); +#endif + + /* Set tx full duplex options */ + bus_space_write_4(t, mac, CAS_MAC_TX_CONFIG, 0); + delay(10000); /* reg must be cleared and delay before changing. */ + v = CAS_MAC_TX_ENA_IPG0|CAS_MAC_TX_NGU|CAS_MAC_TX_NGU_LIMIT| + CAS_MAC_TX_ENABLE; + if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) != 0) { + v |= CAS_MAC_TX_IGN_CARRIER|CAS_MAC_TX_IGN_COLLIS; + } + bus_space_write_4(t, mac, CAS_MAC_TX_CONFIG, v); + + /* XIF Configuration */ + v = CAS_MAC_XIF_TX_MII_ENA; + v |= CAS_MAC_XIF_LINK_LED; + + /* MII needs echo disable if half duplex. */ + if ((IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) != 0) + /* turn on full duplex LED */ + v |= CAS_MAC_XIF_FDPLX_LED; + else + /* half duplex -- disable echo */ + v |= CAS_MAC_XIF_ECHO_DISABL; + + switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) { + case IFM_1000_T: /* Gigabit using GMII interface */ + case IFM_1000_SX: + v |= CAS_MAC_XIF_GMII_MODE; + break; + default: + v &= ~CAS_MAC_XIF_GMII_MODE; + } + bus_space_write_4(t, mac, CAS_MAC_XIF_CONFIG, v); +} + +int +cas_pcs_readreg(device_t self, int phy, int reg) +{ + struct cas_softc *sc = device_private(self); + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t pcs = sc->sc_memh; + +#ifdef CAS_DEBUG + if (sc->sc_debug) + printf("cas_pcs_readreg: phy %d reg %d\n", phy, reg); +#endif + + if (phy != CAS_PHYAD_EXTERNAL) + return (0); + + switch (reg) { + case MII_BMCR: + reg = CAS_MII_CONTROL; + break; + case MII_BMSR: + reg = CAS_MII_STATUS; + break; + case MII_ANAR: + reg = CAS_MII_ANAR; + break; + case MII_ANLPAR: + reg = CAS_MII_ANLPAR; + break; + case MII_EXTSR: + return (EXTSR_1000XFDX|EXTSR_1000XHDX); + default: + return (0); + } + + return bus_space_read_4(t, pcs, reg); +} + +void +cas_pcs_writereg(device_t self, int phy, int reg, int val) +{ + struct cas_softc *sc = device_private(self); + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t pcs = sc->sc_memh; + int reset = 0; + +#ifdef CAS_DEBUG + if (sc->sc_debug) + printf("cas_pcs_writereg: phy %d reg %d val %x\n", + phy, reg, val); +#endif + + if (phy != CAS_PHYAD_EXTERNAL) + return; + + if (reg == MII_ANAR) + bus_space_write_4(t, pcs, CAS_MII_CONFIG, 0); + + switch (reg) { + case MII_BMCR: + reset = (val & CAS_MII_CONTROL_RESET); + reg = CAS_MII_CONTROL; + break; + case MII_BMSR: + reg = CAS_MII_STATUS; + break; + case MII_ANAR: + reg = CAS_MII_ANAR; + break; + case MII_ANLPAR: + reg = CAS_MII_ANLPAR; + break; + default: + return; + } + + bus_space_write_4(t, pcs, reg, val); + + if (reset) + cas_bitwait(sc, pcs, CAS_MII_CONTROL, CAS_MII_CONTROL_RESET, 0); + + if (reg == CAS_MII_ANAR || reset) + bus_space_write_4(t, pcs, CAS_MII_CONFIG, + CAS_MII_CONFIG_ENABLE); +} + +int +cas_mediachange(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + + if (mii->mii_instance) { + struct mii_softc *miisc; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + + return (mii_mediachg(&sc->sc_mii)); +} + +void +cas_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct cas_softc *sc = ifp->if_softc; + + mii_pollstat(&sc->sc_mii); + ifmr->ifm_active = sc->sc_mii.mii_media_active; + ifmr->ifm_status = sc->sc_mii.mii_media_status; +} + +/* + * Process an ioctl request. + */ +int +cas_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct cas_softc *sc = ifp->if_softc; + int s, error = 0; + + s = splnet(); + + if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) { + error = 0; + if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI) + ; + else if (ifp->if_flags & IFF_RUNNING) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + cas_iff(sc); + } + } + + /* Try to get things going again */ +/* + if (ifp->if_flags & IFF_UP) + cas_start(ifp); +*/ + splx(s); + return (error); +} + +static bool +cas_suspend(device_t self PMF_FN_ARGS) +{ + struct cas_softc *sc = device_private(self); + + if (sc->sc_ih != NULL) { + pci_intr_disestablish(sc->sc_pc, sc->sc_ih); + sc->sc_ih = NULL; + } + + return true; +} + +static bool +cas_resume(device_t self PMF_FN_ARGS) +{ + struct cas_softc *sc = device_private(self); + + return cas_estintr(sc); +} + +static bool +cas_estintr(struct cas_softc *sc) +{ + const char *intrstr = NULL; + + intrstr = pci_intr_string(sc->sc_pc, sc->sc_handle); + sc->sc_ih = pci_intr_establish(sc->sc_pc, sc->sc_handle, + IPL_NET, cas_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(sc->sc_dev, "unable to establish interrupt"); + if (intrstr != NULL) + aprint_error(" at %s", intrstr); + aprint_error("\n"); + return false; + } + + aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); + return true; +} + +bool +cas_shutdown(device_t self, int howto) +{ + struct cas_softc *sc = device_private(self); + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + + cas_stop(ifp, 1); + + return true; +} + +void +cas_iff(struct cas_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ethercom *ec = &sc->sc_ethercom; + struct ether_multi *enm; + struct ether_multistep step; + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + u_int32_t crc, hash[16], rxcfg; + int i; + + rxcfg = bus_space_read_4(t, h, CAS_MAC_RX_CONFIG); + rxcfg &= ~(CAS_MAC_RX_HASH_FILTER | CAS_MAC_RX_PROMISCUOUS | + CAS_MAC_RX_PROMISC_GRP); + ifp->if_flags &= ~IFF_ALLMULTI; + + if (ifp->if_flags & IFF_PROMISC || ec->ec_multicnt > 0) { + ifp->if_flags |= IFF_ALLMULTI; + if (ifp->if_flags & IFF_PROMISC) + rxcfg |= CAS_MAC_RX_PROMISCUOUS; + else + rxcfg |= CAS_MAC_RX_PROMISC_GRP; + } else { + /* + * Set up multicast address filter by passing all multicast + * addresses through a crc generator, and then using the + * high order 8 bits as an index into the 256 bit logical + * address filter. The high order 4 bits selects the word, + * while the other 4 bits select the bit within the word + * (where bit 0 is the MSB). + */ + + rxcfg |= CAS_MAC_RX_HASH_FILTER; + + /* Clear hash table */ + for (i = 0; i < 16; i++) + hash[i] = 0; + + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + crc = ether_crc32_le(enm->enm_addrlo, + ETHER_ADDR_LEN); + + /* Just want the 8 most significant bits. */ + crc >>= 24; + + /* Set the corresponding bit in the filter. */ + hash[crc >> 4] |= 1 << (15 - (crc & 15)); + + ETHER_NEXT_MULTI(step, enm); + } + + /* Now load the hash table into the chip (if we are using it) */ + for (i = 0; i < 16; i++) { + bus_space_write_4(t, h, + CAS_MAC_HASH0 + i * (CAS_MAC_HASH1 - CAS_MAC_HASH0), + hash[i]); + } + } + + bus_space_write_4(t, h, CAS_MAC_RX_CONFIG, rxcfg); +} + +int +cas_encap(struct cas_softc *sc, struct mbuf *mhead, u_int32_t *bixp) +{ + u_int64_t flags; + u_int32_t cur, frag, i; + bus_dmamap_t map; + + cur = frag = *bixp; + map = sc->sc_txd[cur].sd_map; + + if (bus_dmamap_load_mbuf(sc->sc_dmatag, map, mhead, + BUS_DMA_NOWAIT) != 0) { + return (ENOBUFS); + } + + if ((sc->sc_tx_cnt + map->dm_nsegs) > (CAS_NTXDESC - 2)) { + bus_dmamap_unload(sc->sc_dmatag, map); + return (ENOBUFS); + } + + bus_dmamap_sync(sc->sc_dmatag, map, 0, map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + for (i = 0; i < map->dm_nsegs; i++) { + sc->sc_txdescs[frag].cd_addr = + CAS_DMA_WRITE(map->dm_segs[i].ds_addr); + flags = (map->dm_segs[i].ds_len & CAS_TD_BUFSIZE) | + (i == 0 ? CAS_TD_START_OF_PACKET : 0) | + ((i == (map->dm_nsegs - 1)) ? CAS_TD_END_OF_PACKET : 0); + sc->sc_txdescs[frag].cd_flags = CAS_DMA_WRITE(flags); + bus_dmamap_sync(sc->sc_dmatag, sc->sc_cddmamap, + CAS_CDTXOFF(frag), sizeof(struct cas_desc), + BUS_DMASYNC_PREWRITE); + cur = frag; + if (++frag == CAS_NTXDESC) + frag = 0; + } + + sc->sc_tx_cnt += map->dm_nsegs; + sc->sc_txd[*bixp].sd_map = sc->sc_txd[cur].sd_map; + sc->sc_txd[cur].sd_map = map; + sc->sc_txd[cur].sd_mbuf = mhead; + + bus_space_write_4(sc->sc_memt, sc->sc_memh, CAS_TX_KICK, frag); + + *bixp = frag; + + /* sync descriptors */ + + return (0); +} + +/* + * Transmit interrupt. + */ +int +cas_tint(struct cas_softc *sc, u_int32_t status) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct cas_sxd *sd; + u_int32_t cons, comp; + + comp = bus_space_read_4(sc->sc_memt, sc->sc_memh, CAS_TX_COMPLETION); + cons = sc->sc_tx_cons; + while (cons != comp) { + sd = &sc->sc_txd[cons]; + if (sd->sd_mbuf != NULL) { + bus_dmamap_sync(sc->sc_dmatag, sd->sd_map, 0, + sd->sd_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmatag, sd->sd_map); + m_freem(sd->sd_mbuf); + sd->sd_mbuf = NULL; + ifp->if_opackets++; + } + sc->sc_tx_cnt--; + if (++cons == CAS_NTXDESC) + cons = 0; + } + sc->sc_tx_cons = cons; + + if (sc->sc_tx_cnt < CAS_NTXDESC - 2) + ifp->if_flags &= ~IFF_OACTIVE; + if (sc->sc_tx_cnt == 0) + ifp->if_timer = 0; + + cas_start(ifp); + + return (1); +} + +void +cas_start(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + struct mbuf *m; + u_int32_t bix; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + bix = sc->sc_tx_prod; + while (sc->sc_txd[bix].sd_mbuf == NULL) { + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) + break; + +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the + * packet before we commit it to the wire. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + /* + * Encapsulate this packet and start it going... + * or fail... + */ + if (cas_encap(sc, m, &bix)) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m); + ifp->if_timer = 5; + } + + sc->sc_tx_prod = bix; +} diff --git a/sys/dev/pci/if_casreg.h b/sys/dev/pci/if_casreg.h new file mode 100644 index 00000000000..1acbb25f72c --- /dev/null +++ b/sys/dev/pci/if_casreg.h @@ -0,0 +1,617 @@ +/* $NetBSD: if_casreg.h,v 1.1 2010/01/07 09:19:55 jdc Exp $ */ +/* $OpenBSD: if_casreg.h,v 1.10 2008/05/31 22:49:03 kettenis Exp $ */ + +/* + * + * Copyright (C) 2007 Mark Kettenis. + * Copyright (C) 2001 Eduardo Horvath. + * 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. + * + * 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. + * + */ + +#ifndef _IF_CASREG_H +#define _IF_CASREG_H + +/* + * Register definitions for Sun Cassini ethernet controllers. + */ + +/* + * First bank: these registers live at the start of the PCI mapping. + */ +#define CAS_SEB_STATE 0x0000 /* SEB state reg, R/O */ +#define CAS_CONFIG 0x0004 /* config reg */ +#define CAS_STATUS 0x000c /* status reg */ +/* Note: Reading the status reg clears bits 0-6 */ +#define CAS_INTMASK 0x0010 +#define CAS_INTACK 0x0014 /* Interrupt acknowledge, W/O */ +#define CAS_STATUS_ALIAS 0x001c +/* Note: Same as CAS_STATUS but reading it does not clear bits. */ + +#define CAS_ERROR_STATUS 0x1000 /* PCI error status R/C */ +#define CAS_ERROR_MASK 0x0004 +#define CAS_BIF_CONFIG 0x0008 /* BIF config reg */ +#define CAS_BIF_DIAG 0x000c +#define CAS_RESET 0x1010 /* Software reset register */ + +/* Bits in CAS_SEB register */ +#define CAS_SEB_ARB 0x000000002 /* Arbitration status */ +#define CAS_SEB_RXWON 0x000000004 + +/* Bits in CAS_CONFIG register */ +#define CAS_CONFIG_BURST_64 0x000000000 /* 0->infinity, 1->64KB */ +#define CAS_CONFIG_BURST_INF 0x000000001 /* 0->infinity, 1->64KB */ +#define CAS_CONFIG_TXDMA_LIMIT 0x00000003e +#define CAS_CONFIG_RXDMA_LIMIT 0x0000007c0 + +#define CAS_CONFIG_TXDMA_LIMIT_SHIFT 1 +#define CAS_CONFIG_RXDMA_LIMIT_SHIFT 6 + +/* Top part of CAS_STATUS has TX completion information */ +#define CAS_STATUS_TX_COMPL 0xfff800000 /* TX completion reg. */ + +/* + * Interrupt bits, for both the CAS_STATUS and CAS_INTMASK regs. + * Bits 0-6 auto-clear when read. + */ +#define CAS_INTR_TX_INTME 0x000000001 /* Frame w/INTME bit set sent */ +#define CAS_INTR_TX_EMPTY 0x000000002 /* TX ring empty */ +#define CAS_INTR_TX_DONE 0x000000004 /* TX complete */ +#define CAS_INTR_TX_TAG_ERR 0x000000008 +#define CAS_INTR_RX_DONE 0x000000010 /* Got a packet */ +#define CAS_INTR_RX_NOBUF 0x000000020 +#define CAS_INTR_RX_TAG_ERR 0x000000040 +#define CAS_INTR_RX_COMP_FULL 0x000000080 +#define CAS_INTR_PCS 0x000002000 /* Physical Code Sub-layer */ +#define CAS_INTR_TX_MAC 0x000004000 +#define CAS_INTR_RX_MAC 0x000008000 +#define CAS_INTR_MAC_CONTROL 0x000010000 /* MAC control interrupt */ +#define CAS_INTR_MIF 0x000020000 +#define CAS_INTR_BERR 0x000040000 /* Bus error interrupt */ +#define CAS_INTR_BITS "\020" \ + "\1INTME\2TXEMPTY\3TXDONE\4TX_TAG_ERR" \ + "\5RXDONE\6RXNOBUF\7RX_TAG_ERR" \ + "\10RX_COMP_FULL" \ + "\16PCS\17TXMAC\20RXMAC" \ + "\21MACCONTROL\22MIF\23BERR" + +/* CAS_ERROR_STATUS and CAS_ERROR_MASK PCI error bits */ +#define CAS_ERROR_STAT_BADACK 0x000000001 /* No ACK64# */ +#define CAS_ERROR_STAT_DTRTO 0x000000002 /* Delayed xaction timeout */ +#define CAS_ERROR_STAT_OTHERS 0x000000004 + +/* CAS_BIF_CONFIG register bits */ +#define CAS_BIF_CONFIG_SLOWCLK 0x000000001 /* Parity error timing */ +#define CAS_BIF_CONFIG_HOST_64 0x000000002 /* 64-bit host */ +#define CAS_BIF_CONFIG_B64D_DIS 0x000000004 /* no 64-bit data cycle */ +#define CAS_BIF_CONFIG_M66EN 0x000000008 + +/* CAS_RESET register bits -- TX and RX self clear when complete. */ +#define CAS_RESET_TX 0x000000001 /* Reset TX half */ +#define CAS_RESET_RX 0x000000002 /* Reset RX half */ +#define CAS_RESET_RSTOUT 0x000000004 /* Force PCI RSTOUT# */ +#define CAS_RESET_BLOCK_PCS 0x00000008 /* Block PCS reset */ + +/* TX DMA registers */ +#define CAS_TX_CONFIG 0x2004 + +#define CAS_TX_FIFO_WR_PTR 0x2014 /* FIFO write pointer */ +#define CAS_TX_FIFO_SDWR_PTR 0x2018 /* FIFO shadow write pointer */ +#define CAS_TX_FIFO_RD_PTR 0x201c /* FIFO read pointer */ +#define CAS_TX_FIFO_SDRD_PTR 0x2020 /* FIFO shadow read pointer */ +#define CAS_TX_FIFO_PKT_CNT 0x2024 /* FIFO packet counter */ + +#define CAS_TX_STATE_MACHINE 0x2028 /* ETX state machine reg */ +#define CAS_TX_DATA_PTR 0x2030 /* ETX state machine reg (64-bit)*/ + +#define CAS_TX_KICK1 0x2038 /* Write last valid desc + 1 */ +#define CAS_TX_KICK2 0x203c +#define CAS_TX_KICK3 0x2040 +#define CAS_TX_KICK4 0x2044 +#define CAS_TX_COMPLETION1 0x2048 +#define CAS_TX_COMPLETION2 0x204c +#define CAS_TX_COMPLETION3 0x2050 +#define CAS_TX_COMPLETION4 0x2054 +#define CAS_TX_RING_PTR_LO1 0x2060 +#define CAS_TX_RING_PTR_HI1 0x2064 +#define CAS_TX_RING_PTR_LO2 0x2068 +#define CAS_TX_RING_PTR_HI2 0x206c +#define CAS_TX_RING_PTR_LO3 0x2070 +#define CAS_TX_RING_PTR_HI3 0x2074 +#define CAS_TX_RING_PTR_LO4 0x2078 +#define CAS_TX_RING_PTR_HI4 0x207c +#define CAS_TX_MAXBURST1 0x2080 +#define CAS_TX_MAXBURST2 0x2084 +#define CAS_TX_MAXBURST3 0x2088 +#define CAS_TX_MAXBURST4 0x208c + +#define CAS_TX_KICK CAS_TX_KICK3 +#define CAS_TX_COMPLETION CAS_TX_COMPLETION3 +#define CAS_TX_RING_PTR_LO CAS_TX_RING_PTR_LO3 +#define CAS_TX_RING_PTR_HI CAS_TX_RING_PTR_HI3 + +#define CAS_TX_FIFO_ADDRESS 0x2104 +#define CAS_TX_FIFO_TAG 0x2108 +#define CAS_TX_FIFO_DATA_LO 0x210c +#define CAS_TX_FIFO_DATA_HI_T1 0x2110 +#define CAS_TX_FIFO_DATA_HI_T0 0x2114 +#define CAS_TX_FIFO_SIZE 0x2118 +#define CAS_TX_DEBUG 0x3028 + +/* CAS_TX_CONFIG register bits. */ +#define CAS_TX_CONFIG_TXDMA_EN 0x00000001 /* TX DMA enable */ +#define CAS_TX_CONFIG_TXRING_SZ 0x0000003c /* TX ring size */ +#define CAS_TX_CONFIG_PACED 0x00100000 /* TX_all_int modifier */ + +#define CAS_RING_SZ_32 0 /* 32 descriptors */ +#define CAS_RING_SZ_64 1 +#define CAS_RING_SZ_128 2 +#define CAS_RING_SZ_256 3 +#define CAS_RING_SZ_512 4 +#define CAS_RING_SZ_1024 5 +#define CAS_RING_SZ_2048 6 +#define CAS_RING_SZ_4096 7 +#define CAS_RING_SZ_8192 8 + +/* CAS_TX_COMPLETION register bits */ +#define CAS_TX_COMPLETION_MASK 0x00001fff /* # of last descriptor */ + +/* RX DMA registers */ +#define CAS_RX_CONFIG 0x4000 +#define CAS_RX_PAGE_SIZE 0x4004 +#define CAS_RX_FIFO_WR_PTR 0x4008 /* FIFO write pointer */ +#define CAS_RX_FIFO_RD_PTR 0x400c /* FIFO read pointer */ +#define CAS_RX_IPPFIFO_WR_PTR 0x4010 /* IPP FIFO write pointer */ +#define CAS_RX_IPPFIFO_RD_PTR 0x4014 /* IPP FIFO read pointer */ +#define CAS_RX_IPPFIFO_SDWR_PTR 0x4018 /* FIFO shadow write pointer */ +#define CAS_RX_DEBUG 0x401c /* Debug reg */ +#define CAS_RX_PAUSE_THRESH 0x4020 +#define CAS_RX_KICK 0x4024 /* Write last valid desc + 1 */ +#define CAS_RX_DRING_PTR_LO 0x4028 +#define CAS_RX_DRING_PTR_HI 0x402c +#define CAS_RX_CRING_PTR_LO 0x4030 +#define CAS_RX_CRING_PTR_HI 0x4034 +#define CAS_RX_COMPLETION 0x4038 /* First pending desc */ +#define CAS_RX_COMP_HEAD 0x403c +#define CAS_RX_COMP_TAIL 0x4040 +#define CAS_RX_BLANKING 0x4044 /* Interrupt blanking reg */ +#define CAS_RX_RED 0x404c /* Random Early Detection */ + +#define CAS_RX_IPP_PKT_CNT 0x4054 /* IPP packet counter */ + +#define CAS_RX_FIFO_ADDRESS 0x4080 +#define CAS_RX_FIFO_TAG 0x4084 +#define CAS_RX_FIFO_DATA_LO 0x4088 +#define CAS_RX_FIFO_DATA_HI_T0 0x408c +#define CAS_RX_FIFO_DATA_HI_T1 0x4090 + +/* The following registers only exist on Cassini+. */ +#define CAS_RX_DRING_PTR_LO2 0x4200 +#define CAS_RX_DRING_PTR_HI2 0x4204 +#define CAS_RX_CRING_PTR_LO2 0x4208 +#define CAS_RX_CRING_PTR_HI2 0x420c +#define CAS_RX_CRING_PTR_LO3 0x4210 +#define CAS_RX_CRING_PTR_HI3 0x4214 +#define CAS_RX_CRING_PTR_LO4 0x4218 +#define CAS_RX_CRING_PTR_HI4 0x421c +#define CAS_RX_KICK2 0x4220 +#define CAS_RX_COMPLETION2 0x4224 +#define CAS_RX_COMP_HEAD2 0x4228 +#define CAS_RX_COMP_TAIL2 0x422c +#define CAS_RX_COMP_HEAD3 0x4230 +#define CAS_RX_COMP_TAIL3 0x4234 +#define CAS_RX_COMP_HEAD4 0x4238 +#define CAS_RX_COMP_TAIL4 0x423c + +/* CAS_RX_CONFIG register bits. */ +#define CAS_RX_CONFIG_RXDMA_EN 0x00000001 /* RX DMA enable */ +#define CAS_RX_CONFIG_RXDRNG_SZ 0x0000001e /* RX descriptor ring size */ +#define CAS_RX_CONFIG_RXCRNG_SZ 0x000001e0 /* RX completion ring size */ +#define CAS_RX_CONFIG_BATCH_DIS 0x00000200 /* desc batching disable */ +#define CAS_RX_CONFIG_FBOFF 0x00001c00 /* first byte offset */ + +#define CAS_RX_CONFIG_RXDRNG_SZ_SHIFT 1 +#define CAS_RX_CONFIG_RXCRNG_SZ_SHIFT 5 +#define CAS_RX_CONFIG_FBOFF_SHFT 10 +#define CAS_RX_CONFIG_RXDRNG2_SZ_SHIFT 16 /* Cassini+ */ + +/* CAS_RX_PAGE_SIZE register bits. */ +#define CAS_RX_PAGE_SIZE_SZ 0x00000003 /* Page size */ +#define CAS_RX_PAGE_SIZE_COUNT 0x00007800 /* MTU buffers per page */ +#define CAS_RX_PAGE_SIZE_STRIDE 0x18000000 /* MTU buffer separation */ +#define CAS_RX_PAGE_SIZE_FBOFF 0xc0000000 /* Firts byte offset */ + +#define CAS_RX_PAGE_SIZE_COUNT_SHIFT 11 +#define CAS_RX_PAGE_SIZE_STRIDE_SHIFT 27 +#define CAS_RX_PAGE_SIZE_FBOFF_SHIFT 30 + +/* CAS_RX_PAUSE_THRESH register bits -- sizes in multiples of 64 bytes */ +#define CAS_RX_PTH_XOFF_THRESH 0x000001ff +#define CAS_RX_PTH_XON_THRESH 0x07fc0000 + +/* CAS_RX_BLANKING register bits */ +#define CAS_RX_BLANKING_PACKETS 0x000001ff /* Delay intr for x packets */ +#define CAS_RX_BLANKING_TIME 0x03fc0000 /* Delay intr for x ticks */ +/* One tick is 1048 PCI clocks, or 16us at 66MHz */ + +/* CAS_MAC registers */ +#define CAS_MAC_TXRESET 0x6000 /* Store 1, cleared when done */ +#define CAS_MAC_RXRESET 0x6004 /* ditto */ +#define CAS_MAC_SEND_PAUSE_CMD 0x6008 +#define CAS_MAC_TX_STATUS 0x6010 +#define CAS_MAC_RX_STATUS 0x6014 +#define CAS_MAC_CONTROL_STATUS 0x6018 /* MAC control status reg */ +#define CAS_MAC_TX_MASK 0x6020 /* TX MAC mask register */ +#define CAS_MAC_RX_MASK 0x6024 +#define CAS_MAC_CONTROL_MASK 0x6028 +#define CAS_MAC_TX_CONFIG 0x6030 +#define CAS_MAC_RX_CONFIG 0x6034 +#define CAS_MAC_CONTROL_CONFIG 0x6038 +#define CAS_MAC_XIF_CONFIG 0x603c +#define CAS_MAC_IPG0 0x6040 /* inter packet gap 0 */ +#define CAS_MAC_IPG1 0x6044 /* inter packet gap 1 */ +#define CAS_MAC_IPG2 0x6048 /* inter packet gap 2 */ +#define CAS_MAC_SLOT_TIME 0x604c /* slot time, bits 0-7 */ +#define CAS_MAC_MAC_MIN_FRAME 0x6050 +#define CAS_MAC_MAC_MAX_FRAME 0x6054 +#define CAS_MAC_PREAMBLE_LEN 0x6058 +#define CAS_MAC_JAM_SIZE 0x605c +#define CAS_MAC_ATTEMPT_LIMIT 0x6060 +#define CAS_MAC_CONTROL_TYPE 0x6064 + +#define CAS_MAC_ADDR0 0x6080 /* Normal MAC address 0 */ +#define CAS_MAC_ADDR1 0x6084 +#define CAS_MAC_ADDR2 0x6088 +#define CAS_MAC_ADDR3 0x608c /* Alternate MAC address 0 */ +#define CAS_MAC_ADDR4 0x6090 +#define CAS_MAC_ADDR5 0x6094 +#define CAS_MAC_ADDR42 0x6128 /* Control MAC address 0 */ +#define CAS_MAC_ADDR43 0x612c +#define CAS_MAC_ADDR44 0x6130 + +#define CAS_MAC_ADDR_FILTER0 0x614c +#define CAS_MAC_ADDR_FILTER1 0x6150 +#define CAS_MAC_ADDR_FILTER2 0x6154 +#define CAS_MAC_ADR_FLT_MASK1_2 0x6158 /* Address filter mask 1,2 */ +#define CAS_MAC_ADR_FLT_MASK0 0x615c /* Address filter mask 0 reg */ + +#define CAS_MAC_HASH0 0x6160 /* Hash table 0 */ +#define CAS_MAC_HASH1 0x6164 +#define CAS_MAC_HASH2 0x6168 +#define CAS_MAC_HASH3 0x616c +#define CAS_MAC_HASH4 0x6170 +#define CAS_MAC_HASH5 0x6174 +#define CAS_MAC_HASH6 0x6178 +#define CAS_MAC_HASH7 0x617c +#define CAS_MAC_HASH8 0x6180 +#define CAS_MAC_HASH9 0x6184 +#define CAS_MAC_HASH10 0x6188 +#define CAS_MAC_HASH11 0x618c +#define CAS_MAC_HASH12 0x6190 +#define CAS_MAC_HASH13 0x6194 +#define CAS_MAC_HASH14 0x6198 +#define CAS_MAC_HASH15 0x619c + +#define CAS_MAC_NORM_COLL_CNT 0x61a0 /* Normal collision counter */ +#define CAS_MAC_FIRST_COLL_CNT 0x61a4 /* 1st successful collision cntr */ +#define CAS_MAC_EXCESS_COLL_CNT 0x61a8 /* Excess collision counter */ +#define CAS_MAC_LATE_COLL_CNT 0x61ac /* Late collision counter */ +#define CAS_MAC_DEFER_TMR_CNT 0x61b0 /* defer timer counter */ +#define CAS_MAC_PEAK_ATTEMPTS 0x61b4 +#define CAS_MAC_RX_FRAME_COUNT 0x61b8 +#define CAS_MAC_RX_LEN_ERR_CNT 0x61bc +#define CAS_MAC_RX_ALIGN_ERR 0x61c0 +#define CAS_MAC_RX_CRC_ERR_CNT 0x61c4 +#define CAS_MAC_RX_CODE_VIOL 0x61c8 +#define CAS_MAC_RANDOM_SEED 0x61cc +#define CAS_MAC_MAC_STATE 0x61d0 /* MAC sstate machine reg */ + +/* CAS_MAC_SEND_PAUSE_CMD register bits */ +#define CAS_MAC_PAUSE_CMD_TIME 0x0000ffff +#define CAS_MAC_PAUSE_CMD_SEND 0x00010000 + +/* CAS_MAC_TX_STATUS and _MASK register bits */ +#define CAS_MAC_TX_XMIT_DONE 0x00000001 +#define CAS_MAC_TX_UNDERRUN 0x00000002 +#define CAS_MAC_TX_PKT_TOO_LONG 0x00000004 +#define CAS_MAC_TX_NCC_EXP 0x00000008 /* Normal collision cnt exp */ +#define CAS_MAC_TX_ECC_EXP 0x00000010 +#define CAS_MAC_TX_LCC_EXP 0x00000020 +#define CAS_MAC_TX_FCC_EXP 0x00000040 +#define CAS_MAC_TX_DEFER_EXP 0x00000080 +#define CAS_MAC_TX_PEAK_EXP 0x00000100 + +/* CAS_MAC_RX_STATUS and _MASK register bits */ +#define CAS_MAC_RX_DONE 0x00000001 +#define CAS_MAC_RX_OVERFLOW 0x00000002 +#define CAS_MAC_RX_FRAME_CNT 0x00000004 +#define CAS_MAC_RX_ALIGN_EXP 0x00000008 +#define CAS_MAC_RX_CRC_EXP 0x00000010 +#define CAS_MAC_RX_LEN_EXP 0x00000020 +#define CAS_MAC_RX_CVI_EXP 0x00000040 /* Code violation */ + +/* CAS_MAC_CONTROL_STATUS and CAS_MAC_CONTROL_MASK register bits */ +#define CAS_MAC_PAUSED 0x00000001 /* Pause received */ +#define CAS_MAC_PAUSE 0x00000002 /* enter pause state */ +#define CAS_MAC_RESUME 0x00000004 /* exit pause state */ +#define CAS_MAC_PAUSE_TIME 0xffff0000 + +/* CAS_MAC_XIF_CONFIG register bits */ +#define CAS_MAC_XIF_TX_MII_ENA 0x00000001 /* Enable XIF output drivers */ +#define CAS_MAC_XIF_MII_LOOPBK 0x00000002 /* Enable MII loopback mode */ +#define CAS_MAC_XIF_ECHO_DISABL 0x00000004 /* Disable echo */ +#define CAS_MAC_XIF_GMII_MODE 0x00000008 /* Select GMII/MII mode */ +#define CAS_MAC_XIF_MII_BUF_ENA 0x00000010 /* Enable MII recv buffers */ +#define CAS_MAC_XIF_LINK_LED 0x00000020 /* force link LED active */ +#define CAS_MAC_XIF_FDPLX_LED 0x00000040 /* force FDPLX LED active */ + +/* CAS_MAC_SLOT_TIME register bits */ +#define CAS_MAC_SLOT_INT 0x40 +#define CAS_MAC_SLOT_EXT 0x200 /* external phy */ + +/* CAS_MAC_TX_CONFIG register bits */ +#define CAS_MAC_TX_ENABLE 0x00000001 /* TX enable */ +#define CAS_MAC_TX_IGN_CARRIER 0x00000002 /* Ignore carrier sense */ +#define CAS_MAC_TX_IGN_COLLIS 0x00000004 /* ignore collisions */ +#define CAS_MAC_TX_ENA_IPG0 0x00000008 /* extend Rx-to-TX IPG */ +#define CAS_MAC_TX_NGU 0x00000010 /* Never give up */ +#define CAS_MAC_TX_NGU_LIMIT 0x00000020 /* Never give up limit */ +#define CAS_MAC_TX_NO_BACKOFF 0x00000040 +#define CAS_MAC_TX_SLOWDOWN 0x00000080 +#define CAS_MAC_TX_NO_FCS 0x00000100 /* no FCS will be generated */ +#define CAS_MAC_TX_CARR_EXTEND 0x00000200 /* Ena TX Carrier Extension */ +/* Carrier Extension is required for half duplex Gbps operation */ + +/* CAS_MAC_RX_CONFIG register bits */ +#define CAS_MAC_RX_ENABLE 0x00000001 /* RX enable */ +#define CAS_MAC_RX_STRIP_PAD 0x00000002 /* strip pad bytes */ +#define CAS_MAC_RX_STRIP_CRC 0x00000004 +#define CAS_MAC_RX_PROMISCUOUS 0x00000008 /* promiscuous mode */ +#define CAS_MAC_RX_PROMISC_GRP 0x00000010 /* promiscuous group mode */ +#define CAS_MAC_RX_HASH_FILTER 0x00000020 /* enable hash filter */ +#define CAS_MAC_RX_ADDR_FILTER 0x00000040 /* enable address filter */ +#define CAS_MAC_RX_ERRCHK_DIS 0x00000080 /* disable error checking */ +#define CAS_MAC_RX_CARR_EXTEND 0x00000100 /* Ena RX Carrier Extension */ +/* + * Carrier Extension enables reception of packet bursts generated by + * senders with carrier extension enabled. + */ + +/* CAS_MAC_CONTROL_CONFIG bits */ +#define CAS_MAC_CC_TX_PAUSE 0x00000001 /* send pause enabled */ +#define CAS_MAC_CC_RX_PAUSE 0x00000002 /* receive pause enabled */ +#define CAS_MAC_CC_PASS_PAUSE 0x00000004 /* pass pause up */ + +/* Cassini MIF registers */ +/* Bit bang registers use low bit only */ +#define CAS_MIF_BB_CLOCK 0x6200 /* bit bang clock */ +#define CAS_MIF_BB_DATA 0x6204 /* bit bang data */ +#define CAS_MIF_BB_OUTPUT_ENAB 0x6208 +#define CAS_MIF_FRAME 0x620c /* MIF frame - ctl and data */ +#define CAS_MIF_CONFIG 0x6210 +#define CAS_MIF_INTERRUPT_MASK 0x6214 +#define CAS_MIF_BASIC_STATUS 0x6218 +#define CAS_MIF_STATE_MACHINE 0x621c + +/* CAS_MIF_FRAME bits */ +#define CAS_MIF_FRAME_DATA 0x0000ffff +#define CAS_MIF_FRAME_TA0 0x00010000 /* TA bit, 1 for completion */ +#define CAS_MIF_FRAME_TA1 0x00020000 /* TA bits */ +#define CAS_MIF_FRAME_REG_ADDR 0x007c0000 +#define CAS_MIF_FRAME_PHY_ADDR 0x0f800000 /* phy address, should be 0 */ +#define CAS_MIF_FRAME_OP 0x30000000 /* operation - write/read */ +#define CAS_MIF_FRAME_START 0xc0000000 /* START bits */ + +#define CAS_MIF_FRAME_READ 0x60020000 +#define CAS_MIF_FRAME_WRITE 0x50020000 + +#define CAS_MIF_REG_SHIFT 18 +#define CAS_MIF_PHY_SHIFT 23 + +/* CAS_MIF_CONFIG register bits */ +#define CAS_MIF_CONFIG_PHY_SEL 0x00000001 /* PHY select, 0=MDIO0 */ +#define CAS_MIF_CONFIG_POLL_ENA 0x00000002 /* poll enable */ +#define CAS_MIF_CONFIG_BB_ENA 0x00000004 /* bit bang enable */ +#define CAS_MIF_CONFIG_REG_ADR 0x000000f8 /* poll register address */ +#define CAS_MIF_CONFIG_MDI0 0x00000100 /* MDIO_0 Data/MDIO_0 atached */ +#define CAS_MIF_CONFIG_MDI1 0x00000200 /* MDIO_1 Data/MDIO_1 atached */ +#define CAS_MIF_CONFIG_PHY_ADR 0x00007c00 /* poll PHY address */ +/* MDI0 is onboard transceiver MID1 is external, PHYAD for both is 0 */ + +/* CAS_MIF_BASIC_STATUS and CAS_MIF_INTERRUPT_MASK bits */ +#define CAS_MIF_STATUS 0x0000ffff +#define CAS_MIF_BASIC 0xffff0000 +/* + * The Basic part is the last value read in the POLL field of the config + * register. + * + * The status part indicates the bits that have changed. + */ + +/* Cassini PCS/Serial link registers */ +#define CAS_MII_CONTROL 0x9000 +#define CAS_MII_STATUS 0x9004 +#define CAS_MII_ANAR 0x9008 /* MII advertisement reg */ +#define CAS_MII_ANLPAR 0x900c /* Link Partner Ability Reg */ +#define CAS_MII_CONFIG 0x9010 +#define CAS_MII_STATE_MACHINE 0x9014 +#define CAS_MII_INTERRUP_STATUS 0x9018 /* PCS interrupt state */ +#define CAS_MII_DATAPATH_MODE 0x9050 +#define CAS_MII_SLINK_CONTROL 0x9054 /* Serial link control */ +#define CAS_MII_OUTPUT_SELECT 0x9058 +#define CAS_MII_SLINK_STATUS 0x905c /* serial link status */ +#define CAS_MII_PACKET_COUNT 0x9060 + +/* CAS_MII_CONTROL bits */ +#define CAS_MII_CONTROL_RESET 0x00008000 +#define CAS_MII_CONTROL_LOOPBK 0x00004000 /* 10-bit i/f loopback */ +#define CAS_MII_CONTROL_1000M 0x00002000 /* speed select, always 0 */ +#define CAS_MII_CONTROL_AUTONEG 0x00001000 /* auto negotiation enabled */ +#define CAS_MII_CONTROL_POWERDN 0x00000800 +#define CAS_MII_CONTROL_ISOLATE 0x00000400 /* isolate phy from mii */ +#define CAS_MII_CONTROL_RAN 0x00000200 /* restart auto negotiation */ +#define CAS_MII_CONTROL_FDUPLEX 0x00000100 /* full duplex, always 0 */ +#define CAS_MII_CONTROL_COL_TST 0x00000080 /* collision test */ + +/* CAS_MII_STATUS reg - PCS "BMSR" (Basic Mode Status Reg) */ +#define CAS_MII_STATUS_GB_FDX 0x00000400 /* can perform GBit FDX */ +#define CAS_MII_STATUS_GB_HDX 0x00000200 /* can perform GBit HDX */ +#define CAS_MII_STATUS_UNK 0x00000100 +#define CAS_MII_STATUS_ANEG_CPT 0x00000020 /* auto negotiate compete */ +#define CAS_MII_STATUS_REM_FLT 0x00000010 /* remote fault detected */ +#define CAS_MII_STATUS_ACFG 0x00000008 /* can auto negotiate */ +#define CAS_MII_STATUS_LINK_STS 0x00000004 /* link status */ +#define CAS_MII_STATUS_JABBER 0x00000002 /* jabber condition detected */ +#define CAS_MII_STATUS_EXTCAP 0x00000001 /* extended register capability */ + +/* CAS_MII_ANAR and CAS_MII_ANLPAR reg bits */ +#define CAS_MII_ANEG_NP 0x00008000 /* next page bit */ +#define CAS_MII_ANEG_ACK 0x00004000 /* ack reception of */ + /* Link Partner Capability */ +#define CAS_MII_ANEG_RF 0x00003000 /* advertise remote fault cap */ +#define CAS_MII_ANEG_ASYM_PAUSE 0x00000100 /* asymmetric pause */ +#define CAS_MII_ANEG_SYM_PAUSE 0x00000080 /* symmetric pause */ +#define CAS_MII_ANEG_HLF_DUPLX 0x00000040 +#define CAS_MII_ANEG_FUL_DUPLX 0x00000020 + +/* CAS_MII_CONFIG reg */ +#define CAS_MII_CONFIG_TIMER 0x0000000e /* link monitor timer values */ +#define CAS_MII_CONFIG_ANTO 0x00000020 /* 10ms ANEG timer override */ +#define CAS_MII_CONFIG_JS 0x00000018 /* Jitter Study, 0 normal + * 1 high freq, 2 low freq */ +#define CAS_MII_CONFIG_SDL 0x00000004 /* Signal Detect active low */ +#define CAS_MII_CONFIG_SDO 0x00000002 /* Signal Detect Override */ +#define CAS_MII_CONFIG_ENABLE 0x00000001 /* Enable PCS */ + +/* + * CAS_MII_STATE_MACHINE + * XXX These are best guesses from observed behavior. + */ +#define CAS_MII_FSM_STOP 0x00000000 /* stopped */ +#define CAS_MII_FSM_RUN 0x00000001 /* running */ +#define CAS_MII_FSM_UNKWN 0x00000100 /* unknown */ +#define CAS_MII_FSM_DONE 0x00000101 /* complete */ + +/* + * CAS_MII_INTERRUP_STATUS reg + * No mask register; mask with the global interrupt mask register. + */ +#define CAS_MII_INTERRUP_LINK 0x00000002 /* PCS link status change */ + +/* CAS_MII_DATAPATH_MODE reg */ +#define CAS_MII_DATAPATH_SERIAL 0x00000001 /* Serial link */ +#define CAS_MII_DATAPATH_SERDES 0x00000002 /* Use PCS via 10bit interfac */ +#define CAS_MII_DATAPATH_MII 0x00000004 /* Use {G}MII, not PCS */ +#define CAS_MII_DATAPATH_MIIOUT 0x00000008 /* enable serial output on GMII */ + +/* CAS_MII_SLINK_CONTROL reg */ +#define CAS_MII_SLINK_LOOPBACK 0x00000001 /* enable loopback at sl, logic + * reversed for SERDES */ +#define CAS_MII_SLINK_EN_SYNC_D 0x00000002 /* enable sync detection */ +#define CAS_MII_SLINK_LOCK_REF 0x00000004 /* lock reference clock */ +#define CAS_MII_SLINK_EMPHASIS 0x00000008 /* enable emphasis */ +#define CAS_MII_SLINK_SELFTEST 0x000001c0 +#define CAS_MII_SLINK_POWER_OFF 0x00000200 /* Power down serial link */ + +/* CAS_MII_SLINK_STATUS reg */ +#define CAS_MII_SLINK_TEST 0x00000000 /* undergoing test */ +#define CAS_MII_SLINK_LOCKED 0x00000001 /* waiting 500us lockrefn */ +#define CAS_MII_SLINK_COMMA 0x00000002 /* waiting for comma detect */ +#define CAS_MII_SLINK_SYNC 0x00000003 /* recv data synchronized */ + +/* Wired PHY addresses */ +#define CAS_PHYAD_INTERNAL 1 +#define CAS_PHYAD_EXTERNAL 0 + +/* + * Cassini ring structures. + */ + +/* Descriptor rings */ +struct cas_desc { + uint64_t cd_flags; + uint64_t cd_addr; +}; + +/* Transmit flags */ +#define CAS_TD_BUFSIZE 0x0000000000007fffLL +#define CAS_TD_CXSUM_START 0x00000000001f8000LL /* Cxsum start offset */ +#define CAS_TD_CXSUM_STARTSHFT 15 +#define CAS_TD_CXSUM_STUFF 0x000000001fe00000LL /* Cxsum stuff offset */ +#define CAS_TD_CXSUM_STUFFSHFT 21 +#define CAS_TD_CXSUM_ENABLE 0x0000000020000000LL /* Cxsum generation enable */ +#define CAS_TD_END_OF_PACKET 0x0000000040000000LL +#define CAS_TD_START_OF_PACKET 0x0000000080000000LL +#define CAS_TD_INTERRUPT_ME 0x0000000100000000LL /* Interrupt me now */ +#define CAS_TD_NO_CRC 0x0000000200000000LL /* do not insert crc */ +/* + * Only need to set CAS_TD_CXSUM_ENABLE, CAS_TD_CXSUM_STUFF, + * CAS_TD_CXSUM_START, and CAS_TD_INTERRUPT_ME in 1st descriptor of a group. + */ + +/* Completion ring */ +struct cas_comp { + u_int64_t cc_word[4]; +}; + +#define CAS_RC0_TYPE 0xc000000000000000ULL +#define CAS_RC0_RELEASE_HDR 0x2000000000000000ULL +#define CAS_RC0_RELEASE_DATA 0x1000000000000000ULL +#define CAS_RC0_SPLIT 0x0400000000000000ULL +#define CAS_RC0_SKIP_MASK 0x0180000000000000ULL +#define CAS_RC0_SKIP_SHIFT 55 +#define CAS_RC0_DATA_IDX_MASK 0x007ffe0000000000ULL +#define CAS_RC0_DATA_IDX_SHIFT 41 +#define CAS_RC0_DATA_OFF_MASK 0x000001fff8000000ULL +#define CAS_RC0_DATA_OFF_SHIFT 27 +#define CAS_RC0_DATA_LEN_MASK 0x0000000007ffe000ULL +#define CAS_RC0_DATA_LEN_SHIFT 13 + +#define CAS_RC0_SKIP(w) \ + (((w) & CAS_RC0_SKIP_MASK) >> CAS_RC0_SKIP_SHIFT) +#define CAS_RC0_DATA_IDX(w) \ + (((w) & CAS_RC0_DATA_IDX_MASK) >> CAS_RC0_DATA_IDX_SHIFT) +#define CAS_RC0_DATA_OFF(w) \ + (((w) & CAS_RC0_DATA_OFF_MASK) >> CAS_RC0_DATA_OFF_SHIFT) +#define CAS_RC0_DATA_LEN(w) \ + (((w) & CAS_RC0_DATA_LEN_MASK) >> CAS_RC0_DATA_LEN_SHIFT) + +#define CAS_RC1_HDR_IDX_MASK 0xfffc000000000000ULL +#define CAS_RC1_HDR_IDX_SHIFT 50 +#define CAS_RC1_HDR_OFF_MASK 0x0003f00000000000ULL +#define CAS_RC1_HDR_OFF_SHIFT 44 +#define CAS_RC1_HDR_LEN_MASK 0x00000ff800000000ULL +#define CAS_RC1_HDR_LEN_SHIFT 35 + +#define CAS_RC1_HDR_IDX(w) \ + (((w) & CAS_RC1_HDR_IDX_MASK) >> CAS_RC1_HDR_IDX_SHIFT) +#define CAS_RC1_HDR_OFF(w) \ + (((w) & CAS_RC1_HDR_OFF_MASK) >> CAS_RC1_HDR_OFF_SHIFT) +#define CAS_RC1_HDR_LEN(w) \ + (((w) & CAS_RC1_HDR_LEN_MASK) >> CAS_RC1_HDR_LEN_SHIFT) + +#define CAS_RC3_OWN 0x0000080000000000ULL /* Owned by hardware */ + +#endif /* _IF_CASREG_H */ diff --git a/sys/dev/pci/if_casvar.h b/sys/dev/pci/if_casvar.h new file mode 100644 index 00000000000..804c4cca767 --- /dev/null +++ b/sys/dev/pci/if_casvar.h @@ -0,0 +1,261 @@ +/* $NetBSD: if_casvar.h,v 1.1 2010/01/07 09:19:55 jdc Exp $ */ +/* $OpenBSD: if_casvar.h,v 1.6 2009/06/13 12:18:58 kettenis Exp $ */ + +/* + * + * Copyright (C) 2007 Mark Kettenis. + * Copyright (C) 2001 Eduardo Horvath. + * 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. + * + * 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. + * + */ + +#ifndef _IF_CASVAR_H +#define _IF_CASVAR_H + +#include <sys/queue.h> +#include <sys/callout.h> + +/* + * Misc. definitions for Sun Cassini ethernet controllers. + */ + +/* + * Preferred page size. Cassini has a configurable page size, but + * needs at least 8k to handle jumbo frames. This happens to be the + * default anyway. + */ +#define CAS_PAGE_SIZE 8192 + +/* + * Transmit descriptor ring size. This is arbitrary, but allocate + * enough descriptors for 64 pending transmissions and 16 segments + * per packet. + */ +#define CAS_NTXSEGS 16 + +#define CAS_TXQUEUELEN 64 +#define CAS_NTXDESC (CAS_TXQUEUELEN * CAS_NTXSEGS) +#define CAS_NTXDESC_MASK (CAS_NTXDESC - 1) +#define CAS_NEXTTX(x) ((x + 1) & CAS_NTXDESC_MASK) + +struct cas_sxd { + struct mbuf *sd_mbuf; + bus_dmamap_t sd_map; +}; + +/* + * Receive descriptor ring size. We have one Rx buffer per incoming + * packet, so this logic is a little simpler. + */ +#define CAS_NRXDESC 128 +#define CAS_NRXDESC_MASK (CAS_NRXDESC - 1) + +/* + * Receive completion ring size. + */ +#define CAS_NRXCOMP 256 +#define CAS_NRXCOMP_MASK (CAS_NRXCOMP - 1) +#define CAS_NEXTRX(x) ((x + 1) & CAS_NRXCOMP_MASK) + +/* + * Control structures are DMA'd to the Cassini chip. We allocate them in + * a single clump that maps to a single DMA segment to make several things + * easier. + */ +struct cas_control_data { + /* + * The transmit descriptors. + */ + struct cas_desc ccd_txdescs[CAS_NTXDESC]; + + /* + * The receive completions. + */ + struct cas_comp ccd_rxcomps[CAS_NRXCOMP]; + + /* + * The receive descriptors. + */ + struct cas_desc ccd_rxdescs[CAS_NRXDESC]; + char ccd_unused[CAS_PAGE_SIZE - CAS_NRXDESC * 16]; + struct cas_desc ccd_rxdescs2[CAS_NRXDESC]; +}; + +#define CAS_CDOFF(x) offsetof(struct cas_control_data, x) +#define CAS_CDTXOFF(x) CAS_CDOFF(ccd_txdescs[(x)]) +#define CAS_CDRXOFF(x) CAS_CDOFF(ccd_rxdescs[(x)]) +#define CAS_CDRXOFF2(x) CAS_CDOFF(ccd_rxdescs2[(x)]) +#define CAS_CDRXCOFF(x) CAS_CDOFF(ccd_rxcomps[(x)]) + +/* + * Software state for receive jobs. + */ +struct cas_rxsoft { + bus_dmamap_t rxs_dmamap; /* our DMA map */ + bus_dma_segment_t rxs_dmaseg; /* our DMA segment */ + char *rxs_kva; +}; + +enum cas_attach_stage { + CAS_ATT_BACKEND_2 = 0 + , CAS_ATT_BACKEND_1 + , CAS_ATT_FINISHED + , CAS_ATT_MII + , CAS_ATT_7 + , CAS_ATT_6 + , CAS_ATT_5 + , CAS_ATT_4 + , CAS_ATT_3 + , CAS_ATT_2 + , CAS_ATT_1 + , CAS_ATT_0 + , CAS_ATT_BACKEND_0 +}; + +/* + * Software state per device. + */ +struct cas_softc { + device_t sc_dev; /* generic device information */ + struct ethercom sc_ethercom; /* ethernet common data */ + struct mii_data sc_mii; /* MII media control */ +#define sc_media sc_mii.mii_media/* shorthand */ + struct callout sc_tick_ch; /* tick callout */ + + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + bus_size_t sc_size; + + void *sc_ih; + pci_chipset_tag_t sc_pc; + pci_intr_handle_t sc_handle; + + bus_dma_tag_t sc_dmatag; /* bus dma tag */ + bus_dmamap_t sc_dmamap; /* bus dma handle */ + int sc_burst; /* DVMA burst size in effect */ + int sc_phys[2]; /* MII instance -> PHY map */ + + int sc_mif_config; /* Selected MII reg setting */ + + /* + * Ring buffer DMA stuff. + */ + bus_dma_segment_t sc_cdseg; /* control data memory */ + int sc_cdnseg; /* number of segments */ + bus_dmamap_t sc_cddmamap; /* control data DMA map */ +#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr + + /* + * Software state for transmit and receive descriptors. + */ + struct cas_sxd sc_txd[CAS_NTXDESC]; + u_int32_t sc_tx_cnt, sc_tx_prod, sc_tx_cons; + + struct cas_rxsoft sc_rxsoft[CAS_NRXDESC]; + struct cas_rxsoft sc_rxsoft2[CAS_NRXDESC]; + + /* + * Control data structures. + */ + struct cas_control_data *sc_control_data; +#define sc_txdescs sc_control_data->ccd_txdescs +#define sc_rxdescs sc_control_data->ccd_rxdescs +#define sc_rxdescs2 sc_control_data->ccd_rxdescs2 +#define sc_rxcomps sc_control_data->ccd_rxcomps + + int sc_rxptr; /* next ready RX descriptor/descsoft */ + int sc_rxfifosize; + int sc_rxdptr; + + int sc_rev; + int sc_inited; + int sc_debug; + void *sc_sh; /* shutdownhook cookie */ + +#if NRND > 0 + rndsource_element_t rnd_source; +#endif + + struct evcnt sc_ev_intr; + enum cas_attach_stage sc_att_stage; +}; + +/* + * This maccro determines whether we have a Cassini+. + */ +#define CAS_PLUS(sc) (sc->sc_rev > 0x10) + +#define CAS_DMA_READ(v) le64toh(v) +#define CAS_DMA_WRITE(v) htole64(v) + +#define CAS_CDTXADDR(sc, x) ((sc)->sc_cddma + CAS_CDTXOFF((x))) +#define CAS_CDRXADDR(sc, x) ((sc)->sc_cddma + CAS_CDRXOFF((x))) +#define CAS_CDRXADDR2(sc, x) ((sc)->sc_cddma + CAS_CDRXOFF2((x))) +#define CAS_CDRXCADDR(sc, x) ((sc)->sc_cddma + CAS_CDRXCOFF((x))) + +#define CAS_CDTXSYNC(sc, x, n, ops) \ +do { \ + int __x, __n; \ + \ + __x = (x); \ + __n = (n); \ + \ + /* If it will wrap around, sync to the end of the ring. */ \ + if ((__x + __n) > CAS_NTXDESC) { \ + bus_dmamap_sync((sc)->sc_dmatag, (sc)->sc_cddmamap, \ + CAS_CDTXOFF(__x), sizeof(struct cas_desc) * \ + (CAS_NTXDESC - __x), (ops)); \ + __n -= (CAS_NTXDESC - __x); \ + __x = 0; \ + } \ + \ + /* Now sync whatever is left. */ \ + bus_dmamap_sync((sc)->sc_dmatag, (sc)->sc_cddmamap, \ + CAS_CDTXOFF(__x), sizeof(struct cas_desc) * __n, (ops)); \ +} while (0) + +#define CAS_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmatag, (sc)->sc_cddmamap, \ + CAS_CDRXOFF((x)), sizeof(struct cas_desc), (ops)) + +#define CAS_CDRXCSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmatag, (sc)->sc_cddmamap, \ + CAS_CDRXCOFF((x)), sizeof(struct cas_desc), (ops)) + +#define CAS_INIT_RXDESC(sc, d, s) \ +do { \ + struct cas_rxsoft *__rxs = &sc->sc_rxsoft[(s)]; \ + struct cas_desc *__rxd = &sc->sc_rxdescs[(d)]; \ + \ + __rxd->cd_addr = \ + CAS_DMA_WRITE(__rxs->rxs_dmamap->dm_segs[0].ds_addr); \ + __rxd->cd_flags = \ + CAS_DMA_WRITE((s)); \ + CAS_CDRXSYNC((sc), (d), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); \ +} while (0) + +#define ETHER_ALIGN 2 + +#endif |
