--- /dev/null
+/* Attansic/Atheros L2 FastEthernet driver, by D.C. van Moolenbroek */
+
+/* No documentation is available for this card. The FreeBSD driver is based
+ * heavily on the official Linux driver; this driver is based heavily on both.
+ */
+
+#include "../drivers.h"
+
+#include <signal.h>
+#include <sys/mman.h>
+#include <minix/ds.h>
+#include <minix/vm.h>
+#include <ibm/pci.h>
+#include <net/gen/ether.h>
+#include <net/gen/eth_io.h>
+#include <assert.h>
+
+#include "atl2.h"
+
+#define VERBOSE 0 /* Verbose debugging output */
+#define ATL2_FKEY 1 /* Register Shift+F11 for dumping statistics */
+
+#if VERBOSE
+#define ATL2_DEBUG(x) printf x
+#else
+#define ATL2_DEBUG(x)
+#endif
+
+typedef struct {
+ u32_t hdr;
+ u32_t vtag;
+ u8_t data[ATL2_RXD_SIZE - sizeof(u32_t) * 2];
+} rxd_t;
+
+PRIVATE struct {
+ int devind; /* PCI device index */
+ int irq; /* IRQ number */
+ int hook_id; /* IRQ hook ID */
+ int mode; /* datalink mode */
+ char *base; /* base address of memory-mapped registers */
+ u32_t hwaddr[2]; /* MAC address, in register representation */
+
+ u8_t *txd_base; /* local address of TxD ring buffer base */
+ u32_t *txs_base; /* local address of TxS ring buffer base */
+ u8_t *rxd_base_u; /* unaligned base address of RxD ring buffer */
+ rxd_t *rxd_base; /* local address of RxD ring buffer base */
+
+ int rxd_align; /* alignment offset of RxD ring buffer */
+
+ vir_bytes txd_phys; /* physical address of TxD ring buffer */
+ vir_bytes txs_phys; /* physical address of TxS ring buffer */
+ vir_bytes rxd_phys; /* physical address of RxD ring buffer */
+
+ int txd_tail; /* tail index into TxD, in bytes */
+ int txd_num; /* head-tail offset into TxD, in bytes */
+ int txs_tail; /* tail index into TxS, in elements */
+ int txs_num; /* head-tail offset into TxS, in elements */
+ int rxd_tail; /* tail index into RxD, in elements */
+
+ int flags; /* state flags (ATL2_FLAG_) */
+ message read_msg; /* suspended read request (READ_PEND) */
+ message write_msg; /* suspended write request (WRITE_PEND) */
+ endpoint_t task_endpt; /* requester endpoint (PACK_RCVD|PACK_SENT) */
+ size_t recv_count; /* packet size (PACK_RCVD) */
+
+ eth_stat_t stat; /* statistics */
+} state;
+
+#define ATL2_FLAG_RX_AVAIL 0x01 /* packet available for receipt */
+#define ATL2_FLAG_READ_PEND 0x02 /* read request pending */
+#define ATL2_FLAG_WRITE_PEND 0x04 /* write request pending */
+#define ATL2_FLAG_PACK_RCVD 0x08 /* packet received */
+#define ATL2_FLAG_PACK_SENT 0x10 /* packet transmitted */
+
+#define ATL2_READ_U8(off) (* (u8_t *) (state.base + (off)))
+#define ATL2_READ_U16(off) (* (u16_t *) (state.base + (off)))
+#define ATL2_READ_U32(off) (* (u32_t *) (state.base + (off)))
+#define ATL2_WRITE_U8(off, val) * (u8_t *) (state.base + (off)) = (val);
+#define ATL2_WRITE_U16(off, val) * (u16_t *) (state.base + (off)) = (val);
+#define ATL2_WRITE_U32(off, val) * (u32_t *) (state.base + (off)) = (val);
+
+#define ATL2_ALIGN_32(n) (((n) + 3) & ~3)
+
+PRIVATE iovec_s_t iovec[NR_IOREQS];
+
+PRIVATE struct {
+ u16_t vid;
+ u16_t did;
+} pcitab[] = {
+ { 0x1969, 0x2048 }, /* Attansic Technology Corp, L2 FastEthernet */
+ { 0x0000, 0x0000 }
+};
+
+/*===========================================================================*
+ * atl2_read_vpd *
+ *===========================================================================*/
+PRIVATE int atl2_read_vpd(int index, u32_t *res)
+{
+ /* Read a value from the VPD register area.
+ */
+ u32_t off, val;
+ int i;
+
+ ATL2_WRITE_U32(ATL2_VPD_DATA_REG, 0);
+
+ off = ATL2_VPD_REGBASE + index * sizeof(u32_t);
+
+ ATL2_WRITE_U32(ATL2_VPD_CAP_REG,
+ (off << ATL2_VPD_CAP_ADDR_SHIFT) & ATL2_VPD_CAP_ADDR_MASK);
+
+ for (i = 0; i < ATL2_VPD_NTRIES; i++) {
+ micro_delay(ATL2_VPD_DELAY);
+
+ val = ATL2_READ_U32(ATL2_VPD_CAP_REG);
+ if (val & ATL2_VPD_CAP_DONE)
+ break;
+ }
+
+ if (i == ATL2_VPD_NTRIES) {
+ printf("ATL2: timeout reading EEPROM register %d\n", index);
+ return FALSE;
+ }
+
+ *res = ATL2_READ_U32(ATL2_VPD_DATA_REG);
+ return TRUE;
+}
+
+/*===========================================================================*
+ * atl2_get_vpd_hwaddr *
+ *===========================================================================*/
+PRIVATE int atl2_get_vpd_hwaddr(void)
+{
+ /* Read the MAC address from the EEPROM, using the Vital Product Data
+ * register interface.
+ */
+ u32_t key, val;
+ int i, n, found[2];
+
+ /* No idea, copied from FreeBSD which copied it from Linux. */
+ val = ATL2_READ_U32(ATL2_SPICTL_REG);
+ if (val & ATL2_SPICTL_VPD_EN) {
+ val &= ~ATL2_SPICTL_VPD_EN;
+ ATL2_WRITE_U32(ATL2_SPICTL_REG, val);
+ }
+
+ /* Is VPD supported? */
+#ifdef PCI_CAP_VPD /* FIXME: just a guess at the future name */
+ if (!pci_find_cap(state.devind, PCI_CAP_VPD, &n))
+ return FALSE;
+#endif
+
+ /* Read out the set of key/value pairs. Look for the two parts that
+ * make up the MAC address.
+ */
+ found[0] = found[1] = FALSE;
+ for (i = 0; i < ATL2_VPD_NREGS; i += 2) {
+ if (!atl2_read_vpd(i, &key))
+ break;
+
+ if ((key & ATL2_VPD_SIG_MASK) != ATL2_VPD_SIG)
+ break;
+
+ key >>= ATL2_VPD_REG_SHIFT;
+
+ if (key != ATL2_HWADDR0_REG && key != ATL2_HWADDR1_REG)
+ continue;
+
+ if (!atl2_read_vpd(i + 1, &val))
+ break;
+
+ n = (key == ATL2_HWADDR1_REG);
+ state.hwaddr[n] = val;
+ found[n] = TRUE;
+
+ if (found[1 - n]) break;
+ }
+
+ return found[0] && found[1];
+}
+
+/*===========================================================================*
+ * atl2_get_hwaddr *
+ *===========================================================================*/
+PRIVATE void atl2_get_hwaddr(void)
+{
+ /* Get the MAC address of the card. First try the EEPROM; if that
+ * fails, just use whatever the card was already set to.
+ */
+
+ if (!atl2_get_vpd_hwaddr()) {
+ printf("ATL2: unable to read from VPD\n");
+
+ state.hwaddr[0] = ATL2_READ_U32(ATL2_HWADDR0_REG);
+ state.hwaddr[1] = ATL2_READ_U32(ATL2_HWADDR1_REG) & 0xffff;
+ }
+
+ ATL2_DEBUG(("ATL2: MAC address %04lx%08lx\n",
+ state.hwaddr[1], state.hwaddr[0]));
+}
+
+/*===========================================================================*
+ * atl2_read_mdio *
+ *===========================================================================*/
+PRIVATE int atl2_read_mdio(int addr, u16_t *res)
+{
+ /* Read a MII PHY register using MDIO.
+ */
+ u32_t rval;
+ int i;
+
+ rval = ((addr << ATL2_MDIO_ADDR_SHIFT) & ATL2_MDIO_ADDR_MASK) |
+ ATL2_MDIO_START | ATL2_MDIO_READ | ATL2_MDIO_SUP_PREAMBLE |
+ ATL2_MDIO_CLK_25_4;
+
+ ATL2_WRITE_U32(ATL2_MDIO_REG, rval);
+
+ for (i = 0; i < ATL2_MDIO_NTRIES; i++) {
+ micro_delay(ATL2_MDIO_DELAY);
+
+ rval = ATL2_READ_U32(ATL2_MDIO_REG);
+
+ if (!(rval & (ATL2_MDIO_START | ATL2_MDIO_BUSY)))
+ break;
+ }
+
+ if (i == ATL2_MDIO_NTRIES) return FALSE;
+
+ *res = (u16_t) (rval & ATL2_MDIO_DATA_MASK);
+ return TRUE;
+}
+
+/*===========================================================================*
+ * atl2_alloc_dma *
+ *===========================================================================*/
+PRIVATE int atl2_alloc_dma(void)
+{
+ /* Allocate DMA ring buffers.
+ */
+
+ state.txd_base = alloc_contig(ATL2_TXD_BUFSIZE,
+ AC_ALIGN4K, &state.txd_phys);
+ state.txs_base = alloc_contig(ATL2_TXS_COUNT * sizeof(u32_t),
+ AC_ALIGN4K, &state.txs_phys);
+
+ /* The data buffer in each RxD descriptor must be 128-byte aligned.
+ * The two Tx buffers merely require a 4-byte start alignment.
+ */
+ state.rxd_align = 128 - offsetof(rxd_t, data);
+ state.rxd_base_u =
+ alloc_contig(state.rxd_align + ATL2_RXD_COUNT * ATL2_RXD_SIZE,
+ AC_ALIGN4K, &state.rxd_phys);
+
+ /* Unlike mmap, alloc_contig returns NULL on failure. */
+ if (!state.txd_base || !state.txs_base || !state.rxd_base_u)
+ return ENOMEM;
+
+ state.rxd_base = (rxd_t *) (state.rxd_base_u + state.rxd_align);
+ state.rxd_phys += state.rxd_align;
+
+ /* Zero out just in case. */
+ memset(state.txd_base, 0, ATL2_TXD_BUFSIZE);
+ memset(state.txs_base, 0, ATL2_TXS_COUNT * sizeof(u32_t));
+ memset(state.rxd_base, 0, ATL2_RXD_COUNT * ATL2_RXD_SIZE);
+
+ return OK;
+}
+
+/*===========================================================================*
+ * atl2_stop *
+ *===========================================================================*/
+PRIVATE int atl2_stop(void)
+{
+ /* Stop the device.
+ */
+ u32_t val;
+ int i;
+
+ /* Clear and disable interrupts. */
+ ATL2_WRITE_U32(ATL2_IMR_REG, 0);
+ ATL2_WRITE_U32(ATL2_ISR_REG, 0xffffffff);
+
+ /* Stop Rx/Tx MACs. */
+ val = ATL2_READ_U32(ATL2_MAC_REG);
+ if (val & (ATL2_MAC_RX_EN | ATL2_MAC_TX_EN)) {
+ val &= ~(ATL2_MAC_RX_EN | ATL2_MAC_TX_EN);
+ ATL2_WRITE_U32(ATL2_MAC_REG, val);
+ }
+
+ ATL2_WRITE_U8(ATL2_DMAWRITE_REG, 0);
+ ATL2_WRITE_U8(ATL2_DMAREAD_REG, 0);
+
+ /* Wait until everything is idle. */
+ for (i = 0; i < ATL2_IDLE_NTRIES; i++) {
+ if (ATL2_READ_U32(ATL2_IDLE_REG) == 0)
+ break;
+
+ micro_delay(ATL2_IDLE_DELAY);
+ }
+
+ /* The caller will generally ignore this return value. */
+ return (i < ATL2_IDLE_NTRIES);
+}
+
+/*===========================================================================*
+ * atl2_reset *
+ *===========================================================================*/
+PRIVATE int atl2_reset(void)
+{
+ /* Reset the device to a known good state.
+ */
+ u32_t val;
+ int i;
+
+ /* Issue a soft reset, and wait for the device to respond. */
+ ATL2_WRITE_U32(ATL2_MASTER_REG, ATL2_MASTER_SOFT_RESET);
+
+ for (i = 0; i < ATL2_RESET_NTRIES; i++) {
+ val = ATL2_READ_U32(ATL2_MASTER_REG);
+ if (!(val & ATL2_MASTER_SOFT_RESET))
+ break;
+
+ micro_delay(ATL2_RESET_DELAY);
+ }
+
+ if (i == ATL2_RESET_NTRIES)
+ return FALSE;
+
+ /* Wait until everything is idle. */
+ for (i = 0; i < ATL2_IDLE_NTRIES; i++) {
+ if (ATL2_READ_U32(ATL2_IDLE_REG) == 0)
+ break;
+
+ micro_delay(ATL2_IDLE_DELAY);
+ }
+
+ return (i < ATL2_IDLE_NTRIES);
+}
+
+/*===========================================================================*
+ * atl2_set_mode *
+ *===========================================================================*/
+PRIVATE void atl2_set_mode(void)
+{
+ /* Reconfigure the device's promiscuity, multicast, and broadcast mode
+ * settings.
+ */
+ u32_t val;
+
+ val = ATL2_READ_U32(ATL2_MAC_REG);
+ val &= ~(ATL2_MAC_PROMISC_EN | ATL2_MAC_MCAST_EN | ATL2_MAC_BCAST_EN);
+
+ if (state.mode & DL_PROMISC_REQ)
+ val |= ATL2_MAC_PROMISC_EN;
+ if (state.mode & DL_MULTI_REQ)
+ val |= ATL2_MAC_MCAST_EN;
+ if (state.mode & DL_BROAD_REQ)
+ val |= ATL2_MAC_BCAST_EN;
+
+ ATL2_WRITE_U32(ATL2_MAC_REG, val);
+}
+
+/*===========================================================================*
+ * atl2_setup *
+ *===========================================================================*/
+PRIVATE int atl2_setup(void)
+{
+ /* Set up the device for normal operation.
+ */
+ u32_t val;
+
+ atl2_stop();
+
+ if (!atl2_reset())
+ return FALSE;
+
+ /* Initialize PCIE module. Magic. */
+ ATL2_WRITE_U32(ATL2_LTSSM_TESTMODE_REG, ATL2_LTSSM_TESTMODE_DEFAULT);
+ ATL2_WRITE_U32(ATL2_DLL_TX_CTRL_REG, ATL2_DLL_TX_CTRL_DEFAULT);
+
+ /* Enable PHY. */
+ ATL2_WRITE_U32(ATL2_PHY_ENABLE_REG, ATL2_PHY_ENABLE);
+ micro_delay(1000);
+
+ /* Clear and disable interrupts. */
+ ATL2_WRITE_U32(ATL2_ISR_REG, 0xffffffff);
+
+ /* Set the MAC address. */
+ ATL2_WRITE_U32(ATL2_HWADDR0_REG, state.hwaddr[0]);
+ ATL2_WRITE_U32(ATL2_HWADDR1_REG, state.hwaddr[1]);
+
+ /* Initialize ring buffer addresses and sizes. */
+ ATL2_WRITE_U32(ATL2_DESC_ADDR_HI_REG, 0); /* no 64 bit */
+ ATL2_WRITE_U32(ATL2_TXD_ADDR_LO_REG, state.txd_phys);
+ ATL2_WRITE_U32(ATL2_TXS_ADDR_LO_REG, state.txs_phys);
+ ATL2_WRITE_U32(ATL2_RXD_ADDR_LO_REG, state.rxd_phys);
+
+ ATL2_WRITE_U16(ATL2_RXD_COUNT_REG, ATL2_RXD_COUNT);
+ ATL2_WRITE_U16(ATL2_TXD_BUFSIZE_REG, ATL2_TXD_BUFSIZE / sizeof(u32_t));
+ ATL2_WRITE_U16(ATL2_TXS_COUNT_REG, ATL2_TXS_COUNT);
+
+ /* A whole lot of other initialization copied from Linux/FreeBSD. */
+ ATL2_WRITE_U32(ATL2_IFG_REG, ATL2_IFG_DEFAULT);
+
+ ATL2_WRITE_U32(ATL2_HDPX_REG, ATL2_HDPX_DEFAULT);
+
+ ATL2_WRITE_U16(ATL2_IMT_REG, ATL2_IMT_DEFAULT);
+ val = ATL2_READ_U32(ATL2_MASTER_REG);
+ ATL2_WRITE_U32(ATL2_MASTER_REG, val | ATL2_MASTER_IMT_EN);
+
+ ATL2_WRITE_U16(ATL2_ICT_REG, ATL2_ICT_DEFAULT);
+
+ ATL2_WRITE_U32(ATL2_CUT_THRESH_REG, ATL2_CUT_THRESH_DEFAULT);
+
+ ATL2_WRITE_U16(ATL2_FLOW_THRESH_HI_REG, (ATL2_RXD_COUNT / 8) * 7);
+ ATL2_WRITE_U16(ATL2_FLOW_THRESH_LO_REG, ATL2_RXD_COUNT / 12);
+
+ /* Set MTU. */
+ ATL2_WRITE_U16(ATL2_MTU_REG, ATL2_MTU_DEFAULT);
+
+ /* Reset descriptors, and enable DMA. */
+ state.txd_tail = state.txs_tail = state.rxd_tail = 0;
+ state.txd_num = state.txs_num = 0;
+ state.flags &= ~ATL2_FLAG_RX_AVAIL;
+ ATL2_WRITE_U16(ATL2_TXD_IDX_REG, 0);
+ ATL2_WRITE_U16(ATL2_RXD_IDX_REG, 0);
+
+ ATL2_WRITE_U8(ATL2_DMAREAD_REG, ATL2_DMAREAD_EN);
+ ATL2_WRITE_U8(ATL2_DMAWRITE_REG, ATL2_DMAWRITE_EN);
+
+ /* Did everything go alright? */
+ val = ATL2_READ_U32(ATL2_ISR_REG);
+ if (val & ATL2_ISR_PHY_LINKDOWN) {
+ printf("ATL2: initialization failed\n");
+ return FALSE;
+ }
+
+ /* Clear interrupt status. */
+ ATL2_WRITE_U32(ATL2_ISR_REG, 0x3fffffff);
+ ATL2_WRITE_U32(ATL2_ISR_REG, 0);
+
+ /* Enable interrupts. */
+ ATL2_WRITE_U32(ATL2_IMR_REG, ATL2_IMR_DEFAULT);
+
+ /* Configure MAC. */
+ ATL2_WRITE_U32(ATL2_MAC_REG, ATL2_MAC_DEFAULT);
+
+ /* Inet does not tell us about the multicast addresses that it is
+ * interested in, so we have to simply accept all multicast packets.
+ */
+ ATL2_WRITE_U32(ATL2_MHT0_REG, 0xffffffff);
+ ATL2_WRITE_U32(ATL2_MHT1_REG, 0xffffffff);
+
+ atl2_set_mode();
+
+ /* Enable Tx/Rx. */
+ val = ATL2_READ_U32(ATL2_MAC_REG);
+ ATL2_WRITE_U32(ATL2_MAC_REG, val | ATL2_MAC_TX_EN | ATL2_MAC_RX_EN);
+
+ return TRUE;
+}
+
+/*===========================================================================*
+ * atl2_probe *
+ *===========================================================================*/
+PRIVATE int atl2_probe(int instance)
+{
+ /* Find a matching PCI device.
+ */
+ u16_t vid, did;
+ char *dname;
+ int i, r, devind, skip;
+
+ pci_init();
+
+ r = pci_first_dev(&devind, &vid, &did);
+ if (r <= 0)
+ return -1;
+
+ skip = 0;
+
+ for (;;) {
+ for (i = 0; pcitab[i].vid != 0; i++)
+ if (pcitab[i].vid == vid && pcitab[i].did == did)
+ break;
+
+ if (pcitab[i].vid != 0) {
+ if (skip == instance) break;
+
+ skip++;
+ }
+
+ r = pci_next_dev(&devind, &vid, &did);
+ if (r <= 0)
+ return -1;
+ }
+
+ dname = pci_dev_name(vid, did);
+ ATL2_DEBUG(("ATL2: found %s (%x/%x) at %s\n",
+ dname ? dname : "<unknown>", vid, did,
+ pci_slot_name(devind)));
+
+ pci_reserve(devind);
+
+ return devind;
+}
+
+/*===========================================================================*
+ * atl2_init *
+ *===========================================================================*/
+PRIVATE void atl2_init(int devind)
+{
+ /* Initialize the device.
+ */
+ u32_t bar;
+ int r;
+
+ /* Initialize global state. */
+ state.devind = devind;
+ state.mode = DL_NOMODE;
+ state.flags = 0;
+ state.recv_count = 0;
+
+ memset(&state.stat, 0, sizeof(state.stat));
+
+ /* FIXME: zero out the upper half of the 64-bit BAR. This is currently
+ * needed because the BIOS sets it to a nonzero value, and our PCI
+ * driver does not yet recognize 64-bit BARs at all. If either ever
+ * gets fixed, this will be a no-op, but for the time being, we simply
+ * hope that it will do the job.
+ */
+ pci_attr_w32(devind, PCI_BAR_2, 0);
+
+ bar = pci_attr_r32(devind, PCI_BAR) & 0xfffffff0;
+
+ /* FIXME: hardcoded length, as PCI doesn't expose the size, and it is
+ * not our job to compute the size from the BAR ourselves.
+ */
+ state.base = vm_map_phys(SELF, (void *) bar, ATL2_MMAP_SIZE);
+ if (state.base == MAP_FAILED)
+ panic("atl2", "unable to map in registers", NO_NUM);
+
+ if ((r = atl2_alloc_dma()) != OK)
+ panic("atl2", "unable to allocate DMA buffers", r);
+
+ state.irq = pci_attr_r8(devind, PCI_ILR);
+
+ if ((r = sys_irqsetpolicy(state.irq, 0, &state.hook_id)) != OK)
+ panic("atl2", "unable to register IRQ", r);
+
+ if (!atl2_reset())
+ panic("atl2", "unable to reset hardware", NO_NUM);
+
+ if ((r = sys_irqenable(&state.hook_id)) != OK)
+ panic("atl2", "unable to enable IRQ", r);
+
+ atl2_get_hwaddr();
+
+ atl2_setup();
+}
+
+/*===========================================================================*
+ * atl2_tx_stat *
+ *===========================================================================*/
+PRIVATE void atl2_tx_stat(u32_t stat)
+{
+ /* Update statistics for packet transmission.
+ */
+
+ if (stat & ATL2_TXS_SUCCESS)
+ state.stat.ets_packetT++;
+ else
+ state.stat.ets_recvErr++;
+
+ if (stat & ATL2_TXS_DEFER)
+ state.stat.ets_transDef++;
+ if (stat & (ATL2_TXS_EXCDEFER | ATL2_TXS_ABORTCOL))
+ state.stat.ets_transAb++;
+ if (stat & ATL2_TXS_SINGLECOL)
+ state.stat.ets_collision++;
+ if (stat & ATL2_TXS_MULTICOL)
+ state.stat.ets_collision++;
+ if (stat & ATL2_TXS_LATECOL)
+ state.stat.ets_OWC++;
+ if (stat & ATL2_TXS_UNDERRUN)
+ state.stat.ets_fifoUnder++;
+}
+
+/*===========================================================================*
+ * atl2_rx_stat *
+ *===========================================================================*/
+PRIVATE void atl2_rx_stat(u32_t stat)
+{
+ /* Update statistics for packet receipt.
+ */
+
+ if (stat & ATL2_RXD_SUCCESS)
+ state.stat.ets_packetR++;
+ else
+ state.stat.ets_recvErr++;
+
+ if (stat & ATL2_RXD_CRCERR)
+ state.stat.ets_CRCerr++;
+ if (stat & ATL2_RXD_FRAG)
+ state.stat.ets_collision++;
+ if (stat & ATL2_RXD_TRUNC)
+ state.stat.ets_fifoOver++;
+ if (stat & ATL2_RXD_ALIGN)
+ state.stat.ets_frameAll++;
+}
+
+/*===========================================================================*
+ * atl2_tx_advance *
+ *===========================================================================*/
+PRIVATE int atl2_tx_advance(void)
+{
+ /* Advance the TxD/TxS tails by as many sent packets as found.
+ */
+ u32_t stat, size, dsize;
+ int advanced;
+
+ advanced = FALSE;
+
+ while (state.txs_num > 0) {
+ /* Has the tail packet been processed by the driver? */
+ stat = state.txs_base[state.txs_tail];
+
+ if (!(stat & ATL2_TXS_UPDATE))
+ break;
+
+ /* The packet size from the status must match the packet size
+ * we put in. If they don't, there's not much we can do..
+ */
+ size = stat & ATL2_TXS_SIZE_MASK;
+
+ assert(state.txd_tail <= ATL2_TXD_BUFSIZE - sizeof(u32_t));
+ dsize = * (u32_t *) (state.txd_base + state.txd_tail);
+ if (size != dsize)
+ printf("ATL2: TxD/TxS size mismatch (%lx vs %lx)\n",
+ size, dsize);
+
+ /* Advance tails accordingly. */
+ size = sizeof(u32_t) + ATL2_ALIGN_32(dsize);
+ assert(state.txd_num >= size);
+ state.txd_tail = (state.txd_tail + size) % ATL2_TXD_BUFSIZE;
+ state.txd_num -= size;
+
+ state.txs_tail = (state.txs_tail + 1) % ATL2_TXS_COUNT;
+ state.txs_num--;
+
+ if (stat & ATL2_TXS_SUCCESS)
+ ATL2_DEBUG(("ATL2: successfully sent packet\n"));
+ else
+ ATL2_DEBUG(("ATL2: failed to send packet\n"));
+
+ /* Update statistics. */
+ atl2_tx_stat(stat);
+
+ advanced = TRUE;
+ }
+
+ return advanced;
+}
+
+/*===========================================================================*
+ * atl2_rx_advance *
+ *===========================================================================*/
+PRIVATE void atl2_rx_advance(int next)
+{
+ /* Advance the RxD tail by as many failed receipts as possible, and
+ * see if there is an actual packet left to receive. If 'next' is set,
+ * the packet at the current tail has been processed.
+ */
+ int update_tail;
+ rxd_t *rxd;
+ u32_t hdr, size;
+
+ update_tail = FALSE;
+
+ if (next) {
+ state.rxd_tail = (state.rxd_tail + 1) % ATL2_RXD_COUNT;
+ update_tail = TRUE;
+
+ ATL2_DEBUG(("ATL2: successfully received packet\n"));
+
+ state.flags &= ~ATL2_FLAG_RX_AVAIL;
+ }
+
+ assert(!(state.flags & ATL2_FLAG_RX_AVAIL));
+
+ for (;;) {
+ /* Check the RxD tail for updates. */
+ rxd = &state.rxd_base[state.rxd_tail];
+
+ hdr = rxd->hdr;
+
+ if (!(hdr & ATL2_RXD_UPDATE))
+ break;
+
+ rxd->hdr = hdr & ~(ATL2_RXD_UPDATE);
+
+ /* Update statistics. */
+ atl2_rx_stat(hdr);
+
+ /* Stop at the first successful receipt. The packet will be
+ * picked up by Inet later.
+ */
+ size = hdr & ATL2_RXD_SIZE_MASK;
+
+ if ((hdr & ATL2_RXD_SUCCESS) && size >= ETH_MIN_PACK_SIZE) {
+ ATL2_DEBUG(("ATL2: packet available, size %ld\n",
+ size));
+
+ state.flags |= ATL2_FLAG_RX_AVAIL;
+ break;
+ }
+
+ ATL2_DEBUG(("ATL2: packet receipt failed\n"));
+
+ /* Advance tail. */
+ state.rxd_tail = (state.rxd_tail + 1) % ATL2_RXD_COUNT;
+ update_tail = TRUE;
+ }
+
+ /* If new RxD descriptors are now up for reuse, tell the device. */
+ if (update_tail)
+ ATL2_WRITE_U32(ATL2_RXD_IDX_REG, state.rxd_tail);
+}
+
+/*===========================================================================*
+ * atl2_reply *
+ *===========================================================================*/
+PRIVATE void atl2_reply(void)
+{
+ /* Send a task reply to Inet.
+ */
+ message m;
+ int r, stat;
+
+ stat = 0;
+ if (state.flags & ATL2_FLAG_PACK_SENT)
+ stat |= DL_PACK_SEND;
+ if (state.flags & ATL2_FLAG_PACK_RCVD)
+ stat |= DL_PACK_RECV;
+
+ m.m_type = DL_TASK_REPLY;
+ m.DL_PORT = 0;
+ m.DL_PROC = state.task_endpt;
+ m.DL_STAT = stat;
+ m.DL_COUNT = state.recv_count;
+
+ ATL2_DEBUG(("ATL2: sending reply stat %x count %d\n", stat,
+ m.DL_COUNT));
+
+ if ((r = send(state.task_endpt, &m)) != OK)
+ panic("atl2", "unable to reply", r);
+
+ state.flags &= ~(ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD);
+ state.recv_count = 0;
+}
+
+/*===========================================================================*
+ * atl2_readv *
+ *===========================================================================*/
+PRIVATE void atl2_readv(message *m, int from_int)
+{
+ /* Read packet data.
+ */
+ rxd_t *rxd;
+ iovec_s_t *iovp;
+ size_t count, off, left, size;
+ u8_t *pos;
+ int i, j, r, batch;
+
+ if (m->DL_PORT != 0) {
+ printf("ATL2: read from invalid port\n");
+
+ return;
+ }
+
+ /* We can deal with only one read request from Inet at a time. */
+ assert(from_int || !(state.flags & ATL2_FLAG_READ_PEND));
+
+ /* The exact semantics of DL_PROC are not clear. This driver takes it
+ * to be only the grant owner; other drivers treat it as the reply
+ * destination as well.
+ */
+ assert(m->m_source == m->DL_PROC);
+ state.task_endpt = m->m_source;
+
+ /* Are there any packets available at all? */
+ if (!(state.flags & ATL2_FLAG_RX_AVAIL))
+ goto suspend;
+
+ /* Get the first available packet's size. Cut off the CRC. */
+ rxd = &state.rxd_base[state.rxd_tail];
+
+ count = rxd->hdr & ATL2_RXD_SIZE_MASK;
+ count -= ETH_CRC_SIZE;
+
+ ATL2_DEBUG(("ATL2: readv: found packet with length %d\n", count));
+
+ /* Copy out the packet. */
+ off = 0;
+ left = count;
+ pos = rxd->data;
+
+ for (i = 0; i < m->DL_COUNT && left > 0; i += batch) {
+ /* Copy in the next batch. */
+ batch = MIN(m->DL_COUNT - i, NR_IOREQS);
+
+ r = sys_safecopyfrom(m->DL_PROC, m->DL_GRANT, off,
+ (vir_bytes) iovec, batch * sizeof(iovec[0]), D);
+ if (r != OK)
+ panic("atl2", "vector copy failed", r);
+
+ /* Copy out each element in the batch, until we run out. */
+ for (j = 0, iovp = iovec; j < batch && left > 0; j++, iovp++) {
+ size = MIN(iovp->iov_size, left);
+
+ r = sys_safecopyto(m->DL_PROC, iovp->iov_grant, 0,
+ (vir_bytes) pos, size, D);
+ if (r != OK)
+ panic("atl2", "safe copy failed", r);
+
+ pos += size;
+ left -= size;
+ }
+
+ off += batch * sizeof(iovec[0]);
+ }
+
+ /* Not sure what to do here. Inet shouldn't mess this up anyway. */
+ if (left > 0) {
+ printf("ATL2: truncated packet of %d bytes by %d bytes\n",
+ count, left);
+ count -= left;
+ }
+
+ /* We are done with this packet. Move on to the next. */
+ atl2_rx_advance(TRUE /*next*/);
+
+ /* We have now successfully received a packet. */
+ state.flags &= ~ATL2_FLAG_READ_PEND;
+ state.flags |= ATL2_FLAG_PACK_RCVD;
+ state.recv_count = count;
+
+ /* If called from the interrupt handler, the caller will reply. */
+ if (!from_int)
+ atl2_reply();
+
+ return;
+
+suspend:
+ /* No packets are available at this time. If we were not already
+ * trying to resume receipt, save the read request for later, and tell
+ * Inet that the request has been suspended.
+ */
+ if (from_int)
+ return;
+
+ state.flags |= ATL2_FLAG_READ_PEND;
+ state.read_msg = *m;
+
+ atl2_reply();
+}
+
+/*===========================================================================*
+ * atl2_writev *
+ *===========================================================================*/
+PRIVATE void atl2_writev(message *m, int from_int)
+{
+ /* Write packet data.
+ */
+ iovec_s_t *iovp;
+ size_t off, count, left, pos, size, skip;
+ u8_t *sizep;
+ int i, j, r, batch, maxnum;
+
+ if (m->DL_PORT != 0) {
+ printf("ATL2: write to invalid port\n");
+
+ return;
+ }
+
+ /* We can deal with only one write request from Inet at a time. */
+ assert(from_int || !(state.flags & ATL2_FLAG_WRITE_PEND));
+
+ assert(m->m_source == m->DL_PROC);
+ state.task_endpt = m->m_source;
+
+ /* If we are already certain that the packet won't fit, bail out.
+ * Keep at least some space between TxD head and tail, as it is not
+ * clear whether the device deals well with the case that they collide.
+ */
+ if (state.txs_num >= ATL2_TXS_COUNT)
+ goto suspend;
+ maxnum = ATL2_TXD_BUFSIZE - ETH_MIN_PACK_SIZE - sizeof(u32_t);
+ if (state.txd_num >= maxnum)
+ goto suspend;
+
+ /* Optimistically try to copy in the data; suspend if it turns out
+ * that it does not fit.
+ */
+ off = 0;
+ count = 0;
+ left = state.txd_num - sizeof(u32_t);
+ pos = (state.txd_tail + state.txd_num +
+ sizeof(u32_t)) % ATL2_TXD_BUFSIZE;
+
+ for (i = 0; i < m->DL_COUNT; i += batch) {
+ /* Copy in the next batch. */
+ batch = MIN(m->DL_COUNT - i, NR_IOREQS);
+
+ r = sys_safecopyfrom(m->DL_PROC, m->DL_GRANT, off,
+ (vir_bytes) iovec, batch * sizeof(iovec[0]), D);
+ if (r != OK)
+ panic("atl2", "vector copy failed", r);
+
+ /* Copy in each element in the batch. */
+ for (j = 0, iovp = iovec; j < batch; j++, iovp++) {
+ size = iovp->iov_size;
+ if (size > left)
+ goto suspend;
+
+ skip = 0;
+ if (size > ATL2_TXD_BUFSIZE - pos) {
+ skip = ATL2_TXD_BUFSIZE - pos;
+ r = sys_safecopyfrom(m->DL_PROC,
+ iovp->iov_grant, 0,
+ (vir_bytes) (state.txd_base + pos),
+ skip, D);
+ if (r != OK)
+ panic("atl2", "safe copy failed", r);
+ pos = 0;
+ }
+
+ r = sys_safecopyfrom(m->DL_PROC, iovp->iov_grant, skip,
+ (vir_bytes) (state.txd_base + pos),
+ size - skip, D);
+ if (r != OK)
+ panic("atl2", "safe copy failed", r);
+
+ pos = (pos + size - skip) % ATL2_TXD_BUFSIZE;
+ left -= size;
+ count += size;
+ }
+
+ off += batch * sizeof(iovec[0]);
+ }
+
+ assert(count <= ETH_MAX_PACK_SIZE_TAGGED);
+
+ /* Write the length to the DWORD right before the packet. */
+ sizep = state.txd_base +
+ (state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE;
+ * (u32_t *) sizep = count;
+
+ /* Update the TxD head. */
+ state.txd_num += sizeof(u32_t) + ATL2_ALIGN_32(count);
+ pos = ATL2_ALIGN_32(pos) % ATL2_TXD_BUFSIZE;
+ assert(pos == (state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE);
+
+ /* Initialize and update the TxS head. */
+ state.txs_base[(state.txs_tail + state.txs_num) % ATL2_TXS_COUNT] = 0;
+ state.txs_num++;
+
+ /* Tell the device about our new position. */
+ ATL2_WRITE_U32(ATL2_TXD_IDX_REG, pos / sizeof(u32_t));
+
+ /* We have now successfully set up the transmission of a packet. */
+ state.flags &= ~ATL2_FLAG_WRITE_PEND;
+ state.flags |= ATL2_FLAG_PACK_SENT;
+
+ /* If called from the interrupt handler, the caller will reply. */
+ if (!from_int)
+ atl2_reply();
+
+ return;
+
+suspend:
+ /* We cannot transmit the packet at this time. If we were not already
+ * trying to resume transmission, save the write request for later,
+ * and tell Inet that the request has been suspended.
+ */
+ if (from_int)
+ return;
+
+ state.flags |= ATL2_FLAG_WRITE_PEND;
+ state.write_msg = *m;
+
+ atl2_reply();
+}
+
+/*===========================================================================*
+ * atl2_intr *
+ *===========================================================================*/
+PRIVATE void atl2_intr(message *m)
+{
+ /* Interrupt received.
+ */
+ u32_t val;
+ int r, try_write, try_read;
+
+ /* Clear and disable interrupts. */
+ val = ATL2_READ_U32(ATL2_ISR_REG);
+
+ ATL2_WRITE_U32(ATL2_ISR_REG, val | ATL2_ISR_DISABLE);
+
+ ATL2_DEBUG(("ATL2: interrupt (0x%08lx)\n", val));
+
+ /* If an error occurred, reset the card. */
+ if (val & (ATL2_ISR_DMAR_TIMEOUT | ATL2_ISR_DMAW_TIMEOUT |
+ ATL2_ISR_PHY_LINKDOWN)) {
+ atl2_setup();
+ }
+
+ try_write = try_read = FALSE;
+
+ /* Process sent data, and possibly send pending data. */
+ if (val & ATL2_ISR_TX_EVENT) {
+ if (atl2_tx_advance())
+ try_write = (state.flags & ATL2_FLAG_WRITE_PEND);
+ }
+
+ /* Receive new data, and possible satisfy a pending receive request. */
+ if (val & ATL2_ISR_RX_EVENT) {
+ if (!(state.flags & ATL2_FLAG_RX_AVAIL)) {
+ atl2_rx_advance(FALSE /*next*/);
+
+ try_read = (state.flags & ATL2_FLAG_READ_PEND);
+ }
+ }
+
+ /* Reenable interrupts. */
+ ATL2_WRITE_U32(ATL2_ISR_REG, 0);
+
+ if ((r = sys_irqenable(&state.hook_id)) != OK)
+ panic("atl2", "unable to enable IRQ", r);
+
+ /* Attempt to satisfy pending write and read requests. */
+ if (try_write)
+ atl2_writev(&state.write_msg, TRUE /*from_int*/);
+ if (try_read)
+ atl2_readv(&state.read_msg, TRUE /*from_int*/);
+ if (state.flags & (ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD))
+ atl2_reply();
+}
+
+/*===========================================================================*
+ * atl2_conf *
+ *===========================================================================*/
+PRIVATE void atl2_conf(message *m)
+{
+ /* Configure the mode of the card.
+ */
+ ether_addr_t *addr;
+ int r;
+
+ if (m->DL_PORT != 0) {
+ m->m3_i1 = ENXIO;
+ }
+ else {
+ state.mode = m->DL_MODE;
+
+ atl2_set_mode();
+
+ addr = (ether_addr_t *) m->m3_ca1;
+
+ addr->ea_addr[0] = state.hwaddr[1] >> 8;
+ addr->ea_addr[1] = state.hwaddr[1] & 0xff;
+ addr->ea_addr[2] = state.hwaddr[0] >> 24;
+ addr->ea_addr[3] = (state.hwaddr[0] >> 16) & 0xff;
+ addr->ea_addr[4] = (state.hwaddr[0] >> 8) & 0xff;
+ addr->ea_addr[5] = state.hwaddr[0] & 0xff;
+
+ m->m3_i1 = OK;
+ }
+
+ m->m_type = DL_CONF_REPLY;
+
+ if ((r = send(m->m_source, m)) != OK)
+ printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ * atl2_getstat *
+ *===========================================================================*/
+PRIVATE void atl2_getstat(message *m)
+{
+ /* Copy out statistics.
+ */
+ int r;
+
+ if (m->DL_PORT != 0) {
+ r = ENXIO;
+ }
+ else {
+ r = sys_safecopyto(m->DL_PROC, m->DL_GRANT, 0,
+ (vir_bytes) &state.stat, sizeof(state.stat), D);
+ }
+
+ m->m_type = DL_STAT_REPLY;
+ /* keep m->DL_PORT */
+ m->DL_STAT = r;
+
+ if ((r = send(m->m_source, m)) != OK)
+ printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ * atl2_getname *
+ *===========================================================================*/
+PRIVATE void atl2_getname(message *m, int instance)
+{
+ /* Tell Inet the name of this driver.
+ */
+ int r;
+
+ /* Each instance must have a unique name. */
+ m->m_type = DL_NAME_REPLY;
+ if (instance > 0)
+ snprintf(m->DL_NAME, sizeof(m->DL_NAME), "atl2:%u",
+ instance - 1);
+ else
+ strcpy(m->DL_NAME, "atl2");
+
+ if ((r = send(m->m_source, m)) != OK)
+ printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ * atl2_shutdown *
+ *===========================================================================*/
+PRIVATE void atl2_shutdown(void)
+{
+ /* Shut down this driver. Stop the device, and deallocate resources
+ * as proof of concept.
+ */
+ int r;
+
+ atl2_stop();
+
+ if ((r = sys_irqrmpolicy(&state.hook_id)) != OK)
+ panic("atl2", "unable to deregister IRQ", r);
+
+ munmap(state.txd_base, ATL2_TXD_BUFSIZE);
+ munmap(state.txs_base, ATL2_TXS_COUNT * sizeof(u32_t));
+ munmap(state.rxd_base_u,
+ state.rxd_align + ATL2_RXD_COUNT * ATL2_RXD_SIZE);
+
+ vm_unmap_phys(SELF, state.base, ATL2_MMAP_SIZE);
+
+ /* We cannot free the PCI device at this time. */
+
+ exit(0);
+}
+
+/*===========================================================================*
+ * atl2_dump_link *
+ *===========================================================================*/
+PRIVATE void atl2_dump_link(void)
+{
+ /* Dump link status.
+ */
+ u16_t val;
+ int link_up;
+
+ /* The link status bit is latched. Read the status register twice. */
+ atl2_read_mdio(ATL2_MII_BMSR, &val);
+ if (!atl2_read_mdio(ATL2_MII_BMSR, &val)) return;
+
+ link_up = val & ATL2_MII_BMSR_LSTATUS;
+ printf("link status: %4s\t", link_up ? "up" : "down");
+
+ if (!link_up) return;
+
+ if (!atl2_read_mdio(ATL2_MII_PSSR, &val)) return;
+
+ if (!(val & ATL2_MII_PSSR_RESOLVED)) {
+ printf("(not resolved)\n");
+
+ return;
+ }
+
+ switch (val & ATL2_MII_PSSR_SPEED) {
+ case ATL2_MII_PSSR_10: printf("(10Mbps "); break;
+ case ATL2_MII_PSSR_100: printf("(100Mbps "); break;
+ case ATL2_MII_PSSR_1000: printf("(1000Mbps "); break;
+ default: printf("(unknown, ");
+ }
+
+ printf("%s duplex)", (val & ATL2_MII_PSSR_DUPLEX) ? "full" : "half");
+}
+
+/*===========================================================================*
+ * atl2_dump *
+ *===========================================================================*/
+PRIVATE void atl2_dump(void)
+{
+ /* Dump statistics.
+ */
+
+ printf("\n");
+ printf("Attansic L2 statistics:\n");
+
+ printf("recvErr: %8ld\t", state.stat.ets_recvErr);
+ printf("sendErr: %8ld\t", state.stat.ets_sendErr);
+ printf("OVW: %8ld\n", state.stat.ets_OVW);
+
+ printf("CRCerr: %8ld\t", state.stat.ets_CRCerr);
+ printf("frameAll: %8ld\t", state.stat.ets_frameAll);
+ printf("missedP: %8ld\n", state.stat.ets_missedP);
+
+ printf("packetR: %8ld\t", state.stat.ets_packetR);
+ printf("packetT: %8ld\t", state.stat.ets_packetT);
+ printf("transDef: %8ld\n", state.stat.ets_transDef);
+
+ printf("collision: %8ld\t", state.stat.ets_collision);
+ printf("transAb: %8ld\t", state.stat.ets_transAb);
+ printf("carrSense: %8ld\n", state.stat.ets_carrSense);
+
+ printf("fifoUnder: %8ld\t", state.stat.ets_fifoUnder);
+ printf("fifoOver: %8ld\t", state.stat.ets_fifoOver);
+ printf("CDheartbeat: %8ld\n", state.stat.ets_CDheartbeat);
+
+ printf("OWC: %8ld\t", state.stat.ets_OWC);
+ printf("TxD tail: %8d\t", state.txd_tail);
+ printf("TxD count: %8d\n", state.txd_num);
+
+ printf("RxD tail: %8d\t", state.rxd_tail);
+ printf("TxS tail: %8d\t", state.txs_tail);
+ printf("TxS count: %8d\n", state.txs_num);
+
+ printf("flags: 0x%04x\t", state.flags);
+ atl2_dump_link();
+ printf("\n");
+}
+
+/*===========================================================================*
+ * sef_local_startup *
+ *===========================================================================*/
+PRIVATE void sef_local_startup(void)
+{
+ /* Initialize the System Event Framework.
+ */
+
+ /* No support for live update yet. */
+ sef_startup();
+}
+
+/*===========================================================================*
+ * main *
+ *===========================================================================*/
+int main(int argc, char **argv)
+{
+ /* Driver task.
+ */
+ u32_t inet_endpt;
+ message m;
+ sigset_t set;
+ int r, devind;
+ long instance;
+#if ATL2_FKEY
+ int fkeys, sfkeys;
+#endif
+
+ /* Initialize SEF. */
+ sef_local_startup();
+
+ /* How many matching devices should we skip? */
+ instance = 0;
+ env_setargs(argc, argv);
+ env_parse("atl2_instance", "d", 0, &instance, 0, 32);
+
+ /* Try to find a recognized device. */
+ devind = atl2_probe(instance);
+
+ if (devind < 0)
+ panic("atl2", "no matching device found", NO_NUM);
+
+ /* Initialize the device. */
+ atl2_init(devind);
+
+ /* Notify Inet of our presence, if it has already been started. */
+ r = ds_retrieve_u32("inet", &inet_endpt);
+ if (r == OK)
+ notify(inet_endpt);
+ else if (r != ESRCH)
+ printf("ATL2: ds_retrieve_u32 failed for 'inet': %d\n", r);
+
+#if ATL2_FKEY
+ /* Register debug dump function key. */
+ fkeys = sfkeys = 0;
+ bit_set(sfkeys, 11);
+ if ((r = fkey_map(&fkeys, &sfkeys)) != OK)
+ printf("ATL2: warning, could not map Shift+F11 key (%d)\n", r);
+#endif
+
+ while (TRUE) {
+ if ((r = sef_receive(ANY, &m)) != OK)
+ panic("atl2", "sef_receive failed", r);
+
+ if (is_notify(m.m_type)) {
+ switch (m.m_source) {
+ case HARDWARE: /* interrupt */
+ atl2_intr(&m);
+
+ break;
+
+ case PM_PROC_NR: /* signal */
+ if (getsigset(&set) != 0) break;
+
+ if (sigismember(&set, SIGTERM))
+ atl2_shutdown();
+
+ break;
+
+ case TTY_PROC_NR: /* function key */
+ atl2_dump();
+
+ break;
+
+ default:
+ printf("ATL2: illegal notify from %d\n",
+ m.m_source);
+ }
+
+ continue;
+ }
+
+ /* Process requests from Inet. */
+ switch (m.m_type) {
+ case DL_GETNAME: atl2_getname(&m, instance); break;
+ case DL_CONF: atl2_conf(&m); break;
+ case DL_GETSTAT_S: atl2_getstat(&m); break;
+ case DL_WRITEV_S: atl2_writev(&m, FALSE); break;
+ case DL_READV_S: atl2_readv(&m, FALSE); break;
+ default:
+ printf("ATL2: illegal message %d from %d\n",
+ m.m_type, m.m_source);
+ }
+ }
+}
--- /dev/null
+/* Attansic/Atheros L2 FastEthernet driver, by D.C. van Moolenbroek */
+
+#define ATL2_MMAP_SIZE 0x40000 /* memory-mapped registers */
+
+/* The first three are configurable to a certain extent; the last is not. */
+#define ATL2_TXD_BUFSIZE 8192 /* TxD ring buffer size */
+#define ATL2_TXS_COUNT 64 /* Tx status ring array size */
+#define ATL2_RXD_COUNT 64 /* Rx descriptors */
+#define ATL2_RXD_SIZE 1536 /* Rx element size */
+
+#define ATL2_MASTER_REG 0x1400 /* master register */
+# define ATL2_MASTER_SOFT_RESET 0x00000001 /* soft reset */
+# define ATL2_MASTER_IMT_EN 0x00000004 /* IMT enabled */
+
+#define ATL2_RESET_NTRIES 100 /* #tries to wait for reset */
+#define ATL2_RESET_DELAY 10 /* delay (us) between tries */
+
+#define ATL2_PHY_ENABLE_REG 0x140c /* PHY enable register */
+# define ATL2_PHY_ENABLE 1 /* enable PHY */
+
+#define ATL2_IDLE_REG 0x1410 /* idle status register */
+
+#define ATL2_IDLE_NTRIES 100 /* #tries to wait for idle */
+#define ATL2_IDLE_DELAY 100 /* delay (us) between tries */
+
+#define ATL2_HWADDR0_REG 0x1488 /* Hardware address (part 0) */
+#define ATL2_HWADDR1_REG 0x148c /* Hardware address (part 1) */
+
+#define ATL2_ISR_REG 0x1600 /* interrupt status register */
+# define ATL2_ISR_RXF_OVERFLOW 0x00000004 /* RxF overflow */
+# define ATL2_ISR_TXF_UNDERRUN 0x00000008 /* TxF underrun */
+# define ATL2_ISR_TXS_OVERFLOW 0x00000010 /* TxS overflow */
+# define ATL2_ISR_RXS_OVERFLOW 0x00000020 /* RxS overflow */
+# define ATL2_ISR_TXD_UNDERRUN 0x00000080 /* TxD underrun */
+# define ATL2_ISR_RXD_OVERFLOW 0x00000100 /* RxD overflow */
+# define ATL2_ISR_DMAR_TIMEOUT 0x00000200 /* DMA read timeout */
+# define ATL2_ISR_DMAW_TIMEOUT 0x00000400 /* DMA write timeout */
+# define ATL2_ISR_TXS_UPDATED 0x00010000 /* Tx status updated */
+# define ATL2_ISR_RXD_UPDATED 0x00020000 /* Rx status updated */
+# define ATL2_ISR_TX_EARLY 0x00040000 /* Tx started xmit */
+# define ATL2_ISR_PHY_LINKDOWN 0x10000000 /* PHY link down */
+# define ATL2_ISR_DISABLE 0x80000000 /* disable intrs */
+# define ATL2_ISR_TX_EVENT (ATL2_ISR_TXF_UNDERRUN | \
+ ATL2_ISR_TXS_OVERFLOW | \
+ ATL2_ISR_TXD_UNDERRUN | \
+ ATL2_ISR_TXS_UPDATED | \
+ ATL2_ISR_TX_EARLY)
+# define ATL2_ISR_RX_EVENT (ATL2_ISR_RXF_OVERFLOW | \
+ ATL2_ISR_RXS_OVERFLOW | \
+ ATL2_ISR_RXD_OVERFLOW | \
+ ATL2_ISR_RXD_UPDATED)
+
+#define ATL2_IMR_REG 0x1604 /* interrupt mask register */
+# define ATL2_IMR_DEFAULT (ATL2_ISR_DMAR_TIMEOUT | \
+ ATL2_ISR_DMAW_TIMEOUT | \
+ ATL2_ISR_TXS_UPDATED | \
+ ATL2_ISR_RXD_UPDATED | \
+ ATL2_ISR_PHY_LINKDOWN)
+
+#define ATL2_MAC_REG 0x1480 /* MAC config register */
+# define ATL2_MAC_TX_EN 0x00000001 /* enable transmit */
+# define ATL2_MAC_RX_EN 0x00000002 /* enable receive */
+# define ATL2_MAC_PROMISC_EN 0x00008000 /* promiscuous */
+# define ATL2_MAC_MCAST_EN 0x02000000 /* multicast */
+# define ATL2_MAC_BCAST_EN 0x04000000 /* broadcast */
+# define ATL2_MAC_DEFAULT 0x28001cec /* (magic) */
+
+#define ATL2_MHT0_REG 0x1490 /* multicast hash table bits */
+#define ATL2_MHT1_REG 0x1494 /* 64 slots in total */
+
+#define ATL2_DMAREAD_REG 0x1580 /* read DMA config register */
+# define ATL2_DMAREAD_EN 1 /* read DMA enabled */
+#define ATL2_DMAWRITE_REG 0x15a0 /* write DMA config register */
+# define ATL2_DMAWRITE_EN 1 /* write DMA enabled */
+
+#define ATL2_DESC_ADDR_HI_REG 0x1540 /* high 32 bits of addresses */
+#define ATL2_TXD_ADDR_LO_REG 0x1544 /* low 32 bits of TxD base */
+#define ATL2_TXD_BUFSIZE_REG 0x1548 /* size of TxD ring buffer */
+#define ATL2_TXS_ADDR_LO_REG 0x154c /* low 32 bits of TxS base */
+#define ATL2_TXS_COUNT_REG 0x1550 /* number of TxS descriptors */
+#define ATL2_RXD_ADDR_LO_REG 0x1554 /* low 32 bits of RxD base */
+#define ATL2_RXD_COUNT_REG 0x1558 /* number of RxD descriptors */
+
+#define ATL2_IFG_REG 0x1484 /* inter-frame gap config */
+# define ATL2_IFG_DEFAULT 0x60405060 /* (magic) */
+
+#define ATL2_HDPX_REG 0x1498 /* half-duplex mode config */
+# define ATL2_HDPX_DEFAULT 0x07a1f037 /* (magic) */
+
+#define ATL2_IMT_REG 0x1408 /* intr moderation timer */
+# define ATL2_IMT_DEFAULT 100 /* 200 us */
+
+#define ATL2_ICT_REG 0x140e /* intr clearing timer */
+# define ATL2_ICT_DEFAULT 50000 /* 100 ms */
+
+#define ATL2_MTU_REG 0x149c /* MTU config */
+# define ATL2_MTU_DEFAULT ETH_MAX_PACK_SIZE_TAGGED
+
+#define ATL2_CUT_THRESH_REG 0x1590 /* cut-through config */
+# define ATL2_CUT_THRESH_DEFAULT 0x177 /* (magic) */
+
+#define ATL2_FLOW_THRESH_HI_REG 0x15a8 /* RxD overflow hi watermark */
+#define ATL2_FLOW_THRESH_LO_REG 0x15aa /* RxD overflow lo watermark */
+
+#define ATL2_TXD_IDX_REG 0x15f0 /* TxD read index */
+#define ATL2_RXD_IDX_REG 0x15f4 /* RxD write index */
+
+#define ATL2_LTSSM_TESTMODE_REG 0x12fc /* PCIE configuration */
+#define ATL2_LTSSM_TESTMODE_DEFAULT 0x6500 /* (magic) */
+#define ATL2_DLL_TX_CTRL_REG 0x1104 /* PCIE configuration */
+#define ATL2_DLL_TX_CTRL_DEFAULT 0x0568 /* (magic) */
+
+#define ATL2_VPD_CAP_REG 0x6c /* VPD command register */
+# define ATL2_VPD_CAP_ADDR_SHIFT 16
+# define ATL2_VPD_CAP_ADDR_MASK 0x7fff0000
+# define ATL2_VPD_CAP_DONE 0x80000000
+#define ATL2_VPD_DATA_REG 0x70 /* VPD data register */
+
+#define ATL2_SPICTL_REG 0x200 /* SPI control register */
+# define ATL2_SPICTL_VPD_EN 0x2000 /* enable VPD */
+
+#define ATL2_VPD_REGBASE 0x100 /* VPD register base */
+#define ATL2_VPD_NREGS 64 /* number of VPD registers */
+#define ATL2_VPD_SIG_MASK 0xff /* signature mask */
+#define ATL2_VPD_SIG 0x5a /* VPD entry signature */
+#define ATL2_VPD_REG_SHIFT 16 /* key shift */
+
+#define ATL2_VPD_NTRIES 10 /* #tries to read from VPD */
+#define ATL2_VPD_DELAY 2000 /* delay (us) between tries */
+
+#define ATL2_MDIO_REG 0x1414 /* Management Data I/O reg */
+# define ATL2_MDIO_ADDR_SHIFT 16 /* register address */
+# define ATL2_MDIO_ADDR_MASK 0x001f0000 /* (shift and mask) */
+# define ATL2_MDIO_READ 0x00200000 /* read operation */
+# define ATL2_MDIO_SUP_PREAMBLE 0x00400000 /* suppress preamble */
+# define ATL2_MDIO_START 0x00800000 /* initiate xfer */
+# define ATL2_MDIO_CLK_25_4 0x00000000 /* 25MHz, 4bit */
+# define ATL2_MDIO_BUSY 0x08000000 /* in progress */
+# define ATL2_MDIO_DATA_MASK 0x0000ffff /* result data mask */
+
+#define ATL2_MDIO_NTRIES 10 /* #tries to access MDIO */
+#define ATL2_MDIO_DELAY 2 /* delay (us) between tries */
+
+#define ATL2_MII_BMSR 1 /* basic mode status reg */
+# define ATL2_MII_BMSR_LSTATUS 0x0004 /* link status */
+#define ATL2_MII_PSSR 17 /* PHY specific status reg */
+# define ATL2_MII_PSSR_RESOLVED 0x0800 /* speed/duplex OK */
+# define ATL2_MII_PSSR_DUPLEX 0x2000 /* full duplex */
+# define ATL2_MII_PSSR_SPEED 0xc000 /* link speed */
+# define ATL2_MII_PSSR_10 0x0000 /* 10Mbps */
+# define ATL2_MII_PSSR_100 0x4000 /* 100Mbps */
+# define ATL2_MII_PSSR_1000 0x8000 /* 1000Mbps */
+
+#define ATL2_RXD_SIZE_MASK 0x000007ff /* packet size mask */
+#define ATL2_RXD_SUCCESS 0x00010000 /* successful receipt */
+#define ATL2_RXD_BCAST 0x00020000 /* broadcast frame */
+#define ATL2_RXD_MCAST 0x00040000 /* multicast frame */
+#define ATL2_RXD_PAUSE 0x00080000 /* pause frame */
+#define ATL2_RXD_CTRL 0x00100000 /* control frame */
+#define ATL2_RXD_CRCERR 0x00200000 /* invalid frame CRC */
+#define ATL2_RXD_CODEERR 0x00400000 /* invalid opcode */
+#define ATL2_RXD_RUNT 0x00800000 /* short frame */
+#define ATL2_RXD_FRAG 0x01000000 /* collision fragment */
+#define ATL2_RXD_TRUNC 0x02000000 /* frame truncated */
+#define ATL2_RXD_ALIGN 0x04000000 /* frame align error */
+#define ATL2_RXD_UPDATE 0x80000000 /* updated by device */
+
+#define ATL2_TXS_SIZE_MASK 0x000007ff /* packet size mask */
+#define ATL2_TXS_SUCCESS 0x00010000 /* successful xmit */
+#define ATL2_TXS_BCAST 0x00020000 /* broadcast frame */
+#define ATL2_TXS_MCAST 0x00040000 /* multicast frame */
+#define ATL2_TXS_PAUSE 0x00080000 /* pause frame */
+#define ATL2_TXS_CTRL 0x00100000 /* control frame */
+#define ATL2_TXS_DEFER 0x00200000 /* deferred transmit */
+#define ATL2_TXS_EXCDEFER 0x00400000 /* excess defer */
+#define ATL2_TXS_SINGLECOL 0x00800000 /* single collision */
+#define ATL2_TXS_MULTICOL 0x01000000 /* multi collisions */
+#define ATL2_TXS_LATECOL 0x02000000 /* late collision */
+#define ATL2_TXS_ABORTCOL 0x04000000 /* collision abort */
+#define ATL2_TXS_UNDERRUN 0x08000000 /* buffer underrun */
+#define ATL2_TXS_UPDATE 0x80000000 /* updated by device */
+
+