summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2007-05-31 18:23:43 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2007-05-31 18:23:43 +0000
commit14c12ff7be8f282899cf90685d09cc912f019e5f (patch)
treeda27b0dd1edd67b55d60e4aead81e436955e88f7 /sys/dev
parentc306a86b5b613b1a88fb2021a1afbd1e893bdd79 (diff)
initial bits of a new driver for the Myricom Myri-10G Lanai-Z8E 10Gb
Ethernet chipset. not working yet. ok dlg@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/if_myx.c894
-rw-r--r--sys/dev/pci/if_myxreg.h206
2 files changed, 1100 insertions, 0 deletions
diff --git a/sys/dev/pci/if_myx.c b/sys/dev/pci/if_myx.c
new file mode 100644
index 00000000000..a0713c8fa4d
--- /dev/null
+++ b/sys/dev/pci/if_myx.c
@@ -0,0 +1,894 @@
+/* $OpenBSD: if_myx.c,v 1.1 2007/05/31 18:23:42 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for the Myricom Myri-10G Lanai-Z8E Ethernet chipsets.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/if_myxreg.h>
+
+#ifdef MYX_DEBUG
+#define MYXDBG_INIT (1<<0) /* chipset initialization */
+#define MYXDBG_CMD (2<<0) /* commands */
+#define MYXDBG_INTR (2<<0) /* interrupts */
+#define MYXDBG_ALL 0xffff /* enable all debugging messages */
+int myx_debug = 0;
+#define DPRINTF(_lvl, _arg...) do { \
+ if (myx_debug & (_lvl)) \
+ printf(_arg); \
+} while (0)
+#else
+#define DPRINTF(_lvl, arg...)
+#endif
+
+#define DEVNAME(_s) ((_s)->_s##_dev.dv_xname)
+
+struct myx_dmamem {
+ bus_dmamap_t mxm_map;
+ bus_dma_segment_t mxm_seg;
+ int mxm_nsegs;
+ size_t mxm_size;
+ caddr_t mxm_kva;
+ const char *mxm_name;
+};
+
+struct myx_softc {
+ struct device sc_dev;
+ struct arpcom sc_ac;
+
+ pci_chipset_tag_t sc_pc;
+ pcitag_t sc_tag;
+ u_int sc_function;
+
+ bus_dma_tag_t sc_dmat;
+ bus_space_tag_t sc_memt;
+ bus_space_handle_t sc_memh;
+ bus_size_t sc_mems;
+
+ struct myx_dmamem sc_cmddma;
+ struct myx_dmamem sc_paddma;
+
+ struct myx_dmamem sc_stsdma;
+ struct myx_status *sc_sts;
+
+ struct myx_dmamem sc_rxdma;
+ struct myx_rxdesc *sc_rxdesc;
+ int sc_rxactive;
+ int sc_rxidx;
+
+ void *sc_irqh;
+ u_int32_t sc_irqcoaloff;
+ u_int32_t sc_irqclaimoff;
+ u_int32_t sc_irqdeassertoff;
+
+ u_int8_t sc_lladdr[ETHER_ADDR_LEN];
+ struct ifmedia sc_media;
+
+ int sc_txringsize;
+ int sc_rxringsize;
+ int sc_txndesc;
+ int sc_rxndesc;
+ size_t sc_rxdescsize;
+
+ u_int sc_phy; /* PHY type (CX4/SR/LR) */
+ u_int sc_hwflags;
+#define MYXFLAG_FLOW_CONTROL (1<<0) /* Rx/Tx pause is enabled */
+#define MYXFLAG_PROMISC (1<<1) /* promisc mode is enabled */
+#define MYXFLAG_ALLMULTI (1<<2) /* allmulti is set */
+ u_int8_t sc_active;
+
+ struct timeout sc_tick;
+};
+
+int myx_match(struct device *, void *, void *);
+void myx_attach(struct device *, struct device *, void *);
+int myx_query(struct myx_softc *sc);
+u_int myx_ether_aton(char *, u_int8_t *, u_int);
+int myx_loadfirmware(struct myx_softc *, u_int8_t *, size_t,
+ u_int32_t, int);
+void myx_attachhook(void *);
+void myx_read(struct myx_softc *, bus_size_t, u_int8_t *, bus_size_t);
+void myx_write(struct myx_softc *, bus_size_t, u_int8_t *, bus_size_t);
+int myx_cmd(struct myx_softc *, u_int32_t, struct myx_cmd *, u_int32_t *);
+int myx_boot(struct myx_softc *, u_int32_t, struct myx_bootcmd *);
+int myx_rdma(struct myx_softc *, u_int);
+int myx_reset(struct myx_softc *);
+int myx_dmamem_alloc(struct myx_softc *, struct myx_dmamem *,
+ bus_size_t, u_int align, const char *);
+void myx_dmamem_free(struct myx_softc *, struct myx_dmamem *);
+int myx_media_change(struct ifnet *);
+void myx_media_status(struct ifnet *, struct ifmediareq *);
+void myx_link_state(struct myx_softc *);
+void myx_watchdog(struct ifnet *);
+void myx_tick(void *);
+int myx_ioctl(struct ifnet *, u_long, caddr_t);
+void myx_iff(struct myx_softc *);
+void myx_init(struct ifnet *);
+void myx_start(struct ifnet *);
+void myx_stop(struct ifnet *);
+int myx_setlladdr(struct myx_softc *, u_int8_t *);
+int myx_intr(void *);
+
+struct cfdriver myx_cd = {
+ 0, "myx", DV_IFNET
+};
+struct cfattach myx_ca = {
+ sizeof(struct myx_softc), myx_match, myx_attach
+};
+
+const struct pci_matchid myx_devices[] = {
+ { PCI_VENDOR_MYRICOM, PCI_PRODUCT_MYRICOM_Z8E }
+};
+
+int
+myx_match(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid((struct pci_attach_args *)aux,
+ myx_devices, sizeof(myx_devices) / sizeof(myx_devices[0])));
+}
+
+void
+myx_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct myx_softc *sc = (struct myx_softc *)self;
+ struct pci_attach_args *pa = aux;
+ pci_intr_handle_t ih;
+ pcireg_t memtype;
+ const char *intrstr;
+ struct ifnet *ifp;
+
+ sc->sc_pc = pa->pa_pc;
+ sc->sc_tag = pa->pa_tag;
+ sc->sc_dmat = pa->pa_dmat;
+ sc->sc_function = pa->pa_function;
+
+ memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, MYXBAR0);
+ switch (memtype) {
+ case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
+ case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
+ break;
+ default:
+ printf(": invalid memory type: 0x%x\n", memtype);
+ return;
+ }
+
+ /* Map the PCI memory space */
+ if (pci_mapreg_map(pa, MYXBAR0, memtype, 0, &sc->sc_memt,
+ &sc->sc_memh, NULL, &sc->sc_mems, 0) != 0) {
+ printf(": unable to map register memory\n");
+ return;
+ }
+
+ /* Get the board information and initialize the h/w */
+ if (myx_query(sc) != 0)
+ goto unmap;
+
+ /*
+ * Allocate command DMA memory
+ */
+ if (myx_dmamem_alloc(sc, &sc->sc_cmddma,
+ sizeof(struct myx_cmd), MYXALIGN_CMD, "cmd") != 0) {
+ printf(": failed to allocate command DMA memory\n");
+ goto unmap;
+ }
+
+ if (myx_dmamem_alloc(sc, &sc->sc_paddma,
+ MYXALIGN_CMD, MYXALIGN_CMD, "pad") != 0) {
+ printf(": failed to allocate pad DMA memory\n");
+ goto err3;
+ }
+
+ if (myx_dmamem_alloc(sc, &sc->sc_stsdma,
+ sizeof(struct myx_status), MYXALIGN_CMD, "status") != 0) {
+ printf(": failed to allocate status DMA memory\n");
+ goto err2;
+ }
+ sc->sc_sts = (struct myx_status *)sc->sc_stsdma.mxm_kva;
+
+ sc->sc_rxdescsize = MYX_NRXDESC * sizeof(struct myx_rxdesc);
+ if (myx_dmamem_alloc(sc, &sc->sc_rxdma,
+ sc->sc_rxdescsize, MYXALIGN_DATA, "rxdesc") != 0) {
+ printf(": failed to allocate Rx descriptor DMA memory\n");
+ goto err1;
+ }
+ sc->sc_rxdesc = (struct myx_rxdesc *)sc->sc_rxdma.mxm_kva;
+
+ /*
+ * Map and establish the interrupt
+ */
+ if (pci_intr_map(pa, &ih) != 0) {
+ printf(": unable to map interrupt\n");
+ goto err;
+ }
+ intrstr = pci_intr_string(pa->pa_pc, ih);
+ sc->sc_irqh = pci_intr_establish(pa->pa_pc, ih, IPL_NET,
+ myx_intr, sc, DEVNAME(sc));
+ if (sc->sc_irqh == NULL) {
+ printf(": unable to establish interrupt %s\n", intrstr);
+ goto err;
+ }
+ printf(": %s, address %s\n", intrstr,
+ ether_sprintf(sc->sc_ac.ac_enaddr));
+
+ ifp = &sc->sc_ac.ac_if;
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = myx_ioctl;
+ ifp->if_start = myx_start;
+ ifp->if_watchdog = myx_watchdog;
+ strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
+ IFQ_SET_MAXLEN(&ifp->if_snd, sc->sc_txndesc - 1);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+#if 0
+ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
+ ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 |
+ IFCAP_CSUM_UDPv4;
+#endif
+ ifp->if_baudrate = ULONG_MAX; /* XXX fix if_baudrate */
+
+ ifmedia_init(&sc->sc_media, 0,
+ myx_media_change, myx_media_status);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|sc->sc_phy, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER|sc->sc_phy);
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ timeout_set(&sc->sc_tick, myx_tick, sc);
+ timeout_add(&sc->sc_tick, hz);
+
+ mountroothook_establish(myx_attachhook, sc);
+
+ return;
+
+ err:
+ myx_dmamem_free(sc, &sc->sc_rxdma);
+ err1:
+ myx_dmamem_free(sc, &sc->sc_stsdma);
+ err2:
+ myx_dmamem_free(sc, &sc->sc_paddma);
+ err3:
+ myx_dmamem_free(sc, &sc->sc_cmddma);
+ unmap:
+ bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
+ sc->sc_mems = 0;
+}
+
+u_int
+myx_ether_aton(char *mac, u_int8_t *lladdr, u_int maxlen)
+{
+ u_int i, j;
+ u_int8_t digit;
+
+ bzero(lladdr, ETHER_ADDR_LEN);
+ for (i = j = 0; mac[i] != '\0' && i < maxlen; i++) {
+ if (mac[i] >= '0' && mac[i] <= '9')
+ digit = mac[i] - '0';
+ else if (mac[i] >= 'A' && mac[i] <= 'F')
+ digit = mac[i] - 'A' + 10;
+ else if (mac[i] >= 'a' && mac[i] <= 'f')
+ digit = mac[i] - 'a' + 10;
+ else
+ continue;
+ if ((j & 1) == 0)
+ digit <<= 4;
+ lladdr[j++/2] |= digit;
+ }
+
+ return (i);
+}
+
+int
+myx_query(struct myx_softc *sc)
+{
+ u_int8_t eeprom[MYX_EEPROM_SIZE];
+ u_int i, maxlen;
+
+ myx_read(sc, MYX_EEPROM, eeprom, MYX_EEPROM_SIZE);
+
+ for (i = 0; i < MYX_EEPROM_SIZE; i++) {
+ maxlen = MYX_EEPROM_SIZE - i;
+ if (eeprom[i] == '\0')
+ break;
+ if (maxlen > 4 && bcmp("MAC=", &eeprom[i], 4) == 0) {
+ i += 4;
+ i += myx_ether_aton(&eeprom[i],
+ sc->sc_ac.ac_enaddr, maxlen);
+ }
+ for (; i < MYX_EEPROM_SIZE; i++)
+ if (eeprom[i] == '\0')
+ break;
+ }
+
+ return (0);
+}
+
+int
+myx_loadfirmware(struct myx_softc *sc, u_int8_t *fw, size_t fwlen,
+ u_int32_t fwhdroff, int reload)
+{
+ struct myx_firmware_hdr *fwhdr;
+ u_int i, len, ret = 0;
+
+ fwhdr = (struct myx_firmware_hdr *)(fw + fwhdroff);
+ DPRINTF(MYXDBG_INIT, "%s(%s): "
+ "fw hdr off %d, length %d, type 0x%x, version %s\n",
+ DEVNAME(sc), __func__,
+ fwhdroff, betoh32(fwhdr->fw_hdrlength),
+ betoh32(fwhdr->fw_type),
+ fwhdr->fw_version);
+
+ if (betoh32(fwhdr->fw_type) != MYXFW_TYPE_ETH ||
+ bcmp(MYXFW_VER, fwhdr->fw_version, strlen(MYXFW_VER)) != 0) {
+ if (reload)
+ printf("%s: invalid firmware type 0x%x version %s\n",
+ DEVNAME(sc), betoh32(fwhdr->fw_type),
+ fwhdr->fw_version);
+ ret = 1;
+ goto done;
+ }
+
+ if (!reload)
+ goto done;
+
+ /* Write the firmware to the card's SRAM */
+ for (i = 0; i < fwlen; i += 256) {
+ len = min(256, fwlen - i);
+ myx_write(sc, i + MYX_FW, fw + i, min(256, fwlen - i));
+ }
+
+ done:
+ free(fw, M_DEVBUF);
+ return (ret);
+}
+
+void
+myx_attachhook(void *arg)
+{
+ struct myx_softc *sc = (struct myx_softc *)arg;
+ size_t fwlen;
+ u_int8_t *fw = NULL;
+ u_int32_t fwhdroff;
+ struct myx_bootcmd bc;
+
+ /*
+ * First try the firmware found in the SRAM
+ */
+ myx_read(sc, MYX_HEADER_POS,
+ (u_int8_t *)&fwhdroff, sizeof(fwhdroff));
+ fwhdroff = betoh32(fwhdroff);
+ fwlen = sizeof(struct myx_firmware_hdr);
+ if ((fwhdroff + fwlen) > MYX_SRAM_SIZE)
+ goto load;
+
+ fw = malloc(fwlen, M_DEVBUF, M_WAIT);
+ myx_read(sc, MYX_HEADER_POS, fw, fwlen);
+
+ if (myx_loadfirmware(sc, fw, fwlen, fwhdroff, 0) == 0)
+ goto boot;
+
+ load:
+ /*
+ * Now try the firmware stored on disk
+ */
+ if (loadfirmware(MYXFW_UNALIGNED, &fw, &fwlen) != 0) {
+ printf("%s: could not load firmware\n", DEVNAME(sc));
+ return;
+ }
+ if (fwlen > MYX_SRAM_SIZE || fwlen < MYXFW_MIN_LEN) {
+ printf("%s: invalid firmware image size\n", DEVNAME(sc));
+ goto err;
+ }
+
+ bcopy(fw + MYX_HEADER_POS, &fwhdroff, sizeof(fwhdroff));
+ fwhdroff = betoh32(fwhdroff);
+ if ((fwhdroff + sizeof(struct myx_firmware_hdr)) > fwlen) {
+ printf("%s: invalid firmware image\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (myx_loadfirmware(sc, fw, fwlen, fwhdroff, 1) != 0) {
+ fw = NULL;
+ goto err;
+ }
+ fw = NULL;
+
+ boot:
+ bzero(&bc, sizeof(bc));
+ if (myx_boot(sc, fwlen, &bc) != 0) {
+ printf("%s: failed to bootstrap the device\n", DEVNAME(sc));
+ goto err;
+ }
+ if (myx_reset(sc) != 0)
+ goto err;
+
+ sc->sc_active = 1;
+ return;
+
+ err:
+ if (fw != NULL)
+ free(fw, M_DEVBUF);
+}
+
+void
+myx_read(struct myx_softc *sc, bus_size_t off, u_int8_t *ptr, bus_size_t len)
+{
+ bus_space_barrier(sc->sc_memt, sc->sc_memh, off, len,
+ BUS_SPACE_BARRIER_READ);
+ bus_space_read_region_1(sc->sc_memt, sc->sc_memh, off, ptr, len);
+}
+
+void
+myx_write(struct myx_softc *sc, bus_size_t off, u_int8_t *ptr, bus_size_t len)
+{
+ bus_space_write_region_4(sc->sc_memt, sc->sc_memh, off, ptr, len);
+ bus_space_barrier(sc->sc_memt, sc->sc_memh, off, len,
+ BUS_SPACE_BARRIER_WRITE);
+}
+
+int
+myx_dmamem_alloc(struct myx_softc *sc, struct myx_dmamem *mxm,
+ bus_size_t size, u_int align, const char *mname)
+{
+ mxm->mxm_size = size;
+
+ if (bus_dmamap_create(sc->sc_dmat, mxm->mxm_size, 1,
+ mxm->mxm_size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+ &mxm->mxm_map) != 0)
+ return (1);
+ if (bus_dmamem_alloc(sc->sc_dmat, mxm->mxm_size,
+ align, 0, &mxm->mxm_seg, 1, &mxm->mxm_nsegs,
+ BUS_DMA_NOWAIT) != 0)
+ goto destroy;
+ if (bus_dmamem_map(sc->sc_dmat, &mxm->mxm_seg, mxm->mxm_nsegs,
+ mxm->mxm_size, &mxm->mxm_kva, BUS_DMA_NOWAIT) != 0)
+ goto free;
+ if (bus_dmamap_load(sc->sc_dmat, mxm->mxm_map, mxm->mxm_kva,
+ mxm->mxm_size, NULL, BUS_DMA_NOWAIT) != 0)
+ goto unmap;
+
+ bzero(mxm->mxm_kva, mxm->mxm_size);
+ mxm->mxm_name = mname;
+
+ return (0);
+ unmap:
+ bus_dmamem_unmap(sc->sc_dmat, mxm->mxm_kva, mxm->mxm_size);
+ free:
+ bus_dmamem_free(sc->sc_dmat, &mxm->mxm_seg, 1);
+ destroy:
+ bus_dmamap_destroy(sc->sc_dmat, mxm->mxm_map);
+ return (1);
+}
+
+void
+myx_dmamem_free(struct myx_softc *sc, struct myx_dmamem *mxm)
+{
+ bus_dmamap_unload(sc->sc_dmat, mxm->mxm_map);
+ bus_dmamem_unmap(sc->sc_dmat, mxm->mxm_kva, mxm->mxm_size);
+ bus_dmamem_free(sc->sc_dmat, &mxm->mxm_seg, 1);
+ bus_dmamap_destroy(sc->sc_dmat, mxm->mxm_map);
+}
+
+int
+myx_cmd(struct myx_softc *sc, u_int32_t cmd, struct myx_cmd *mc, u_int32_t *r)
+{
+ bus_dmamap_t map = sc->sc_cmddma.mxm_map;
+ struct myx_response *mr;
+ u_int i;
+
+ DPRINTF(MYXDBG_CMD, "%s(%s) command %d\n", DEVNAME(sc),
+ __func__, cmd);
+
+ mc->mc_cmd = htobe32(cmd);
+ mc->mc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
+ mc->mc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
+
+ mr = (struct myx_response *)sc->sc_cmddma.mxm_kva;
+ mr->mr_result = 0xffffffff;
+
+ /* Send command */
+ myx_write(sc, MYX_CMD, (u_int8_t *)mc, sizeof(struct myx_cmd));
+
+ for (i = 0; i < 20; i++) {
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD);
+ if (mr->mr_result != 0xffffffff)
+ break;
+ delay(1000);
+ }
+
+ DPRINTF(MYXDBG_CMD, "%s(%s): command %d completed, "
+ "result %x, data %x\n", DEVNAME(sc), __func__, cmd,
+ betoh32(mr->mr_result), betoh32(mr->mr_data));
+
+ if (betoh32(mr->mr_result) != 0)
+ return (-1);
+
+ if (r != NULL)
+ *r = betoh32(mr->mr_data);
+ return (0);
+}
+
+int
+myx_boot(struct myx_softc *sc, u_int32_t length, struct myx_bootcmd *bc)
+{
+ bus_dmamap_t map = sc->sc_cmddma.mxm_map;
+ u_int32_t *status;
+ u_int i;
+
+ bc->bc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
+ bc->bc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
+ bc->bc_result = 0xffffffff;
+ bc->bc_offset = htobe32(MYX_FW_BOOT);
+ bc->bc_length = htobe32(length);
+ bc->bc_copyto = htobe32(8);
+ bc->bc_jumpto = htobe32(0);
+
+ status = (u_int32_t *)sc->sc_cmddma.mxm_kva;
+ *status = 0;
+
+ /* Send command */
+ myx_write(sc, MYX_BOOT, (u_int8_t *)bc, sizeof(struct myx_bootcmd));
+
+ for (i = 0; i < 2000; i++) {
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD);
+ if (*status == 0xffffffff)
+ break;
+ delay(100);
+ }
+
+ DPRINTF(MYXDBG_CMD, "%s(%s): boot completed, result %x\n",
+ DEVNAME(sc), __func__, betoh32(*status));
+
+ if (*status != 0xffffffff)
+ return (-1);
+
+ return (0);
+}
+
+int
+myx_rdma(struct myx_softc *sc, u_int do_enable)
+{
+ struct myx_rdmacmd rc;
+ bus_dmamap_t map = sc->sc_cmddma.mxm_map;
+ bus_dmamap_t pad = sc->sc_paddma.mxm_map;
+ u_int32_t *status;
+ u_int i;
+
+ /*
+ * It is required to setup a _dummy_ RDMA address. It also makes
+ * some PCI-E chipsets resend dropped messages.
+ */
+ rc.rc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
+ rc.rc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
+ rc.rc_result = 0xffffffff;
+ rc.rc_rdma_high = MYX_ADDRHIGH(pad->dm_segs[0].ds_addr);
+ rc.rc_rdma_low = MYX_ADDRLOW(pad->dm_segs[0].ds_addr);
+ rc.rc_enable = htobe32(do_enable);
+
+ status = (u_int32_t *)sc->sc_cmddma.mxm_kva;
+ *status = 0;
+
+ /* Send command */
+ myx_write(sc, MYX_RDMA, (u_int8_t *)&rc, sizeof(struct myx_rdmacmd));
+
+ for (i = 0; i < 200; i++) {
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD);
+ if (*status == 0xffffffff)
+ break;
+ delay(1000);
+ }
+
+ DPRINTF(MYXDBG_CMD, "%s(%s): dummy RDMA %s, result %x\n",
+ DEVNAME(sc), __func__,
+ do_enable ? "enabled" : "disabled", betoh32(*status));
+
+ if (*status != 0xffffffff)
+ return (-1);
+
+ return (0);
+}
+
+int
+myx_reset(struct myx_softc *sc)
+{
+ struct myx_cmd mc;
+ u_int32_t result;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ bus_dmamap_t map;
+
+ bzero(&mc, sizeof(mc));
+ if (myx_cmd(sc, MYXCMD_RESET, &mc, NULL) != 0) {
+ printf("%s: failed to reset the device\n", DEVNAME(sc));
+ return (-1);
+ }
+
+ if (myx_rdma(sc, MYXRDMA_ON) != 0) {
+ printf("%s: failed to enable dummy RDMA\n", DEVNAME(sc));
+ return (-1);
+ }
+
+ bzero(&mc, sizeof(mc));
+ mc.mc_data0 = htobe32(sc->sc_rxdescsize);
+ if (myx_cmd(sc, MYXCMD_SET_INTRQSZ, &mc, NULL) != 0) {
+ printf("%s: failed to set Rx DMA size\n", DEVNAME(sc));
+ return (-1);
+ }
+
+ bzero(&mc, sizeof(mc));
+ map = sc->sc_rxdma.mxm_map;
+ mc.mc_data0 = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
+ mc.mc_data1 = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
+ if (myx_cmd(sc, MYXCMD_SET_INTRQSZ, &mc, NULL) != 0) {
+ printf("%s: failed to set Rx DMA address\n", DEVNAME(sc));
+ return (-1);
+ }
+
+ bzero(&mc, sizeof(mc));
+ if (myx_cmd(sc, MYXCMD_GET_INTRCOALDELAYOFF, &mc, &result) != 0) {
+ printf("%s: failed to get IRQ coal offset\n", DEVNAME(sc));
+ return (-1);
+ }
+ sc->sc_irqcoaloff = result;
+
+ bzero(&mc, sizeof(mc));
+ if (myx_cmd(sc, MYXCMD_GET_INTRACKOFF, &mc, &result) != 0) {
+ printf("%s: failed to get IRQ ack offset\n", DEVNAME(sc));
+ return (-1);
+ }
+ sc->sc_irqclaimoff = result;
+
+ bzero(&mc, sizeof(mc));
+ if (myx_cmd(sc, MYXCMD_GET_INTRDEASSERTOFF, &mc, &result) != 0) {
+ printf("%s: failed to get IRQ deassert offset\n", DEVNAME(sc));
+ return (-1);
+ }
+ sc->sc_irqdeassertoff = result;
+
+ /* XXX */
+ sc->sc_txndesc = 2;
+ sc->sc_rxndesc = 2;
+
+ if (myx_setlladdr(sc, LLADDR(ifp->if_sadl)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+
+int
+myx_media_change(struct ifnet *ifp)
+{
+ return (EINVAL);
+}
+
+void
+myx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct myx_softc *sc = (struct myx_softc *)ifp->if_softc;
+
+ imr->ifm_active = IFM_ETHER|sc->sc_phy;
+ imr->ifm_status = IFM_AVALID;
+ myx_link_state(sc);
+ if (!LINK_STATE_IS_UP(ifp->if_link_state))
+ return;
+ imr->ifm_active |= IFM_FDX;
+ imr->ifm_status |= IFM_ACTIVE;
+
+ /* Flow control */
+ if (sc->sc_hwflags & MYXFLAG_FLOW_CONTROL)
+ imr->ifm_active |= IFM_FLOW|IFM_ETH_RXPAUSE|IFM_ETH_TXPAUSE;
+}
+
+void
+myx_link_state(struct myx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ int link_state = LINK_STATE_DOWN;
+
+ if (sc->sc_sts->ms_linkstate == MYXSTS_LINKUP)
+ link_state = LINK_STATE_FULL_DUPLEX;
+ if (ifp->if_link_state != link_state) {
+ ifp->if_link_state = link_state;
+ if_link_state_change(ifp);
+ }
+}
+
+void
+myx_watchdog(struct ifnet *ifp)
+{
+ return;
+}
+
+void
+myx_tick(void *arg)
+{
+ struct myx_softc *sc = (struct myx_softc *)arg;
+
+ if (!sc->sc_active)
+ return;
+
+ myx_link_state(sc);
+ timeout_add(&sc->sc_tick, hz);
+}
+
+int
+myx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct myx_softc *sc = (struct myx_softc *)ifp->if_softc;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+ if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) {
+ splx(s);
+ return (error);
+ }
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ arp_ifinit(&sc->sc_ac, ifa);
+#endif
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING)
+ myx_iff(sc);
+ else
+ myx_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ myx_stop(ifp);
+ }
+ break;
+
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ifp->if_hardmtu)
+ error = EINVAL;
+ else if (ifp->if_mtu != ifr->ifr_mtu)
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ error = ether_addmulti(ifr, &sc->sc_ac);
+ break;
+
+ case SIOCDELMULTI:
+ error = ether_delmulti(ifr, &sc->sc_ac);
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
+
+ default:
+ error = ENOTTY;
+ }
+
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+ (IFF_UP | IFF_RUNNING))
+ myx_iff(sc);
+ error = 0;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+void
+myx_iff(struct myx_softc *sc)
+{
+ /* XXX set multicast filters etc. */
+ return;
+}
+
+void
+myx_init(struct ifnet *ifp)
+{
+ struct myx_softc *sc = (struct myx_softc *)ifp->if_softc;
+
+ if (myx_setlladdr(sc, LLADDR(ifp->if_sadl)) != 0)
+ return;
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+}
+
+void
+myx_start(struct ifnet *ifp)
+{
+}
+
+void
+myx_stop(struct ifnet *ifp)
+{
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+}
+
+int
+myx_setlladdr(struct myx_softc *sc, u_int8_t *addr)
+{
+ struct myx_cmd mc;
+
+ bzero(&mc, sizeof(mc));
+ mc.mc_data0 = addr[3] | addr[2] << 8 | addr[1] << 16 | addr[0] << 24;
+ mc.mc_data1 = addr[5] | addr[4] << 8;
+ if (myx_cmd(sc, MYXCMD_SET_LLADDR, &mc, NULL) != 0) {
+ printf("%s: failed to set the lladdr\n", DEVNAME(sc));
+ return (-1);
+ }
+ return (0);
+}
+
+int
+myx_intr(void *arg)
+{
+ struct myx_softc *sc = (struct myx_softc *)arg;
+
+ if (!sc->sc_active || sc->sc_sts->ms_isvalid == 0)
+ return (0);
+
+ DPRINTF(MYXDBG_INTR, "%s(%s): interrupt\n", DEVNAME(sc), __func__);
+ return (1);
+}
diff --git a/sys/dev/pci/if_myxreg.h b/sys/dev/pci/if_myxreg.h
new file mode 100644
index 00000000000..1edd00c2407
--- /dev/null
+++ b/sys/dev/pci/if_myxreg.h
@@ -0,0 +1,206 @@
+/* $OpenBSD: if_myxreg.h,v 1.1 2007/05/31 18:23:42 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Register definitions for the Myricom Myri-10G Lanai-Z8E Ethernet chipsets.
+ */
+
+#ifndef _MYX_REG_H
+#define _MYX_REG_H
+
+/*
+ * Common definitions
+ */
+
+#define MYXBAR0 PCI_MAPREG_START
+
+#define MYX_NRXDESC 256
+#define MYX_IRQCOALDELAY 30
+#define MYX_IRQDEASSERTWAIT 1
+#define MYX_FLOW_CONTROL 1
+
+#define MYXALIGN_CMD 64
+#define MYXALIGN_DATA PAGE_SIZE
+
+#define MYX_ADDRHIGH(_v) htobe32((_v >> 32) & 0xffffffff)
+#define MYX_ADDRLOW(_v) htobe32(_v & 0xffffffff)
+
+/*
+ * PCI memory/register layout
+ */
+
+#define MYX_SRAM 0x00000000 /* SRAM offset */
+#define MYX_SRAM_SIZE 0x001dff00 /* SRAM size */
+#define MYX_HEADER_POS 0x0000003c /* Header position offset */
+#define MYX_HEADER_POS_SIZE 0x00000004 /* Header position size */
+#define MYX_FW 0x00100000 /* Firmware offset */
+#define MYX_FW_BOOT 0x00100008 /* Firmware boot offset */
+#define MYX_EEPROM 0x001dfe00 /* EEPROM offset */
+#define MYX_EEPROM_SIZE 0x00000100 /* EEPROM size */
+#define MYX_BOOT 0x00fc0000 /* Boot handoff */
+#define MYX_RDMA 0x00fc01c0 /* Dummy RDMA */
+#define MYX_CMD 0x00f80000 /* Command offset */
+
+/*
+ * Firmware definitions
+ */
+
+#define MYXFW_ALIGNED "myx-eth_z8e"
+#define MYXFW_UNALIGNED "myx-ethp_z8e"
+#define MYXFW_TYPE_ETH 0x45544820
+#define MYXFW_VER "1.4." /* stored as a string... */
+
+#define MYXFW_MIN_LEN (MYX_HEADER_POS + MYX_HEADER_POS_SIZE)
+
+struct myx_firmware_hdr {
+ u_int32_t fw_hdrlength;
+ u_int32_t fw_type;
+ u_int8_t fw_version[128];
+ u_int32_t fw_sram_size;
+ u_int32_t fw_specs;
+ u_int32_t fw_specs_len;
+} __packed;
+
+
+/*
+ * Commands, descriptors, and DMA structures
+ */
+
+struct myx_cmd {
+ u_int32_t mc_cmd;
+ u_int32_t mc_data0;
+ u_int32_t mc_data1;
+ u_int32_t mc_data2;
+ u_int32_t mc_addr_high;
+ u_int32_t mc_addr_low;
+ u_int8_t mc_pad[40]; /* pad up to 64 bytes */
+} __packed;
+
+struct myx_response {
+ u_int32_t mr_data;
+ u_int32_t mr_result;
+} __packed;
+
+struct myx_bootcmd {
+ u_int32_t bc_addr_high;
+ u_int32_t bc_addr_low;
+ u_int32_t bc_result;
+ u_int32_t bc_offset;
+ u_int32_t bc_length;
+ u_int32_t bc_copyto;
+ u_int32_t bc_jumpto;
+ u_int8_t bc_pad[36]; /* pad up to 64 bytes */
+} __packed;
+
+struct myx_rdmacmd {
+ u_int32_t rc_addr_high;
+ u_int32_t rc_addr_low;
+ u_int32_t rc_result;
+ u_int32_t rc_rdma_high;
+ u_int32_t rc_rdma_low;
+ u_int32_t rc_enable;
+#define MYXRDMA_ON 1
+#define MYXRDMA_OFF 0
+ u_int8_t rc_pad[40]; /* pad up to 64 bytes */
+} __packed;
+
+struct myx_status {
+ u_int32_t ms_reserved;
+ u_int32_t ms_dropped_pause;
+ u_int32_t ms_dropped_unicast;
+ u_int32_t ms_dropped_crc32err;
+ u_int32_t ms_dropped_phyerr;
+ u_int32_t ms_dropped_mcast;
+ u_int32_t ms_txdonecnt;
+ u_int32_t ms_linkstate;
+#define MYXSTS_LINKDOWN 0
+#define MYXSTS_LINKUP 1
+#define MYXSTS_LINKMYRINET 2
+#define MYXSTS_LINKUNKNOWN 3
+ u_int32_t ms_dropped_linkoverflow;
+ u_int32_t ms_dropped_linkerror;
+ u_int32_t ms_dropped_runt;
+ u_int32_t ms_dropped_overrun;
+ u_int32_t ms_dropped_smallbufunderrun;
+ u_int32_t ms_dropped_bigbufunderrun;
+ u_int32_t ms_rdmatags_available;
+#define MYXSTS_RDMAON 1
+#define MYXSTS_RDMAOFF 0
+ u_int8_t ms_txstopped;
+ u_int8_t ms_linkdowncnt;
+ u_int8_t ms_statusupdated;
+ u_int8_t ms_isvalid;
+} __packed;
+
+struct myx_rxdesc {
+ u_int16_t rd_csum;
+ u_int16_t rd_length;
+} __packed;
+
+enum {
+ MYXCMD_NONE = 0,
+ MYXCMD_RESET = 1,
+ MYXCMD_GET_VERSION = 2,
+ MYXCMD_SET_INTRQDMA = 3,
+ MYXCMD_SET_BIGBUFSZ = 4,
+ MYXCMD_SET_SMALLBUFSZ = 5,
+ MYXCMD_GET_TXOFF = 6,
+ MYXCMD_GET_SMALLRXOFF = 7,
+ MYXCMD_GET_BIGRXOFF = 8,
+ MYXCMD_GET_INTRACKOFF = 9,
+ MYXCMD_GET_INTRDEASSERTOFF = 10,
+ MYXCMD_GET_TXRINGSZ = 11,
+ MYXCMD_GET_RXRINGSZ = 12,
+ MYXCMD_SET_INTRQSZ = 13,
+ MYXCMD_SET_IFUP = 14,
+ MYXCMD_SET_IFDOWN = 15,
+ MYXCMD_SET_MTU = 16,
+ MYXCMD_GET_INTRCOALDELAYOFF = 17,
+ MYXCMD_SET_STATSINTVL = 18,
+ MYXCMD_SET_STATSDMA_OLD = 19,
+ MYXCMD_SET_PROMISC = 20,
+ MYXCMD_UNSET_PROMISC = 21,
+ MYXCMD_SET_LLADDR = 22,
+ MYXCMD_SET_FC = 23,
+ MYXCMD_UNSET_FC = 24,
+ MYXCMD_DMA_TEST = 25,
+ MYXCMD_SET_ALLMULTI = 26,
+ MYXCMD_UNSET_ALLMULTI = 27,
+ MYXCMD_SET_MCASTGROUP = 28,
+ MYXCMD_UNSET_MCASTGROUP = 29,
+ MYXCMD_UNSET_MCAST = 30,
+ MYXCMD_SET_STATSDMA = 31,
+ MYXCMD_UNALIGNED_DMA_TEST = 32,
+ MYXCMD_GET_UNALIGNED_STATUS = 33
+};
+
+enum {
+ MYXCMD_OK = 0,
+ MYXCMD_UNKNOWN = 1,
+ MYXCMD_ERR_RANGE = 2,
+ MYXCMD_ERR_BUSY = 3,
+ MYXCMD_ERR_EMPTY = 4,
+ MYXCMD_ERR_CLOSED = 5,
+ MYXCMD_ERR_HASH = 6,
+ MYXCMD_ERR_BADPORT = 7,
+ MYXCMD_ERR_RES = 8,
+ MYXCMD_ERR_MULTICAST = 9,
+ MYXCMD_ERR_UNALIGNED = 10
+};
+
+#endif /* _MYX_REG_H */