From 8e9ff38303b83909b6d9b84bb775d82c64b80d52 Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Sun, 30 Nov 2008 18:16:19 +0000 Subject: [PATCH] Update Attansic L2 driver with latest source from FreeBSD, it is now fully functional! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28752 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- build/jam/HaikuImage | 3 +- .../network/attansic_l2/dev/ae/if_ae.c | 1070 +++++++++++------ .../network/attansic_l2/dev/ae/if_aereg.h | 72 +- .../network/attansic_l2/dev/ae/if_aevar.h | 39 +- 4 files changed, 794 insertions(+), 390 deletions(-) diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index cd7c283127..5c8a4e1b03 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -143,7 +143,8 @@ BEOS_ADD_ONS_DRIVERS_NET = $(X86_ONLY)3com $(X86_ONLY)broadcom440x pegasus $(X86_ONLY)rtl8139 rtl8169 $(X86_ONLY)rtl81xx sis900 $(X86_ONLY)via_rhine wb840 $(X86_ONLY)ipro100 $(X86_ONLY)nforce #vlance - $(X86_ONLY)marvell_yukon $(X86_ONLY)syskonnect usb_ecm + $(X86_ONLY)marvell_yukon $(X86_ONLY)syskonnect + $(X86_ONLY)attansic_l2 usb_ecm ; #BEOS_ADD_ONS_DRIVERS_ACPI = $(X86_ONLY)acpi_button ; BEOS_ADD_ONS_BUS_MANAGERS = pci $(X86_ONLY)ps2 $(X86_ONLY)isa ide scsi diff --git a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_ae.c b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_ae.c index 36b8d40329..e0e688811e 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_ae.c +++ b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_ae.c @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/ae/if_ae.c,v 1.1.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $"); #include #include @@ -61,26 +61,16 @@ __FBSDID("$FreeBSD$"); #include #include - #include #include #include -#include - -#ifdef __HAIKU__ -#include -#endif #include "miibus_if.h" #include "if_aereg.h" #include "if_aevar.h" -MODULE_DEPEND(ae, pci, 1, 1, 1); -MODULE_DEPEND(ae, ether, 1, 1, 1); -MODULE_DEPEND(ae, miibus, 1, 1, 1); - /* * Devices supported by this driver. */ @@ -92,51 +82,71 @@ static struct ae_dev { { VENDORID_ATTANSIC, DEVICEID_ATTANSIC_L2, "Attansic Technology Corp, L2 FastEthernet" }, }; +#define AE_DEVS_COUNT (sizeof(ae_devs) / sizeof(*ae_devs)) -#define AE_DEVS_COUNT (sizeof(ae_devs) / sizeof(*ae_devs)) +static struct resource_spec ae_res_spec_mem[] = { + { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, + { -1, 0, 0 } +}; +static struct resource_spec ae_res_spec_irq[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; +static struct resource_spec ae_res_spec_msi[] = { + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { -1, 0, 0 } +}; -static int ae_probe(device_t dev); -static int ae_attach(device_t dev); -static void ae_pcie_init(ae_softc_t *sc); -static void ae_phy_reset(ae_softc_t *sc); -static int ae_reset(ae_softc_t *sc); -static void ae_init(void *arg); -static int ae_init_locked(ae_softc_t *sc); -static unsigned int ae_detach(device_t dev); -static int ae_miibus_readreg(device_t dev, int phy, int reg); -static int ae_miibus_writereg(device_t dev, int phy, int reg, int val); -static void ae_miibus_statchg(device_t dev); -static void ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr); -static int ae_mediachange(struct ifnet *ifp); -static void ae_retrieve_address(ae_softc_t *sc); -static void ae_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error); -static int ae_alloc_rings(ae_softc_t *sc); -static void ae_dma_free(ae_softc_t *sc); -static int ae_shutdown(device_t dev); -static int ae_suspend(device_t dev); -static int ae_resume(device_t dev); -static unsigned int ae_tx_avail_size(ae_softc_t *sc); -static int ae_encap(ae_softc_t *sc, struct mbuf **m_head); -static void ae_start(struct ifnet *ifp); -static void ae_link_task(void *arg, int pending); -static void ae_stop_rxmac(ae_softc_t *sc); -static void ae_stop_txmac(ae_softc_t *sc); -static void ae_tx_task(void *arg, int pending); -static void ae_mac_config(ae_softc_t *sc); -static int ae_intr(void *arg); -static void ae_int_task(void *arg, int pending); -static void ae_tx_intr(ae_softc_t *sc); -static int ae_rxeof(ae_softc_t *sc, ae_rxd_t *rxd); -static void ae_rx_intr(ae_softc_t *sc); -static void ae_watchdog(ae_softc_t *sc); -static void ae_tick(void *arg); -static void ae_rxfilter(ae_softc_t *sc); -static int ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); -static void ae_stop(ae_softc_t *sc); -static int ae_check_eeprom_present(ae_softc_t *sc, int *vpdc); -static int ae_vpd_read_word(ae_softc_t *sc, int reg, uint32_t *word); -static int ae_get_vpd_eaddr(ae_softc_t *sc, uint32_t *eaddr); -static int ae_get_reg_eaddr(ae_softc_t *sc, uint32_t *eaddr); +static int ae_probe(device_t dev); +static int ae_attach(device_t dev); +static void ae_pcie_init(ae_softc_t *sc); +static void ae_phy_reset(ae_softc_t *sc); +static void ae_phy_init(ae_softc_t *sc); +static int ae_reset(ae_softc_t *sc); +static void ae_init(void *arg); +static int ae_init_locked(ae_softc_t *sc); +static unsigned int ae_detach(device_t dev); +static int ae_miibus_readreg(device_t dev, int phy, int reg); +static int ae_miibus_writereg(device_t dev, int phy, int reg, int val); +static void ae_miibus_statchg(device_t dev); +static void ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr); +static int ae_mediachange(struct ifnet *ifp); +static void ae_retrieve_address(ae_softc_t *sc); +static void ae_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, + int error); +static int ae_alloc_rings(ae_softc_t *sc); +static void ae_dma_free(ae_softc_t *sc); +static int ae_shutdown(device_t dev); +static int ae_suspend(device_t dev); +static void ae_powersave_disable(ae_softc_t *sc); +static void ae_powersave_enable(ae_softc_t *sc); +static int ae_resume(device_t dev); +static unsigned int ae_tx_avail_size(ae_softc_t *sc); +static int ae_encap(ae_softc_t *sc, struct mbuf **m_head); +static void ae_start(struct ifnet *ifp); +static void ae_link_task(void *arg, int pending); +static void ae_stop_rxmac(ae_softc_t *sc); +static void ae_stop_txmac(ae_softc_t *sc); +static void ae_tx_task(void *arg, int pending); +static void ae_mac_config(ae_softc_t *sc); +static int ae_intr(void *arg); +static void ae_int_task(void *arg, int pending); +static void ae_tx_intr(ae_softc_t *sc); +static int ae_rxeof(ae_softc_t *sc, ae_rxd_t *rxd); +static void ae_rx_intr(ae_softc_t *sc); +static void ae_watchdog(ae_softc_t *sc); +static void ae_tick(void *arg); +static void ae_rxfilter(ae_softc_t *sc); +static void ae_rxvlan(ae_softc_t *sc); +static int ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +static void ae_stop(ae_softc_t *sc); +static int ae_check_eeprom_present(ae_softc_t *sc, int *vpdc); +static int ae_vpd_read_word(ae_softc_t *sc, int reg, uint32_t *word); +static int ae_get_vpd_eaddr(ae_softc_t *sc, uint32_t *eaddr); +static int ae_get_reg_eaddr(ae_softc_t *sc, uint32_t *eaddr); +static void ae_update_stats_rx(uint16_t flags, ae_stats_t *stats); +static void ae_update_stats_tx(uint16_t flags, ae_stats_t *stats); +static void ae_init_tunables(ae_softc_t *sc); static device_method_t ae_methods[] = { /* Device interface. */ @@ -154,59 +164,98 @@ static device_method_t ae_methods[] = { { NULL, NULL } }; - static driver_t ae_driver = { "ae", ae_methods, sizeof(ae_softc_t) }; - static devclass_t ae_devclass; DRIVER_MODULE(ae, pci, ae_driver, ae_devclass, 0, 0); DRIVER_MODULE(miibus, ae, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(ae, pci, 1, 1, 1); +MODULE_DEPEND(ae, ether, 1, 1, 1); +MODULE_DEPEND(ae, miibus, 1, 1, 1); -static struct resource_spec ae_res_spec_mem[] = { - { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, - { -1, 0, 0 } -}; - -static struct resource_spec ae_res_spec_irq[] = { - { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, - { -1, 0, 0 } -}; +/* + * Tunables. + */ +static int msi_disable = 0; +TUNABLE_INT("hw.ae.msi_disable", &msi_disable); #define AE_READ_4(sc, reg) \ bus_read_4((sc)->mem[0], (reg)) - #define AE_READ_2(sc, reg) \ bus_read_2((sc)->mem[0], (reg)) - #define AE_READ_1(sc, reg) \ bus_read_1((sc)->mem[0], (reg)) - #define AE_WRITE_4(sc, reg, val) \ bus_write_4((sc)->mem[0], (reg), (val)) - #define AE_WRITE_2(sc, reg, val) \ bus_write_2((sc)->mem[0], (reg), (val)) - -#define AE_WRITE_1(sc, reg, val) \ +#define AE_WRITE_1(sc, reg, val) \ bus_write_1((sc)->mem[0], (reg), (val)) - +#define AE_PHY_READ(sc, reg) \ + ae_miibus_readreg(sc->dev, 0, reg) +#define AE_PHY_WRITE(sc, reg, val) \ + ae_miibus_writereg(sc->dev, 0, reg, val) #define AE_CHECK_EADDR_VALID(eaddr) \ ((eaddr[0] == 0 && eaddr[1] == 0) || \ (eaddr[0] == 0xffffffff && eaddr[1] == 0xffff)) +#define AE_RXD_VLAN(vtag) \ + (((vtag) >> 4) | (((vtag) & 0x07) << 13) | (((vtag) & 0x08) << 9)) +#define AE_TXD_VLAN(vtag) \ + (((vtag) << 4) | (((vtag) >> 13) & 0x07) | (((vtag) >> 9) & 0x08)) + +/* + * ae statistics. + */ +#define STATS_ENTRY(node, desc, field) \ + { node, desc, offsetof(struct ae_stats, field) } +struct { + const char *node; + const char *desc; + intptr_t offset; +} ae_stats_tx[] = { + STATS_ENTRY("bcast", "broadcast frames", tx_bcast), + STATS_ENTRY("mcast", "multicast frames", tx_mcast), + STATS_ENTRY("pause", "PAUSE frames", tx_pause), + STATS_ENTRY("control", "control frames", tx_ctrl), + STATS_ENTRY("defers", "deferrals occuried", tx_defer), + STATS_ENTRY("exc_defers", "excessive deferrals occuried", tx_excdefer), + STATS_ENTRY("singlecols", "single collisions occuried", tx_singlecol), + STATS_ENTRY("multicols", "multiple collisions occuried", tx_multicol), + STATS_ENTRY("latecols", "late collisions occuried", tx_latecol), + STATS_ENTRY("aborts", "transmit aborts due collisions", tx_abortcol), + STATS_ENTRY("underruns", "Tx FIFO underruns", tx_underrun) +}, ae_stats_rx[] = { + STATS_ENTRY("bcast", "broadcast frames", rx_bcast), + STATS_ENTRY("mcast", "multicast frames", rx_mcast), + STATS_ENTRY("pause", "PAUSE frames", rx_pause), + STATS_ENTRY("control", "control frames", rx_ctrl), + STATS_ENTRY("crc_errors", "frames with CRC errors", rx_crcerr), + STATS_ENTRY("code_errors", "frames with invalid opcode", rx_codeerr), + STATS_ENTRY("runt", "runt frames", rx_runt), + STATS_ENTRY("frag", "fragmented frames", rx_frag), + STATS_ENTRY("align_errors", "frames with alignment errors", rx_align), + STATS_ENTRY("truncated", "frames truncated due to Rx FIFO inderrun", + rx_trunc) +}; +#define AE_STATS_RX_LEN (sizeof(ae_stats_rx) / sizeof(*ae_stats_rx)) +#define AE_STATS_TX_LEN (sizeof(ae_stats_tx) / sizeof(*ae_stats_tx)) static int ae_probe(device_t dev) { - uint16_t vendorid, deviceid; + uint16_t deviceid, vendorid; int i; vendorid = pci_get_vendor(dev); deviceid = pci_get_device(dev); + /* + * Search through the list of supported devs for matching one. + */ for (i = 0; i < AE_DEVS_COUNT; i++) { if (vendorid == ae_devs[i].vendorid && deviceid == ae_devs[i].deviceid) { @@ -214,7 +263,6 @@ ae_probe(device_t dev) return (BUS_PROBE_DEFAULT); } } - return (ENXIO); } @@ -223,67 +271,94 @@ ae_attach(device_t dev) { ae_softc_t *sc; struct ifnet *ifp; - int error; uint8_t chiprev; uint32_t pcirev; + int nmsi, pmc; + int error; sc = device_get_softc(dev); /* Automatically allocated and zeroed on attach. */ - - KASSERT(sc != NULL, ("[ae, %d]: sc is null", __LINE__)); + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); sc->dev = dev; - /* Initialize per-device mutex. */ + /* + * Initialize mutexes and tasks. + */ mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); - callout_init_mtx(&sc->tick_ch, &sc->mtx, 0); TASK_INIT(&sc->int_task, 0, ae_int_task, sc); - TASK_INIT(&sc->link_task, 0, ae_link_task, sc); + TASK_INIT(&sc->link_task, 0, ae_link_task, sc); - /* Enable bus mastering. */ - pci_enable_busmaster(dev); + pci_enable_busmaster(dev); /* Enable bus mastering. */ sc->spec_mem = ae_res_spec_mem; - sc->spec_irq = ae_res_spec_irq; - /* Allocate memory registers. */ + /* + * Allocate memory-mapped registers. + */ error = bus_alloc_resources(dev, sc->spec_mem, sc->mem); if (error != 0) { device_printf(dev, "could not allocate memory resources.\n"); + sc->spec_mem = NULL; goto fail; } - /* Retrieve PCI and chip revisions. */ + /* + * Retrieve PCI and chip revisions. + */ pcirev = pci_get_revid(dev); chiprev = (AE_READ_4(sc, AE_MASTER_REG) >> AE_MASTER_REVNUM_SHIFT) & AE_MASTER_REVNUM_MASK; - if (bootverbose || 1) { + if (bootverbose) { device_printf(dev, "pci device revision: %#04x\n", pcirev); device_printf(dev, "chip id: %#02x\n", chiprev); } + nmsi = pci_msi_count(dev); + if (bootverbose) + device_printf(dev, "MSI count: %d.\n", nmsi); - /* Allocate IRQ resources. */ - error = bus_alloc_resources(dev, sc->spec_irq, sc->irq); - if (error != 0) { - device_printf(dev, "could not allocate IRQ resources.\n"); - goto fail; + /* + * Allocate interrupt resources. + */ + if (msi_disable == 0 && nmsi == 1) { + error = pci_alloc_msi(dev, &nmsi); + if (error == 0) { + device_printf(dev, "Using MSI messages.\n"); + sc->spec_irq = ae_res_spec_msi; + error = bus_alloc_resources(dev, sc->spec_irq, sc->irq); + if (error != 0) { + device_printf(dev, "MSI allocation failed.\n"); + sc->spec_irq = NULL; + pci_release_msi(dev); + } else { + sc->flags |= AE_FLAG_MSI; + } + } } + if (sc->spec_irq == NULL) { + sc->spec_irq = ae_res_spec_irq; + error = bus_alloc_resources(dev, sc->spec_irq, sc->irq); + if (error != 0) { + device_printf(dev, "could not allocate IRQ resources.\n"); + sc->spec_irq = NULL; + goto fail; + } + } + + ae_init_tunables(sc); - /* Reset PHY */ - AE_LOCK(sc); - ae_phy_reset(sc); - AE_UNLOCK(sc); - - /* Reset the controller. */ - error = ae_reset(sc); + ae_phy_reset(sc); /* Reset PHY. */ + error = ae_reset(sc); /* Reset the controller itself. */ if (error != 0) goto fail; - /* Initialize PCIE controller. */ ae_pcie_init(sc); - /* Load MAC address. */ - ae_retrieve_address(sc); + ae_retrieve_address(sc); /* Load MAC address. */ + + error = ae_alloc_rings(sc); /* Allocate ring buffers. */ + if (error != 0) + goto fail; /* Set default PHY address. */ sc->phyaddr = AE_PHYADDR_DEFAULT; @@ -300,14 +375,18 @@ ae_attach(device_t dev) ifp->if_ioctl = ae_ioctl; ifp->if_start = ae_start; ifp->if_init = ae_init; - ifp->if_capabilities = IFCAP_VLAN_MTU; - ifp->if_capenable = ifp->if_capabilities; + ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; ifp->if_hwassist = 0; ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); + if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0) + sc->flags |= AE_FLAG_PMG; + ifp->if_capenable = ifp->if_capabilities; - /* Configure and attach MII bus. */ + /* + * Configure and attach MII bus. + */ error = mii_phy_probe(dev, &sc->miibus, ae_mediachange, ae_mediastatus); if (error != 0) { @@ -316,7 +395,12 @@ ae_attach(device_t dev) } ether_ifattach(ifp, sc->eaddr); + /* Tell the upper layer(s) we support long frames. */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + /* + * Create and run all helper tasks. + */ TASK_INIT(&sc->tx_task, 1, ae_tx_task, ifp); sc->tq = taskqueue_create_fast("ae_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->tq); @@ -326,10 +410,12 @@ ae_attach(device_t dev) error = ENXIO; goto fail; } - taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->dev)); + /* + * Configure interrupt handlers. + */ error = bus_setup_intr(dev, sc->irq[0], INTR_TYPE_NET | INTR_MPSAFE, ae_intr, NULL, sc, &sc->intrhand); if (error != 0) { @@ -347,16 +433,55 @@ fail: return (error); } +static void +ae_init_tunables(ae_softc_t *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *root, *stats, *stats_rx, *stats_tx; + struct ae_stats *ae_stats; + unsigned int i; + + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); + ae_stats = &sc->stats; + + ctx = device_get_sysctl_ctx(sc->dev); + root = device_get_sysctl_tree(sc->dev); + stats = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "stats", + CTLFLAG_RD, NULL, "ae statistics"); + + /* + * Receiver statistcics. + */ + stats_rx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "rx", + CTLFLAG_RD, NULL, "Rx MAC statistics"); + for (i = 0; i < AE_STATS_RX_LEN; i++) + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_rx), OID_AUTO, + ae_stats_rx[i].node, CTLFLAG_RD, (char *)ae_stats + + ae_stats_rx[i].offset, 0, ae_stats_rx[i].desc); + + /* + * Receiver statistcics. + */ + stats_tx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "tx", + CTLFLAG_RD, NULL, "Tx MAC statistics"); + for (i = 0; i < AE_STATS_TX_LEN; i++) + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_tx), OID_AUTO, + ae_stats_tx[i].node, CTLFLAG_RD, (char *)ae_stats + + ae_stats_tx[i].offset, 0, ae_stats_tx[i].desc); +} + static void ae_pcie_init(ae_softc_t *sc) { - AE_WRITE_4(sc, AE_LTSSM_TESTMODE_REG, AE_LTSSM_TESTMODE_DEFAULT); - AE_WRITE_4(sc, AE_DLL_TX_REG, AE_DLL_TX_DEFAULT); + + AE_WRITE_4(sc, AE_PCIE_LTSSM_TESTMODE_REG, AE_PCIE_LTSSM_TESTMODE_DEFAULT); + AE_WRITE_4(sc, AE_PCIE_DLL_TX_CTRL_REG, AE_PCIE_DLL_TX_CTRL_DEFAULT); } static void ae_phy_reset(ae_softc_t *sc) { + AE_WRITE_4(sc, AE_PHY_ENABLE_REG, AE_PHY_ENABLE); DELAY(1000); /* XXX: pause(9) ? */ } @@ -366,34 +491,38 @@ ae_reset(ae_softc_t *sc) { int i; - /* Issue a soft reset. */ + /* + * Issue a soft reset. + */ AE_WRITE_4(sc, AE_MASTER_REG, AE_MASTER_SOFT_RESET); bus_barrier(sc->mem[0], AE_MASTER_REG, 4, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + /* + * Wait for reset to complete. + */ for (i = 0; i < AE_RESET_TIMEOUT; i++) { if ((AE_READ_4(sc, AE_MASTER_REG) & AE_MASTER_SOFT_RESET) == 0) break; DELAY(10); } - if (i == AE_RESET_TIMEOUT) { device_printf(sc->dev, "reset timeout.\n"); return (ENXIO); } - /* Wait for everything to enter idle state */ + /* + * Wait for everything to enter idle state. + */ for (i = 0; i < AE_IDLE_TIMEOUT; i++) { if (AE_READ_4(sc, AE_IDLE_REG) == 0) break; DELAY(100); } - if (i == AE_IDLE_TIMEOUT) { device_printf(sc->dev, "could not enter idle state.\n"); return (ENXIO); } - return (0); } @@ -408,6 +537,19 @@ ae_init(void *arg) AE_UNLOCK(sc); } +static void +ae_phy_init(ae_softc_t *sc) +{ + + /* + * Enable link status change interrupt. + * XXX magic numbers. + */ +#ifdef notyet + AE_PHY_WRITE(sc, 18, 0xc00); +#endif +} + static int ae_init_locked(ae_softc_t *sc) { @@ -422,29 +564,29 @@ ae_init_locked(ae_softc_t *sc) ifp = sc->ifp; mii = device_get_softc(sc->miibus); - /* Stop the device. */ ae_stop(sc); - - /* Reset the adapter. */ ae_reset(sc); - - /* Initialize PCIE module. */ - ae_pcie_init(sc); + ae_pcie_init(sc); /* Initialize PCIE stuff. */ + ae_phy_init(sc); + ae_powersave_disable(sc); - /* Initialize ring buffers. */ - ae_alloc_rings(sc); - - /* Clear and disable interrupts. */ + /* + * Clear and disable interrupts. + */ AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff); - /* Set the MAC address. */ + /* + * Set the MAC address. + */ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); val = eaddr[2] << 24 | eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5]; AE_WRITE_4(sc, AE_EADDR0_REG, val); val = eaddr[0] << 8 | eaddr[1]; AE_WRITE_4(sc, AE_EADDR1_REG, val); - /* Set ring buffers base addresses. */ + /* + * Set ring buffers base addresses. + */ addr = sc->dma_rxd_busaddr; AE_WRITE_4(sc, AE_DESC_ADDR_HI_REG, BUS_ADDR_HI(addr)); AE_WRITE_4(sc, AE_RXD_ADDR_LO_REG, BUS_ADDR_LO(addr)); @@ -453,12 +595,16 @@ ae_init_locked(ae_softc_t *sc) addr = sc->dma_txs_busaddr; AE_WRITE_4(sc, AE_TXS_ADDR_LO_REG, BUS_ADDR_LO(addr)); - /* Configure ring buffers sizes. */ + /* + * Configure ring buffers sizes. + */ AE_WRITE_2(sc, AE_RXD_COUNT_REG, AE_RXD_COUNT_DEFAULT); AE_WRITE_2(sc, AE_TXD_BUFSIZE_REG, AE_TXD_BUFSIZE_DEFAULT / 4); AE_WRITE_2(sc, AE_TXS_COUNT_REG, AE_TXS_COUNT_DEFAULT); - /* Configure interframe gap parameters. */ + /* + * Configure interframe gap parameters. + */ val = ((AE_IFG_TXIPG_DEFAULT << AE_IFG_TXIPG_SHIFT) & AE_IFG_TXIPG_MASK) | ((AE_IFG_RXIPG_DEFAULT << AE_IFG_RXIPG_SHIFT) & @@ -469,7 +615,9 @@ ae_init_locked(ae_softc_t *sc) AE_IFG_IPGR2_MASK); AE_WRITE_4(sc, AE_IFG_REG, val); - /* Configure half-duplex operation. */ + /* + * Configure half-duplex operation. + */ val = ((AE_HDPX_LCOL_DEFAULT << AE_HDPX_LCOL_SHIFT) & AE_HDPX_LCOL_MASK) | ((AE_HDPX_RETRY_DEFAULT << AE_HDPX_RETRY_SHIFT) & @@ -480,30 +628,42 @@ ae_init_locked(ae_softc_t *sc) AE_HDPX_JAMIPG_MASK) | AE_HDPX_EXC_EN; AE_WRITE_4(sc, AE_HDPX_REG, val); - /* Configure interrupt moderate timer. */ + /* + * Configure interrupt moderate timer. + */ AE_WRITE_2(sc, AE_IMT_REG, AE_IMT_DEFAULT); val = AE_READ_4(sc, AE_MASTER_REG); val |= AE_MASTER_IMT_EN; AE_WRITE_4(sc, AE_MASTER_REG, val); - /* Configure interrupt clearing timer. */ + /* + * Configure interrupt clearing timer. + */ AE_WRITE_2(sc, AE_ICT_REG, AE_ICT_DEFAULT); - /* Configure MTU. */ + /* + * Configure MTU. + */ val = ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + ETHER_CRC_LEN; AE_WRITE_2(sc, AE_MTU_REG, val); - /* Configure cut-through threshold. */ + /* + * Configure cut-through threshold. + */ AE_WRITE_4(sc, AE_CUT_THRESH_REG, AE_CUT_THRESH_DEFAULT); - /* Configure flow control. */ + /* + * Configure flow control. + */ AE_WRITE_2(sc, AE_FLOW_THRESH_HI_REG, (AE_RXD_COUNT_DEFAULT / 8) * 7); AE_WRITE_2(sc, AE_FLOW_THRESH_LO_REG, (AE_RXD_COUNT_MIN / 8) > (AE_RXD_COUNT_DEFAULT / 12) ? (AE_RXD_COUNT_MIN / 8) : (AE_RXD_COUNT_DEFAULT / 12)); - /* Init mailboxes. */ + /* + * Init mailboxes. + */ sc->txd_cur = sc->rxd_cur = 0; sc->txs_ack = sc->txd_ack = 0; sc->rxd_cur = 0; @@ -511,31 +671,44 @@ ae_init_locked(ae_softc_t *sc) AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur); sc->tx_inproc = 0; /* Number of packets the chip processes now. */ + sc->flags |= AE_FLAG_TXAVAIL; /* Free Tx's available. */ - /* We have free Txs available */ - sc->flags |= AE_FLAG_TXAVAIL; - - /* Enable DMA. */ + /* + * Enable DMA. + */ AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN); AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN); - /* Check if everything is OK. */ + /* + * Check if everything is OK. + */ val = AE_READ_4(sc, AE_ISR_REG); if ((val & AE_ISR_PHY_LINKDOWN) != 0) { - printf("Initialization failed.\n"); + device_printf(sc->dev, "Initialization failed.\n"); return (ENXIO); } - /* Clear interrupt status. */ + /* + * Clear interrupt status. + */ AE_WRITE_4(sc, AE_ISR_REG, 0x3fffffff); AE_WRITE_4(sc, AE_ISR_REG, 0x0); - /* Enable interrupts. */ + /* + * Enable interrupts. + */ val = AE_READ_4(sc, AE_MASTER_REG); AE_WRITE_4(sc, AE_MASTER_REG, val | AE_MASTER_MANUAL_INT); AE_WRITE_4(sc, AE_IMR_REG, AE_IMR_DEFAULT); - /* Configure MAC. */ + /* + * Disable WOL. + */ + AE_WRITE_4(sc, AE_WOL_REG, 0); + + /* + * Configure MAC. + */ val = AE_MAC_TX_CRC_EN | AE_MAC_TX_AUTOPAD | AE_MAC_FULL_DUPLEX | AE_MAC_CLK_PHY | AE_MAC_TX_FLOW_EN | AE_MAC_RX_FLOW_EN | @@ -544,18 +717,20 @@ ae_init_locked(ae_softc_t *sc) AE_MAC_PREAMBLE_MASK); AE_WRITE_4(sc, AE_MAC_REG, val); - /* Set up the receive filter. */ + /* + * Configure Rx MAC. + */ ae_rxfilter(sc); -/* ae_rxvlan(sc); XXX: */ + ae_rxvlan(sc); - /* Enable Tx/Rx. */ + /* + * Enable Tx/Rx. + */ val = AE_READ_4(sc, AE_MAC_REG); AE_WRITE_4(sc, AE_MAC_REG, val | AE_MAC_TX_EN | AE_MAC_RX_EN); sc->flags &= ~AE_FLAG_LINK; - - /* Switch to the current media */ - mii_mediachg(mii); + mii_mediachg(mii); /* Switch to the current media. */ callout_reset(&sc->tick_ch, hz, ae_tick, sc); @@ -576,9 +751,8 @@ ae_detach(device_t dev) struct ifnet *ifp; sc = device_get_softc(dev); - KASSERT(sc != NULL, ("[ae: %d]: sc is null", __LINE__)); + KASSERT(sc != NULL, ("[ae: %d]: sc is NULL", __LINE__)); ifp = sc->ifp; - if (device_is_attached(dev)) { AE_LOCK(sc); sc->flags |= AE_FLAG_DETACH; @@ -590,36 +764,31 @@ ae_detach(device_t dev) taskqueue_drain(taskqueue_swi, &sc->link_task); ether_ifdetach(ifp); } - if (sc->tq != NULL) { taskqueue_drain(sc->tq, &sc->int_task); taskqueue_free(sc->tq); sc->tq = NULL; } - if (sc->miibus != NULL) { device_delete_child(dev, sc->miibus); sc->miibus = NULL; } - bus_generic_detach(sc->dev); ae_dma_free(sc); - if (sc->intrhand != NULL) { bus_teardown_intr(dev, sc->irq[0], sc->intrhand); sc->intrhand = NULL; } - if (ifp != NULL) { if_free(ifp); sc->ifp = NULL; } - if (sc->spec_irq != NULL) bus_release_resources(dev, sc->spec_irq, sc->irq); if (sc->spec_mem != NULL) bus_release_resources(dev, sc->spec_mem, sc->mem); - + if ((sc->flags & AE_FLAG_MSI) != 0) + pci_release_msi(dev); mtx_destroy(&sc->mtx); return (0); @@ -635,11 +804,9 @@ ae_miibus_readreg(device_t dev, int phy, int reg) sc = device_get_softc(dev); KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); - /* Locking is done in upper layers. */ - -#ifdef notdef - device_printf(sc->dev, "MII read reg%d\n", reg); -#endif + /* + * Locking is done in upper layers. + */ if (phy != sc->phyaddr) return (0); @@ -649,18 +816,19 @@ ae_miibus_readreg(device_t dev, int phy, int reg) ((AE_MDIO_CLK_25_4 << AE_MDIO_CLK_SHIFT) & AE_MDIO_CLK_MASK); AE_WRITE_4(sc, AE_MDIO_REG, val); + /* + * Wait for operation to complete. + */ for (i = 0; i < AE_MDIO_TIMEOUT; i++) { DELAY(2); val = AE_READ_4(sc, AE_MDIO_REG); if ((val & (AE_MDIO_START | AE_MDIO_BUSY)) == 0) break; } - if (i == AE_MDIO_TIMEOUT) { - device_printf(sc->dev, "phy read timeout: %d\n", reg); + device_printf(sc->dev, "phy read timeout: %d.\n", reg); return (0); } - return ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK); } @@ -674,7 +842,9 @@ ae_miibus_writereg(device_t dev, int phy, int reg, int val) sc = device_get_softc(dev); KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); - /* Locking is done in upper layers. */ + /* + * Locking is done in upper layers. + */ if (phy != sc->phyaddr) return (0); @@ -685,21 +855,21 @@ ae_miibus_writereg(device_t dev, int phy, int reg, int val) ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK); AE_WRITE_4(sc, AE_MDIO_REG, aereg); + /* + * Wait for operation to complete. + */ for (i = 0; i < AE_MDIO_TIMEOUT; i++) { DELAY(2); aereg = AE_READ_4(sc, AE_MDIO_REG); if ((aereg & (AE_MDIO_START | AE_MDIO_BUSY)) == 0) break; } - if (i == AE_MDIO_TIMEOUT) { - device_printf(sc->dev, "phy read timeout: %d\n", reg); + device_printf(sc->dev, "phy write timeout: %d.\n", reg); } - return (0); } -/* MII bus callback when media changes */ static void ae_miibus_statchg(device_t dev) { @@ -709,7 +879,6 @@ ae_miibus_statchg(device_t dev) taskqueue_enqueue(taskqueue_swi, &sc->link_task); } -/* Get current media status */ static void ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) { @@ -727,7 +896,6 @@ ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) AE_UNLOCK(sc); } -/* Switch to new media. */ static int ae_mediachange(struct ifnet *ifp) { @@ -737,7 +905,6 @@ ae_mediachange(struct ifnet *ifp) int error; /* XXX: check IFF_UP ?? */ - sc = ifp->if_softc; KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); AE_LOCK(sc); @@ -746,7 +913,6 @@ ae_mediachange(struct ifnet *ifp) LIST_FOREACH(mii_sc, &mii->mii_phys, mii_list) mii_phy_reset(mii_sc); } - error = mii_mediachg(mii); AE_UNLOCK(sc); @@ -769,9 +935,7 @@ ae_check_eeprom_present(ae_softc_t *sc, int *vpdc) val &= ~AE_SPICTL_VPD_EN; AE_WRITE_4(sc, AE_SPICTL_REG, val); } - error = pci_find_extcap(sc->dev, PCIY_VPD, vpdc); - return (error); } @@ -783,26 +947,24 @@ ae_vpd_read_word(ae_softc_t *sc, int reg, uint32_t *word) AE_WRITE_4(sc, AE_VPD_DATA_REG, 0); /* Clear register value. */ - /* VPD registers start at offset 0x100. */ + /* + * VPD registers start at offset 0x100. Read them. + */ val = 0x100 + reg * 4; AE_WRITE_4(sc, AE_VPD_CAP_REG, (val << AE_VPD_CAP_ADDR_SHIFT) & AE_VPD_CAP_ADDR_MASK); - for (i = 0; i < AE_VPD_TIMEOUT; i++) { DELAY(2000); val = AE_READ_4(sc, AE_VPD_CAP_REG); if ((val & AE_VPD_CAP_DONE) != 0) break; } - if (i == AE_VPD_TIMEOUT) { device_printf(sc->dev, "timeout reading VPD register %d.\n", reg); return (ETIMEDOUT); } - *word = AE_READ_4(sc, AE_VPD_DATA_REG); - return (0); } @@ -818,7 +980,9 @@ ae_get_vpd_eaddr(ae_softc_t *sc, uint32_t *eaddr) KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); KASSERT(eaddr != NULL, ("[ae, %d]: eaddr is NULL", __LINE__)); - /* Check for EEPROM. */ + /* + * Check for EEPROM. + */ error = ae_check_eeprom_present(sc, &vpdc); if (error != 0) return (error); @@ -828,21 +992,18 @@ ae_get_vpd_eaddr(ae_softc_t *sc, uint32_t *eaddr) * Each register is prefixed with signature, * so we can check if it is valid. */ - for (i = 0, found = 0; i < AE_VPD_NREGS; i++) { error = ae_vpd_read_word(sc, i, &word); if (error != 0) break; - - /* Check signature. */ + + /* + * Check signature. + */ if ((word & AE_VPD_SIG_MASK) != AE_VPD_SIG) break; - - /* Ok, we've found a valid block. */ reg = word >> AE_VPD_REG_SHIFT; - - /* Move to the next word. */ - i++; + i++; /* Move to the next word. */ if (reg != AE_EADDR0_REG && reg != AE_EADDR1_REG) continue; @@ -850,52 +1011,43 @@ ae_get_vpd_eaddr(ae_softc_t *sc, uint32_t *eaddr) error = ae_vpd_read_word(sc, i, &val); if (error != 0) break; - if (reg == AE_EADDR0_REG) eaddr[0] = val; else eaddr[1] = val; - found++; } if (found < 2) return (ENOENT); - /* Only last 2 bytes are used. */ - eaddr[1] &= 0xffff; - + eaddr[1] &= 0xffff; /* Only last 2 bytes are used. */ if (AE_CHECK_EADDR_VALID(eaddr) != 0) { - if (bootverbose || 1) + if (bootverbose) device_printf(sc->dev, "VPD ethernet address registers are invalid.\n"); - return (EINVAL); } - return (0); } static int ae_get_reg_eaddr(ae_softc_t *sc, uint32_t *eaddr) { + /* * BIOS is supposed to set this. */ eaddr[0] = AE_READ_4(sc, AE_EADDR0_REG); eaddr[1] = AE_READ_4(sc, AE_EADDR1_REG); - - /* Only last 2 bytes are used. */ - eaddr[1] &= 0xffff; + eaddr[1] &= 0xffff; /* Only last 2 bytes are used. */ if (AE_CHECK_EADDR_VALID(eaddr) != 0) { - if (bootverbose || 1) + if (bootverbose) device_printf(sc->dev, "Ethetnet address registers are invalid.\n"); - return (EINVAL); } - return (0); } @@ -905,23 +1057,26 @@ ae_retrieve_address(ae_softc_t *sc) uint32_t eaddr[2] = {0, 0}; int error; - /* Check for EEPROM. */ + /* + *Check for EEPROM. + */ error = ae_get_vpd_eaddr(sc, eaddr); if (error != 0) error = ae_get_reg_eaddr(sc, eaddr); - if (error != 0) { - if (bootverbose || 1) + if (bootverbose) device_printf(sc->dev, - "Generating fake ethernet address.\n"); - + "Generating random ethernet address.\n"); #ifdef __HAIKU__ - eaddr[0] = rand(); + eaddr[0] = random(); #else eaddr[0] = arc4random(); #endif - /* Set OUI to ASUSTek COMPUTER INC. */ - sc->eaddr[0] = 0x00; + + /* + * Set OUI to ASUSTek COMPUTER INC. + */ + sc->eaddr[0] = 0x02; /* U/L bit set. */ sc->eaddr[1] = 0x1f; sc->eaddr[2] = 0xc6; sc->eaddr[3] = (eaddr[0] >> 16) & 0xff; @@ -944,10 +1099,8 @@ ae_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) if (error != 0) return; - KASSERT(nsegs == 1, ("[ae, %d]: %d segments instead of 1!", __LINE__, nsegs)); - *addr = segs[0].ds_addr; } @@ -960,7 +1113,9 @@ ae_alloc_rings(ae_softc_t *sc) bustag = bus_get_dma_tag(sc->dev); - /* Create parent DMA tag. */ + /* + * Create parent DMA tag. + */ error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, @@ -971,7 +1126,9 @@ ae_alloc_rings(ae_softc_t *sc) return (error); } - /* Create DMA tag for TxD. */ + /* + * Create DMA tag for TxD. + */ error = bus_dma_tag_create(sc->dma_parent_tag, 4, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, AE_TXD_BUFSIZE_DEFAULT, 1, @@ -982,7 +1139,9 @@ ae_alloc_rings(ae_softc_t *sc) return (error); } - /* Create DMA tag for TxS. */ + /* + * Create DMA tag for TxS. + */ error = bus_dma_tag_create(sc->dma_parent_tag, 4, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, AE_TXS_COUNT_DEFAULT * 4, 1, @@ -993,7 +1152,9 @@ ae_alloc_rings(ae_softc_t *sc) return (error); } - /* Create DMA tag for RxD. */ + /* + * Create DMA tag for RxD. + */ error = bus_dma_tag_create(sc->dma_parent_tag, 128, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, AE_RXD_COUNT_DEFAULT * 1536 + 120, 1, @@ -1004,7 +1165,9 @@ ae_alloc_rings(ae_softc_t *sc) return (error); } - /* Allocate DMA memory. */ + /* + * Allocate TxD DMA memory. + */ error = bus_dmamem_alloc(sc->dma_txd_tag, (void **)&sc->txd_base, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dma_txd_map); @@ -1013,7 +1176,6 @@ ae_alloc_rings(ae_softc_t *sc) "could not allocate DMA memory for TxD ring.\n"); return (error); } - error = bus_dmamap_load(sc->dma_txd_tag, sc->dma_txd_map, sc->txd_base, AE_TXD_BUFSIZE_DEFAULT, ae_dmamap_cb, &busaddr, BUS_DMA_NOWAIT); if (error != 0 || busaddr == 0) { @@ -1021,9 +1183,11 @@ ae_alloc_rings(ae_softc_t *sc) "could not load DMA map for TxD ring.\n"); return (error); } - sc->dma_txd_busaddr = busaddr; + /* + * Allocate TxS DMA memory. + */ error = bus_dmamem_alloc(sc->dma_txs_tag, (void **)&sc->txs_base, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dma_txs_map); @@ -1032,7 +1196,6 @@ ae_alloc_rings(ae_softc_t *sc) "could not allocate DMA memory for TxS ring.\n"); return (error); } - error = bus_dmamap_load(sc->dma_txs_tag, sc->dma_txs_map, sc->txs_base, AE_TXS_COUNT_DEFAULT * 4, ae_dmamap_cb, &busaddr, BUS_DMA_NOWAIT); if (error != 0 || busaddr == 0) { @@ -1042,6 +1205,9 @@ ae_alloc_rings(ae_softc_t *sc) } sc->dma_txs_busaddr = busaddr; + /* + * Allocate RxD DMA memory. + */ error = bus_dmamem_alloc(sc->dma_rxd_tag, (void **)&sc->rxd_base_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dma_rxd_map); @@ -1050,15 +1216,14 @@ ae_alloc_rings(ae_softc_t *sc) "could not allocate DMA memory for RxD ring.\n"); return (error); } - - error = bus_dmamap_load(sc->dma_rxd_tag, sc->dma_rxd_map, sc->rxd_base_dma, - AE_RXD_COUNT_DEFAULT * 1536 + 120, ae_dmamap_cb, &busaddr, BUS_DMA_NOWAIT); + error = bus_dmamap_load(sc->dma_rxd_tag, sc->dma_rxd_map, + sc->rxd_base_dma, AE_RXD_COUNT_DEFAULT * 1536 + 120, ae_dmamap_cb, + &busaddr, BUS_DMA_NOWAIT); if (error != 0 || busaddr == 0) { device_printf(sc->dev, "could not load DMA map for RxD ring.\n"); return (error); } - sc->dma_rxd_busaddr = busaddr + 120; sc->rxd_base = (ae_rxd_t *)(sc->rxd_base_dma + 120); @@ -1068,54 +1233,46 @@ ae_alloc_rings(ae_softc_t *sc) static void ae_dma_free(ae_softc_t *sc) { + if (sc->dma_txd_tag != NULL) { if (sc->dma_txd_map != NULL) { bus_dmamap_unload(sc->dma_txd_tag, sc->dma_txd_map); - if (sc->txd_base != NULL) bus_dmamem_free(sc->dma_txd_tag, sc->txd_base, sc->dma_txd_map); } - bus_dma_tag_destroy(sc->dma_txd_tag); sc->dma_txd_map = NULL; sc->dma_txd_tag = NULL; sc->txd_base = NULL; } - if (sc->dma_txs_tag != NULL) { if (sc->dma_txs_map != NULL) { bus_dmamap_unload(sc->dma_txs_tag, sc->dma_txs_map); - if (sc->txs_base != NULL) bus_dmamem_free(sc->dma_txs_tag, sc->txs_base, sc->dma_txs_map); } - bus_dma_tag_destroy(sc->dma_txs_tag); sc->dma_txs_map = NULL; sc->dma_txs_tag = NULL; sc->txs_base = NULL; } - if (sc->dma_rxd_tag != NULL) { if (sc->dma_rxd_map != NULL) { bus_dmamap_unload(sc->dma_rxd_tag, sc->dma_rxd_map); - if (sc->rxd_base_dma != NULL) - bus_dmamem_free(sc->dma_rxd_tag, sc->rxd_base_dma, - sc->dma_rxd_map); + bus_dmamem_free(sc->dma_rxd_tag, + sc->rxd_base_dma, sc->dma_rxd_map); } - bus_dma_tag_destroy(sc->dma_rxd_tag); sc->dma_rxd_map = NULL; sc->dma_rxd_tag = NULL; sc->rxd_base_dma = NULL; } - if (sc->dma_parent_tag != NULL) { bus_dma_tag_destroy(sc->dma_parent_tag); sc->dma_parent_tag = NULL; @@ -1125,13 +1282,92 @@ ae_dma_free(ae_softc_t *sc) static int ae_shutdown(device_t dev) { + ae_softc_t *sc; int error; - error = ae_suspend(dev); + sc = device_get_softc(dev); + KASSERT(sc != NULL, ("[ae: %d]: sc is NULL", __LINE__)); + error = ae_suspend(dev); + AE_LOCK(sc); + ae_powersave_enable(sc); + AE_UNLOCK(sc); return (error); } +static void +ae_powersave_disable(ae_softc_t *sc) +{ + uint32_t val; + + AE_LOCK_ASSERT(sc); + + AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0); + val = AE_PHY_READ(sc, AE_PHY_DBG_DATA); + if (val & AE_PHY_DBG_POWERSAVE) { + val &= ~AE_PHY_DBG_POWERSAVE; + AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, val); + DELAY(1000); + } +} + +static void +ae_powersave_enable(ae_softc_t *sc) +{ + uint32_t val; + + AE_LOCK_ASSERT(sc); + + /* + * XXX magic numbers. + */ + AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0); + val = AE_PHY_READ(sc, AE_PHY_DBG_DATA); + AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, val | 0x1000); + AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 2); + AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0x3000); + AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 3); + AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0); +} + +static void +ae_pm_init(ae_softc_t *sc) +{ + struct ifnet *ifp; + uint32_t val; + uint16_t pmstat; + int pmc; + + AE_LOCK_ASSERT(sc); + + ifp = sc->ifp; + if ((sc->flags & AE_FLAG_PMG) == 0) { + /* Disable WOL entirely. */ + AE_WRITE_4(sc, AE_WOL_REG, 0); + return; + } + + ae_powersave_enable(sc); + + /* + * PCIE hacks. Magic numbers. + */ + val = AE_READ_4(sc, AE_PCIE_PHYMISC_REG); + val |= AE_PCIE_PHYMISC_FORCE_RCV_DET; + AE_WRITE_4(sc, AE_PCIE_PHYMISC_REG, val); + val = AE_READ_4(sc, AE_PCIE_DLL_TX_CTRL_REG); + val |= AE_PCIE_DLL_TX_CTRL_SEL_NOR_CLK; + AE_WRITE_4(sc, AE_PCIE_DLL_TX_CTRL_REG, val); + + /* + * Configure PME. + */ + pci_find_extcap(sc->dev, PCIY_PMG, &pmc); + pmstat = pci_read_config(sc->dev, pmc + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + pci_write_config(sc->dev, pmc + PCIR_POWER_STATUS, pmstat, 2); +} + static int ae_suspend(device_t dev) { @@ -1141,6 +1377,7 @@ ae_suspend(device_t dev) AE_LOCK(sc); ae_stop(sc); + ae_pm_init(sc); AE_UNLOCK(sc); return (0); @@ -1152,9 +1389,10 @@ ae_resume(device_t dev) ae_softc_t *sc; sc = device_get_softc(dev); - KASSERT(sc != NULL, ("[ae, %d]: sc is null", __LINE__)); + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); AE_LOCK(sc); + AE_READ_4(sc, AE_WOL_REG); /* Clear WOL status. */ if ((sc->ifp->if_flags & IFF_UP) != 0) ae_init_locked(sc); AE_UNLOCK(sc); @@ -1181,13 +1419,15 @@ ae_encap(ae_softc_t *sc, struct mbuf **m_head) struct mbuf *m0; ae_txd_t *hdr; unsigned int to_end; + uint16_t len; AE_LOCK_ASSERT(sc); m0 = *m_head; + len = m0->m_pkthdr.len; if ((sc->flags & AE_FLAG_TXAVAIL) == 0 || - ae_tx_avail_size(sc) < m0->m_pkthdr.len) { + ae_tx_avail_size(sc) < len) { #ifdef AE_DEBUG if_printf(sc->ifp, "No free Tx available.\n"); #endif @@ -1196,50 +1436,50 @@ ae_encap(ae_softc_t *sc, struct mbuf **m_head) hdr = (ae_txd_t *)(sc->txd_base + sc->txd_cur); bzero(hdr, sizeof(*hdr)); - - hdr->len = htole32(m0->m_pkthdr.len); sc->txd_cur = (sc->txd_cur + 4) % AE_TXD_BUFSIZE_DEFAULT; /* Header size. */ - to_end = AE_TXD_BUFSIZE_DEFAULT - sc->txd_cur; /* Space available to * the end of the ring */ - -#ifdef AE_DEBUG - if_printf(sc->ifp, "Writing data.\n"); -#endif - - if (to_end >= m0->m_pkthdr.len) { - m_copydata(m0, 0, m0->m_pkthdr.len, - (caddr_t)(sc->txd_base + sc->txd_cur)); + if (to_end >= len) { + m_copydata(m0, 0, len, (caddr_t)(sc->txd_base + sc->txd_cur)); } else { m_copydata(m0, 0, to_end, (caddr_t)(sc->txd_base + sc->txd_cur)); - m_copydata(m0, to_end, m0->m_pkthdr.len - to_end, - (caddr_t)sc->txd_base); + m_copydata(m0, to_end, len - to_end, (caddr_t)sc->txd_base); } -#ifdef AE_DEBUG - if_printf(sc->ifp, "Written data.\n"); -#endif - - /* Set current TxD position and round up to a 4-byte boundary. */ - sc->txd_cur = ((sc->txd_cur + m0->m_pkthdr.len + 3) & ~3) % - AE_TXD_BUFSIZE_DEFAULT; + /* + * Set TxD flags and parameters. + */ + if ((m0->m_flags & M_VLANTAG) != 0) { + hdr->vlan = htole16(AE_TXD_VLAN(m0->m_pkthdr.ether_vtag)); + hdr->len = htole16(len | AE_TXD_INSERT_VTAG); + } else { + hdr->len = htole16(len); + } + /* + * Set current TxD position and round up to a 4-byte boundary. + */ + sc->txd_cur = ((sc->txd_cur + len + 3) & ~3) % AE_TXD_BUFSIZE_DEFAULT; if (sc->txd_cur == sc->txd_ack) sc->flags &= ~AE_FLAG_TXAVAIL; - #ifdef AE_DEBUG if_printf(sc->ifp, "New txd_cur = %d.\n", sc->txd_cur); #endif - /* Update TxS position and check if there are empty TxS available */ - sc->txs_base[sc->txs_cur].update = 0; + /* + * Update TxS position and check if there are empty TxS available. + */ + sc->txs_base[sc->txs_cur].flags &= ~htole16(AE_TXS_UPDATE); sc->txs_cur = (sc->txs_cur + 1) % AE_TXS_COUNT_DEFAULT; if (sc->txs_cur == sc->txs_ack) sc->flags &= ~AE_FLAG_TXAVAIL; + /* + * Synchronize DMA memory. + */ bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, @@ -1252,25 +1492,25 @@ static void ae_start(struct ifnet *ifp) { ae_softc_t *sc; - unsigned int count = 0; + unsigned int count; struct mbuf *m0; int error; sc = ifp->if_softc; - KASSERT(sc != NULL, ("[ae, %d]: sc is null", __LINE__)); - + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); AE_LOCK(sc); - + +#ifdef AE_DEBUG + if_printf(ifp, "Start called.\n"); +#endif + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->flags & AE_FLAG_LINK) == 0) { AE_UNLOCK(sc); return; } -#ifdef AE_DEBUG - if_printf(ifp, "Start called.\n"); -#endif - + count = 0; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) @@ -1287,7 +1527,6 @@ ae_start(struct ifnet *ifp) } break; } - count++; sc->tx_inproc++; @@ -1305,7 +1544,6 @@ ae_start(struct ifnet *ifp) if_printf(ifp, "Tx pos now is %d.\n", sc->txd_cur); #endif } - AE_UNLOCK(sc); } @@ -1318,13 +1556,11 @@ ae_link_task(void *arg, int pending) uint32_t val; sc = (ae_softc_t *)arg; - KASSERT(sc != NULL, ("[ae, %d]: sc is null", __LINE__)); - + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); AE_LOCK(sc); ifp = sc->ifp; mii = device_get_softc(sc->miibus); - if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { AE_UNLOCK(sc); /* XXX: could happen? */ @@ -1332,7 +1568,8 @@ ae_link_task(void *arg, int pending) } sc->flags &= ~AE_FLAG_LINK; - if ((mii->mii_media_status & IFM_AVALID) != 0) { + if ((mii->mii_media_status & (IFM_AVALID | IFM_ACTIVE)) == + (IFM_AVALID | IFM_ACTIVE)) { switch(IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: @@ -1343,23 +1580,28 @@ ae_link_task(void *arg, int pending) } } - /* Stop Rx/Tx MACs. */ + /* + * Stop Rx/Tx MACs. + */ ae_stop_rxmac(sc); ae_stop_txmac(sc); if ((sc->flags & AE_FLAG_LINK) != 0) { ae_mac_config(sc); - /* Restart DMA engines. */ + /* + * Restart DMA engines. + */ AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN); AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN); - /* Enable Rx and Tx MACs. */ + /* + * Enable Rx and Tx MACs. + */ val = AE_READ_4(sc, AE_MAC_REG); val |= AE_MAC_TX_EN | AE_MAC_RX_EN; AE_WRITE_4(sc, AE_MAC_REG, val); } - AE_UNLOCK(sc); } @@ -1371,25 +1613,30 @@ ae_stop_rxmac(ae_softc_t *sc) AE_LOCK_ASSERT(sc); - /* Stop Rx MAC engine. */ + /* + * Stop Rx MAC engine. + */ val = AE_READ_4(sc, AE_MAC_REG); if ((val & AE_MAC_RX_EN) != 0) { val &= ~AE_MAC_RX_EN; AE_WRITE_4(sc, AE_MAC_REG, val); } - /* Stop Rx DMA engine. */ + /* + * Stop Rx DMA engine. + */ if (AE_READ_1(sc, AE_DMAWRITE_REG) == AE_DMAWRITE_EN) AE_WRITE_1(sc, AE_DMAWRITE_REG, 0); - /* Wait for IDLE state. */ + /* + * Wait for IDLE state. + */ for (i = 0; i < AE_IDLE_TIMEOUT; i--) { val = AE_READ_4(sc, AE_IDLE_REG); if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0) break; DELAY(100); } - if (i == AE_IDLE_TIMEOUT) device_printf(sc->dev, "timed out while stopping Rx MAC.\n"); } @@ -1402,25 +1649,30 @@ ae_stop_txmac(ae_softc_t *sc) AE_LOCK_ASSERT(sc); - /* Stop Tx MAC engine. */ + /* + * Stop Tx MAC engine. + */ val = AE_READ_4(sc, AE_MAC_REG); if ((val & AE_MAC_TX_EN) != 0) { val &= ~AE_MAC_TX_EN; AE_WRITE_4(sc, AE_MAC_REG, val); } - /* Stop Tx DMA engine. */ + /* + * Stop Tx DMA engine. + */ if (AE_READ_1(sc, AE_DMAREAD_REG) == AE_DMAREAD_EN) AE_WRITE_1(sc, AE_DMAREAD_REG, 0); - /* Wait for IDLE state. */ + /* + * Wait for IDLE state. + */ for (i = 0; i < AE_IDLE_TIMEOUT; i--) { val = AE_READ_4(sc, AE_IDLE_REG); if ((val & (AE_IDLE_TXMAC | AE_IDLE_DMAREAD)) == 0) break; DELAY(100); } - if (i == AE_IDLE_TIMEOUT) device_printf(sc->dev, "timed out while stopping Tx MAC.\n"); } @@ -1445,6 +1697,7 @@ ae_mac_config(ae_softc_t *sc) mii = device_get_softc(sc->miibus); val = AE_READ_4(sc, AE_MAC_REG); val &= ~AE_MAC_FULL_DUPLEX; + /* XXX disable AE_MAC_TX_FLOW_EN? */ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) val |= AE_MAC_FULL_DUPLEX; @@ -1459,11 +1712,11 @@ ae_intr(void *arg) uint32_t val; sc = (ae_softc_t *)arg; - KASSERT(sc != NULL, ("[ae, %d]: sc is null", __LINE__)); + KASSERT(sc != NULL, ("[ae, %d]: sc is NULL", __LINE__)); val = AE_READ_4(sc, AE_ISR_REG); if (val == 0 || (val & AE_IMR_DEFAULT) == 0) - return FILTER_STRAY; + return (FILTER_STRAY); /* Disable interrupts. */ AE_WRITE_4(sc, AE_ISR_REG, AE_ISR_DISABLE); @@ -1479,7 +1732,6 @@ ae_int_task(void *arg, int pending) { ae_softc_t *sc; struct ifnet *ifp; - struct mii_data *mii; uint32_t val; sc = (ae_softc_t *)arg; @@ -1488,17 +1740,11 @@ ae_int_task(void *arg, int pending) ifp = sc->ifp; - /* Read interrupt status. */ - val = AE_READ_4(sc, AE_ISR_REG); + val = AE_READ_4(sc, AE_ISR_REG); /* Read interrupt status. */ - if ((val & AE_ISR_PHY) != 0) { - /* - * Clear PHY interrupt. Not sure if it needed. From Linux. - */ - ae_miibus_readreg(sc->miibus, 1, 19); - } - - /* Clear interrupts and disable them. */ + /* + * Clear interrupts and disable them. + */ AE_WRITE_4(sc, AE_ISR_REG, val | AE_ISR_DISABLE); #ifdef AE_DEBUG @@ -1506,24 +1752,19 @@ ae_int_task(void *arg, int pending) #endif if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { - if ((val & (AE_ISR_PHY | AE_ISR_MANUAL)) != 0) { - mii = device_get_softc(sc->miibus); - mii_mediachg(mii); - } - if ((val & (AE_ISR_DMAR_TIMEOUT | AE_ISR_DMAW_TIMEOUT | AE_ISR_PHY_LINKDOWN)) != 0) { ae_init_locked(sc); } - if ((val & AE_ISR_TX_EVENT) != 0) ae_tx_intr(sc); - if ((val & AE_ISR_RX_EVENT) != 0) ae_rx_intr(sc); } - /* Re-enable interrupts. */ + /* + * Re-enable interrupts. + */ AE_WRITE_4(sc, AE_ISR_REG, 0); AE_UNLOCK(sc); @@ -1535,6 +1776,7 @@ ae_tx_intr(ae_softc_t *sc) struct ifnet *ifp; ae_txd_t *txd; ae_txs_t *txs; + uint16_t flags; AE_LOCK_ASSERT(sc); @@ -1544,37 +1786,42 @@ ae_tx_intr(ae_softc_t *sc) if_printf(ifp, "Tx interrupt occuried.\n"); #endif - /* Syncronize DMA buffers. */ + /* + * Syncronize DMA buffers. + */ bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (;;) { txs = sc->txs_base + sc->txs_ack; - if (txs->update == 0) + flags = le16toh(txs->flags); + if ((flags & AE_TXS_UPDATE) == 0) break; + txs->flags = htole16(flags & ~AE_TXS_UPDATE); + /* Update stats. */ + ae_update_stats_tx(flags, &sc->stats); - /* Update TxS position. */ + /* + * Update TxS position. + */ sc->txs_ack = (sc->txs_ack + 1) % AE_TXS_COUNT_DEFAULT; sc->flags |= AE_FLAG_TXAVAIL; - txs->update = 0; /* Clear status. */ - txd = (ae_txd_t *)(sc->txd_base + sc->txd_ack); if (txs->len != txd->len) device_printf(sc->dev, "Size mismatch: TxS:%d TxD:%d\n", - le32toh(txs->len), le32toh(txd->len)); + le16toh(txs->len), le16toh(txd->len)); - /* Move txd ack and align on 4-byte boundary. */ - sc->txd_ack = ((sc->txd_ack + le32toh(txd->len) + 4 + 3) & ~3) % + /* + * Move txd ack and align on 4-byte boundary. + */ + sc->txd_ack = ((sc->txd_ack + le16toh(txd->len) + 4 + 3) & ~3) % AE_TXD_BUFSIZE_DEFAULT; - if (txs->flags & AE_TXF_SUCCESS) { /* XXX: endianess. */ + if ((flags & AE_TXS_SUCCESS) != 0) ifp->if_opackets++; - /* if_printf(ifp, "Packet successfully sent.\n"); */ - } else ifp->if_oerrors++; @@ -1596,10 +1843,11 @@ ae_tx_intr(ae_softc_t *sc) taskqueue_enqueue(sc->tq, &sc->tx_task); } - /* Syncronize DMA buffers. */ + /* + * Syncronize DMA buffers. + */ bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } @@ -1610,39 +1858,35 @@ ae_rxeof(ae_softc_t *sc, ae_rxd_t *rxd) struct ifnet *ifp; struct mbuf *m; unsigned int size; + uint16_t flags; AE_LOCK_ASSERT(sc); ifp = sc->ifp; + flags = le16toh(rxd->flags); #ifdef AE_DEBUG if_printf(ifp, "Rx interrupt occuried.\n"); #endif - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return (ENOBUFS); - - size = le32toh(rxd->len) - ETHER_CRC_LEN; + size = le16toh(rxd->len) - ETHER_CRC_LEN; if (size < 0) { if_printf(ifp, "Negative length packet received."); return (EIO); } - if (size > MHLEN) { - MCLGET(m, M_DONTWAIT); - if ((m->m_flags & M_EXT) == 0) { - m_freem(m); - return (ENOBUFS); - } + m = m_devget(&rxd->data[0], size, ETHER_ALIGN, ifp, NULL); + if (m == NULL) + return (ENOBUFS); + + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && + (flags & AE_RXD_HAS_VLAN) != 0) { + m->m_pkthdr.ether_vtag = AE_RXD_VLAN(le16toh(rxd->vlan)); + m->m_flags |= M_VLANTAG; } - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = size; - - memcpy(mtod(m, void *), &rxd->data[0], size); - - /* Pass it on. */ + /* + * Pass it through. + */ AE_UNLOCK(sc); (*ifp->if_input)(ifp, m); AE_LOCK(sc); @@ -1655,6 +1899,7 @@ ae_rx_intr(ae_softc_t *sc) { ae_rxd_t *rxd; struct ifnet *ifp; + uint16_t flags; int error; KASSERT(sc != NULL, ("[ae, %d]: sc is NULL!", __LINE__)); @@ -1663,20 +1908,27 @@ ae_rx_intr(ae_softc_t *sc) ifp = sc->ifp; - /* Syncronize DMA buffers. */ + /* + * Syncronize DMA buffers. + */ bus_dmamap_sync(sc->dma_rxd_tag, sc->dma_rxd_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (;;) { rxd = (ae_rxd_t *)(sc->rxd_base + sc->rxd_cur); - if (rxd->update == 0) + flags = le16toh(rxd->flags); + if ((flags & AE_RXD_UPDATE) == 0) break; + rxd->flags = htole16(flags & ~AE_RXD_UPDATE); + /* Update stats. */ + ae_update_stats_rx(flags, &sc->stats); - /* Update position index. */ + /* + * Update position index. + */ sc->rxd_cur = (sc->rxd_cur + 1) % AE_RXD_COUNT_DEFAULT; - rxd->update = 0; - if ((rxd->flags & AE_RXF_SUCCESS) == 0) { + if ((flags & AE_RXD_SUCCESS) == 0) { ifp->if_ierrors++; continue; } @@ -1689,7 +1941,9 @@ ae_rx_intr(ae_softc_t *sc) } } - /* Update Rx index. */ + /* + * Update Rx index. + */ AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur); } @@ -1699,14 +1953,12 @@ ae_watchdog(ae_softc_t *sc) struct ifnet *ifp; KASSERT(sc != NULL, ("[ae, %d]: sc is NULL!", __LINE__)); - AE_LOCK_ASSERT(sc); + ifp = sc->ifp; if (sc->wd_timer == 0 || --sc->wd_timer != 0) return; /* Noting to do. */ - ifp = sc->ifp; - if ((sc->flags & AE_FLAG_LINK) == 0) if_printf(ifp, "watchdog timeout (missed link).\n"); else @@ -1725,19 +1977,30 @@ ae_tick(void *arg) struct mii_data *mii; sc = (ae_softc_t *)arg; - KASSERT(sc != NULL, ("[ae, %d]: sc is NULL!", __LINE__)); - AE_LOCK_ASSERT(sc); mii = device_get_softc(sc->miibus); mii_tick(mii); - ae_watchdog(sc); /* Watchdog check. */ - callout_reset(&sc->tick_ch, hz, ae_tick, sc); } +static void +ae_rxvlan(ae_softc_t *sc) +{ + struct ifnet *ifp; + uint32_t val; + + AE_LOCK_ASSERT(sc); + ifp = sc->ifp; + val = AE_READ_4(sc, AE_MAC_REG); + val &= ~AE_MAC_RMVLAN_EN; + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) + val |= AE_MAC_RMVLAN_EN; + AE_WRITE_4(sc, AE_MAC_REG, val); +} + static void ae_rxfilter(ae_softc_t *sc) { @@ -1758,17 +2021,16 @@ ae_rxfilter(ae_softc_t *sc) if ((ifp->if_flags & IFF_BROADCAST) != 0) rxcfg |= AE_MAC_BCAST_EN; - if ((ifp->if_flags & IFF_PROMISC) != 0) rxcfg |= AE_MAC_PROMISC_EN; - if ((ifp->if_flags & IFF_ALLMULTI) != 0) rxcfg |= AE_MAC_MCAST_EN; - /* Wipe old settings. */ + /* + * Wipe old settings. + */ AE_WRITE_4(sc, AE_REG_MHT0, 0); AE_WRITE_4(sc, AE_REG_MHT1, 0); - if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { AE_WRITE_4(sc, AE_REG_MHT0, 0xffffffff); AE_WRITE_4(sc, AE_REG_MHT1, 0xffffffff); @@ -1776,8 +2038,10 @@ ae_rxfilter(ae_softc_t *sc) return; } + /* + * Load multicast tables. + */ bzero(mchash, sizeof(mchash)); - IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) @@ -1787,7 +2051,6 @@ ae_rxfilter(ae_softc_t *sc) mchash[crc >> 31] |= 1 << ((crc >> 26) & 0x1f); } IF_ADDR_UNLOCK(ifp); - AE_WRITE_4(sc, AE_REG_MHT0, mchash[0]); AE_WRITE_4(sc, AE_REG_MHT1, mchash[1]); AE_WRITE_4(sc, AE_MAC_REG, rxcfg); @@ -1799,11 +2062,10 @@ ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ae_softc *sc; struct ifreq *ifr; struct mii_data *mii; - int error; + int error, mask; sc = ifp->if_softc; ifr = (struct ifreq *)data; - error = 0; switch (cmd) { @@ -1849,13 +2111,20 @@ ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: + AE_LOCK(sc); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + ae_rxvlan(sc); + } + VLAN_CAPABILITIES(ifp); + AE_UNLOCK(sc); break; - default: error = ether_ioctl(ifp, cmd, data); break; } - return (error); } @@ -1873,25 +2142,86 @@ ae_stop(ae_softc_t *sc) sc->wd_timer = 0; /* Cancel watchdog. */ callout_stop(&sc->tick_ch); - /* Clear and disable interrupts. */ + /* + * Clear and disable interrupts. + */ AE_WRITE_4(sc, AE_IMR_REG, 0); AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff); - /* Stop Rx/Tx MACs. */ + /* + * Stop Rx/Tx MACs. + */ ae_stop_txmac(sc); ae_stop_rxmac(sc); - /* Stop DMA engines. */ + /* + * Stop DMA engines. + */ AE_WRITE_1(sc, AE_DMAREAD_REG, ~AE_DMAREAD_EN); AE_WRITE_1(sc, AE_DMAWRITE_REG, ~AE_DMAWRITE_EN); - /* Wait for everything to enter idle state */ + /* + * Wait for everything to enter idle state. + */ for (i = 0; i < AE_IDLE_TIMEOUT; i++) { if (AE_READ_4(sc, AE_IDLE_REG) == 0) break; DELAY(100); } - if (i == AE_IDLE_TIMEOUT) device_printf(sc->dev, "could not enter idle state in stop.\n"); } + +static void +ae_update_stats_tx(uint16_t flags, ae_stats_t *stats) +{ + + if ((flags & AE_TXS_BCAST) != 0) + stats->tx_bcast++; + if ((flags & AE_TXS_MCAST) != 0) + stats->tx_mcast++; + if ((flags & AE_TXS_PAUSE) != 0) + stats->tx_pause++; + if ((flags & AE_TXS_CTRL) != 0) + stats->tx_ctrl++; + if ((flags & AE_TXS_DEFER) != 0) + stats->tx_defer++; + if ((flags & AE_TXS_EXCDEFER) != 0) + stats->tx_excdefer++; + if ((flags & AE_TXS_SINGLECOL) != 0) + stats->tx_singlecol++; + if ((flags & AE_TXS_MULTICOL) != 0) + stats->tx_multicol++; + if ((flags & AE_TXS_LATECOL) != 0) + stats->tx_latecol++; + if ((flags & AE_TXS_ABORTCOL) != 0) + stats->tx_abortcol++; + if ((flags & AE_TXS_UNDERRUN) != 0) + stats->tx_underrun++; +} + +static void +ae_update_stats_rx(uint16_t flags, ae_stats_t *stats) +{ + + if ((flags & AE_RXD_BCAST) != 0) + stats->rx_bcast++; + if ((flags & AE_RXD_MCAST) != 0) + stats->rx_mcast++; + if ((flags & AE_RXD_PAUSE) != 0) + stats->rx_pause++; + if ((flags & AE_RXD_CTRL) != 0) + stats->rx_ctrl++; + if ((flags & AE_RXD_CRCERR) != 0) + stats->rx_crcerr++; + if ((flags & AE_RXD_CODEERR) != 0) + stats->rx_codeerr++; + if ((flags & AE_RXD_RUNT) != 0) + stats->rx_runt++; + if ((flags & AE_RXD_FRAG) != 0) + stats->rx_frag++; + if ((flags & AE_RXD_TRUNC) != 0) + stats->rx_trunc++; + if ((flags & AE_RXD_ALIGN) != 0) + stats->rx_align++; +} diff --git a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aereg.h b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aereg.h index 7f9bc177ae..19481b3401 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aereg.h +++ b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aereg.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/sys/dev/ae/if_aereg.h,v 1.1.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ */ /* @@ -75,9 +75,8 @@ #define AE_IMR_REG 0x1604 #define AE_IMR_DEFAULT (AE_ISR_DMAR_TIMEOUT | AE_ISR_DMAW_TIMEOUT | \ - AE_ISR_PHY | AE_ISR_PHY_LINKDOWN | \ - AE_ISR_TXS_UPDATED | AE_ISR_RXD_UPDATED | \ - AE_ISR_MANUAL) + AE_ISR_PHY_LINKDOWN | \ + AE_ISR_TXS_UPDATED | AE_ISR_RXD_UPDATED ) /* * Ethernet address register. @@ -287,14 +286,25 @@ #define AE_REG_MHT0 0x1490 #define AE_REG_MHT1 0x1494 +/* + * Wake on lan (WOL). + */ +#define AE_WOL_REG 0x14a0 +#define AE_WOL_MAGIC 0x00000004 +#define AE_WOL_MAGIC_PME 0x00000008 +#define AE_WOL_LNKCHG 0x00000010 +#define AE_WOL_LNKCHG_PME 0x00000020 + /* * PCIE configuration registers. Descriptions unknown. */ -#define AE_LTSSM_TESTMODE_REG 0x12fc -#define AE_LTSSM_TESTMODE_DEFAULT 0x6500 -#define AE_DLL_TX_REG 0x1104 -#define AE_DLL_TX_SEL_NOR_CLK 0x0400 -#define AE_DLL_TX_DEFAULT 0x0568 +#define AE_PCIE_LTSSM_TESTMODE_REG 0x12fc +#define AE_PCIE_LTSSM_TESTMODE_DEFAULT 0x6500 +#define AE_PCIE_DLL_TX_CTRL_REG 0x1104 +#define AE_PCIE_DLL_TX_CTRL_SEL_NOR_CLK 0x0400 +#define AE_PCIE_DLL_TX_CTRL_DEFAULT 0x0568 +#define AE_PCIE_PHYMISC_REG 0x1000 +#define AE_PCIE_PHYMISC_FORCE_RCV_DET 0x4 /* * PHY enable register. @@ -328,12 +338,48 @@ #define AE_SPICTL_VPD_EN 0x2000 /* Enable VPD. */ /* - * Tx descriptors flags. + * PHY-specific registers constants. */ -#define AE_TXF_SUCCESS 0x0001 +#define AE_PHY_DBG_ADDR 0x1d +#define AE_PHY_DBG_DATA 0x1e +#define AE_PHY_DBG_POWERSAVE 0x1000 /* - * Rx descriptors flags. + * TxD flags. */ +#define AE_TXD_INSERT_VTAG 0x8000 /* Insert VLAN tag on transfer. */ -#define AE_RXF_SUCCESS 0x0001 +/* + * TxS flags. + */ +#define AE_TXS_SUCCESS 0x0001 /* Packed transmitted successfully. */ +#define AE_TXS_BCAST 0x0002 /* Transmitted broadcast frame. */ +#define AE_TXS_MCAST 0x0004 /* Transmitted multicast frame. */ +#define AE_TXS_PAUSE 0x0008 /* Transmitted pause frame. */ +#define AE_TXS_CTRL 0x0010 /* Transmitted control frame. */ +#define AE_TXS_DEFER 0x0020 /* Frame transmitted with defer. */ +#define AE_TXS_EXCDEFER 0x0040 /* Excessive collision. */ +#define AE_TXS_SINGLECOL 0x0080 /* Single collision occuried. */ +#define AE_TXS_MULTICOL 0x0100 /* Multiple collisions occuried. */ +#define AE_TXS_LATECOL 0x0200 /* Late collision occuried. */ +#define AE_TXS_ABORTCOL 0x0400 /* Frame abort due to collisions. */ +#define AE_TXS_UNDERRUN 0x0800 /* Tx SRAM underrun occuried. */ +#define AE_TXS_UPDATE 0x8000 + +/* + * RxD flags. + */ +#define AE_RXD_SUCCESS 0x0001 +#define AE_RXD_BCAST 0x0002 /* Broadcast frame received. */ +#define AE_RXD_MCAST 0x0004 /* Multicast frame received. */ +#define AE_RXD_PAUSE 0x0008 /* Pause frame received. */ +#define AE_RXD_CTRL 0x0010 /* Control frame received. */ +#define AE_RXD_CRCERR 0x0020 /* Invalid frame CRC. */ +#define AE_RXD_CODEERR 0x0040 /* Invalid frame opcode. */ +#define AE_RXD_RUNT 0x0080 /* Runt frame received. */ +#define AE_RXD_FRAG 0x0100 /* Collision fragment received. */ +#define AE_RXD_TRUNC 0x0200 /* The frame was truncated due + to Rx SRAM underrun. */ +#define AE_RXD_ALIGN 0x0400 /* Frame alignment error. */ +#define AE_RXD_HAS_VLAN 0x0800 /* VLAN tag present. */ +#define AE_RXD_UPDATE 0x8000 diff --git a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aevar.h b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aevar.h index 33d5cf565f..b63253fe83 100644 --- a/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aevar.h +++ b/src/add-ons/kernel/drivers/network/attansic_l2/dev/ae/if_aevar.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/sys/dev/ae/if_aevar.h,v 1.1.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ */ #ifndef IF_AEVAR_H @@ -61,20 +61,43 @@ typedef struct ae_txd { /* Tx status descriptor format. */ typedef struct ae_txs { uint16_t len; - uint16_t flags:15; - uint16_t update:1; + uint16_t flags; } __packed ae_txs_t; /* Rx packet descriptor format. */ typedef struct ae_rxd { uint16_t len; - uint16_t flags:15; - uint16_t update:1; + uint16_t flags; uint16_t vlan; uint16_t __pad; uint8_t data[1528]; } __packed ae_rxd_t; +/* Statistics. */ +typedef struct ae_stats { + uint32_t rx_bcast; + uint32_t rx_mcast; + uint32_t rx_pause; + uint32_t rx_ctrl; + uint32_t rx_crcerr; + uint32_t rx_codeerr; + uint32_t rx_runt; + uint32_t rx_frag; + uint32_t rx_trunc; + uint32_t rx_align; + uint32_t tx_bcast; + uint32_t tx_mcast; + uint32_t tx_pause; + uint32_t tx_ctrl; + uint32_t tx_defer; + uint32_t tx_excdefer; + uint32_t tx_singlecol; + uint32_t tx_multicol; + uint32_t tx_latecol; + uint32_t tx_abortcol; + uint32_t tx_underrun; +} ae_stats_t; + /* Software state structure. */ typedef struct ae_softc { struct ifnet *ifp; @@ -128,6 +151,8 @@ typedef struct ae_softc { int tx_inproc; /* Active Tx frames in ring. */ int wd_timer; + + ae_stats_t stats; } ae_softc_t; #define AE_LOCK(_sc) mtx_lock(&(_sc)->mtx) @@ -139,6 +164,8 @@ typedef struct ae_softc { #define AE_FLAG_LINK 0x01 /* Has link. */ #define AE_FLAG_DETACH 0x02 /* Is detaching. */ -#define AE_FLAG_TXAVAIL 0x04 /* Txes available. */ +#define AE_FLAG_TXAVAIL 0x04 /* Tx'es available. */ +#define AE_FLAG_MSI 0x08 /* Using MSI. */ +#define AE_FLAG_PMG 0x10 /* Supports PCI power management. */ #endif /* IF_AEVAR_H */