diff --git a/commands/scripts/netconf.sh b/commands/scripts/netconf.sh index e4c6c53be..0c7200a91 100755 --- a/commands/scripts/netconf.sh +++ b/commands/scripts/netconf.sh @@ -110,7 +110,8 @@ cards() card 6 "NE2000, 3com 503 or WD based card (also emulated by Bochs)" card 7 "AMD LANCE (also emulated by VMWare and VirtualBox)" "1022:2000" card 8 "Intel PRO/1000 Gigabit" "8086:100E" "8086:107C" "8086:10CD" - card 9 "Different Ethernet card (no networking)" + card 9 "Attansic/Atheros L2 FastEthernet" "1969:2048" + card 10 "Different Ethernet card (no networking)" } warn() @@ -157,7 +158,8 @@ drv_params() ;; 7) driver="lance"; ;; 8) driver="e1000"; ;; - 9) driver="psip0"; ;; + 9) driver="atl2"; ;; + 10) driver="psip0"; ;; *) warn "choose a number" esac } diff --git a/drivers/Makefile b/drivers/Makefile index 4e3156125..d8f34c8a9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -27,6 +27,7 @@ all install depend clean: cd ./orinoco && $(MAKE) $@ cd ./fxp && $(MAKE) $@ cd ./dpeth && $(MAKE) $@ + cd ./atl2 && $(MAKE) $@ cd ./log && $(MAKE) $@ cd ./bios_wini && $(MAKE) $@ cd ./filter && $(MAKE) $@ diff --git a/drivers/atl2/Makefile b/drivers/atl2/Makefile new file mode 100644 index 000000000..e97dfd40e --- /dev/null +++ b/drivers/atl2/Makefile @@ -0,0 +1,40 @@ +# Makefile for the Attansic/Atheros L2 ethernet driver (ATL2) +DRIVER = atl2 + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = .. + +# programs, flags, etc. +MAKE = exec make +CC = exec cc +CFLAGS = -I$i $(CPROFILE) +LDFLAGS = -i +LIBS = -lsys + +OBJ = atl2.o + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + +# install with other drivers +install: /usr/sbin/$(DRIVER) +/usr/sbin/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(DRIVER) *.o *.bak + +depend: + mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend + +# Include generated dependencies. +include .depend + diff --git a/drivers/atl2/atl2.c b/drivers/atl2/atl2.c new file mode 100644 index 000000000..e57a8983f --- /dev/null +++ b/drivers/atl2/atl2.c @@ -0,0 +1,1343 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#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 : "", 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); + } + } +} diff --git a/drivers/atl2/atl2.h b/drivers/atl2/atl2.h new file mode 100644 index 000000000..06812bcd0 --- /dev/null +++ b/drivers/atl2/atl2.h @@ -0,0 +1,183 @@ +/* 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 */ + + diff --git a/drivers/pci/pci_table.c b/drivers/pci/pci_table.c index a49f16f6f..3f8702d5a 100644 --- a/drivers/pci/pci_table.c +++ b/drivers/pci/pci_table.c @@ -44,6 +44,7 @@ struct pci_vendor pci_vendor_table[]= { 0x1385, "Netgear" }, { 0x16ec, "US Robotics" }, { 0x1737, "Linksys" }, + { 0x1969, "Atheros Communications" }, { 0x5333, "S3" }, { 0x8086, "Intel" }, { 0x9004, "Adaptec" }, @@ -124,6 +125,7 @@ struct pci_device pci_device_table[]= { 0x1385, 0x8169, "Netgear Gigabit Ethernet Adapter" }, { 0x16ec, 0x0116, "US Robotics Realtek 8169S chip" }, { 0x1737, 0x1032, "Linksys Instant Gigabit Desktop Network Interface" }, + { 0x1969, 0x2048, "Atheros L2 Fast Ethernet Controller" }, { 0x5333, 0x8811, "S3 86c764/765 [Trio32/64/64V+]" }, { 0x5333, 0x883d, "S3 Virge/VX" }, { 0x5333, 0x88d0, "S3 Vision 964 vers 0" }, diff --git a/etc/system.conf b/etc/system.conf index 055831a37..eef96fce9 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -534,3 +534,22 @@ service e1000 SYSTEM PM RS LOG TTY DS VM pci inet ; }; + +service atl2 +{ + system + UMAP # 14 + IRQCTL # 19 + TIMES # 25 + GETINFO # 26 + SAFECOPYFROM # 31 + SAFECOPYTO # 32 + SETGRANT # 34 + SYSCTL # 44 + ; + pci device 1969/2048; + ipc + SYSTEM PM RS TTY DS VM + pci inet + ; +}; diff --git a/etc/usr/rc b/etc/usr/rc index 295fe7ef0..da71e108d 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -91,7 +91,7 @@ start) fi # start only network drivers that are in use - for driver in lance rtl8139 rtl8169 fxp e1000 dpeth dp8390 orinoco + for driver in lance rtl8139 rtl8169 fxp e1000 dpeth dp8390 orinoco atl2 do if grep " $driver " /etc/inet.conf > /dev/null 2>&1 then