svn commit: r189714 - head/sys/dev/txp

Pyun YongHyeon yongari at FreeBSD.org
Wed Mar 11 18:14:48 PDT 2009


Author: yongari
Date: Thu Mar 12 01:14:47 2009
New Revision: 189714
URL: http://svn.freebsd.org/changeset/base/189714

Log:
  bus_dma(9) conversion and make txp(4) work on all architectures.
  o Header file cleanup.
  o bus_dma(9) conversion.
    - Removed all consumers of vtophys(9) and converted to use
      bus_dma(9).
    - Typhoon2 functional specification says the controller supports
      64bit DMA addressing. However all Typhoon controllers are known
      to lack of DAC support so 64bit DMA support was disabled.
    - The hardware can't handle more 16 fragmented Tx DMA segments so
      teach txp(4) to collapse these segments to be less than 16.
    - Added Rx buffer alignment requirements(4 bytes alignment) and
      implemented fixup code to align receive frame. Previously
      txp(4) always copied Rx frame to align it on 2 byte boundary
      but its copy overhead is much higher than unaligned access on
      i386/amd64. Alignment fixup code is now applied only for
      strict-alignment architectures. With this change i386 and
      amd64 will get instant Rx performance boost. Typhoon2 datasheet
      mentions a command that pads arbitrary bytes in Rx buffer but
      that command does not work.
    - Nuked pointer trick in descriptor ring. This does not work on
      sparc64 and replaced it with bcopy. Alternatively txp(4) can
      embed a 32 bits index value into the descriptor and compute
      real buffer address but it may make code complicated.
    - Added endianness support code in various Tx/Rx/command/response
      descriptor access. With this change txp(4) should work on all
      architectures.
  o Added comments for known firmware bugs(Tx checksum offloading,
    TSO, VLAN stripping and Rx buffer padding control).
  o Prefer faster memory space register access to I/O space access.
    Added fall-back mechanism to use alternative I/O space access.
    The hardware supports both memory and I/O mapped access. Users
    can still force to use old I/O space access by setting
    hw.txp.prefer_iomap tunable to 1 in /boot/loader.conf.
  o Added experimental suspend/resume methods.
  o Nuke error prone Rx buffer handling code and implemented local
    buffer management with TAILQ. Be definition the controller can't
    pass the last received frame to host if no Rx free buffers are
    available to use as head and tail pointer of Rx descriptor ring
    can't have the same value. In that case the Rx buffer pointer in
    Rx buffer ring still holds a valid buffer and txp_rxbuf_reclaim()
    can't fill Rx buffers as the first buffer is still valid. Instead
    of relying on the value of Rx buffer ring, introduce local buffer
    management code to handle empty buffer situation. This should fix
    a long standing bug which completely hangs the controller under
    high network load. I could easily trigger the issue by sending 64
    bytes UDP frames with netperf. I have no idea how this bugs was
    not fixed for a long time.
  o Converted ithread interrupt handler to filter based one.
  o Rearranged txp_detach routine such that it's now used for general
    clean-up routine.
  o Show sleep image version on device attach time. This will help
    to know what action should be taken depending on sleep image
    version. The version information in datasheet was wrong for newer
    NV images so I followed Linux which seems to correctly extract
    version numbers from response descriptors.
  o Firmware image is no longer downloaded in device attach time. Now
    it is reloaded whenever if_init is invoked. This is to ensure
    correct operation of hardware when something goes wrong.
    Previously the controller always run without regard to running
    state of firmware. This change will add additional controller
    initialization time but it give more robust operation as txp(4)
    always start off from a known state. The controller is put into
    sleep state until administrator explicitly up the interface.
  o As firmware is loaded in if_init handler, it's now possible to
    implement real watchdog timeout handler. When watchdog timer is
    expired, full-reset the controller and initialize the hardware
    again as most other drivers do. While I'm here use our own timer
    for watchdog instead of using if_watchdog/if_timer interface.
  o Instead of masking specific interrupts with TXP_IMR register,
    program TXP_IER register with the interrupts to be raised and
    use TXP_IMR to toggle interrupt generation.
  o Implemented txp_wait() to wait a specific state of a controller.
  o Separate boot related code from txp_download_fw() and name it
    txp_boot() to handle boot process.
  o Added bus_barrier(9) to host to ARM communication.
  o Added endianness to all typhoon command processing. The ARM93C
    always expects little-endian format of command/data.
  o Removed __STRICT_ALIGNMENT which is not valid on FreeBSD.
    __NO_STRICT_ALIGNMENT is provided for that purpose on FreeBSD.
    Previously __STRICT_ALIGNMENT was unconditionally defined for
    all architectures.
  o Rewrote SIOCSIFCAP ioctl handler such that each capability can be
    controlled by ifconfig(8). Note, disabling VLAN hardware tagging
    has no effect due to the bug of firmware.
  o Don't send TXP_CMD_CLEAR_STATISTICS to clear MAC statistics in
    txp_tick(). The command is not atomic. Instead, just read the
    statistics and reflect saved statistics to the statistics.
    dev.txp.%d.stats sysctl node provides detailed MAC statistics.
    This also reduces a lot of waste of CPU cycles as processing a
    command ring takes a very long time on ARM93C. Note, Rx
    multicast and broadcast statistics does not seem to right. It
    might be another bug of firmware.
  o Implemented link state change handling in txp_tick(). Now sending
    packets is allowed only after establishing a valid link. Also
    invoke link state change notification whenever its state is
    changed so pseudo drivers like lagg(4) that relies on link state
    can work with failover or link aggregation without hacks.
    if_baudrate is updated to resolved speed so SNMP agents can get
    correct bandwidth parameters.
  o Overhauled Tx routine such that it now honors number of allowable
    DMA segments and checks for 4 free descriptors before trying to
    send a frame. A frame may require  4 descriptors(1 frame
    descriptor, 1 or more frame descriptors, 1 TSO option descriptor,
    one free descriptor to prevent descriptor wrap-around) at least
    so it's necessary to check available free descriptors prior to
    setting up DMA operation.
  o Added a sysctl variable dev.txp.%d.process_limit to control
    how many received frames should be served in Rx handler. Valid
    ranges are 16 to 128(default 64) in unit of frames.
  o Added ALTQ(4) support.
  o Added missing IFCAP_VLAN_HWCSUM as txp(4) can offload checksum
    calculation as well as VLAN tag insertion/stripping.
  o Fixed media header length for VLAN.
  o Don't set if_mtu in device attach, it's already set in
    ether_ifattach().
  o Enabled MWI.
  o Fixed module unload panic when bpf listeners are active.
  o Rearranged ethernet address programming logic such that it works
     on strict-alignment architectures.
  o Removed unused member variables in softc.
  o Added support for WOL.
  o Removed now unused TXP_PCI_LOMEM/TXP_PCI_LOIO.
  o Added wakeup command TXP_BOOTCMD_WAKEUP definition.
  o Added a new firmware version query command, TXP_CMD_READ_VERSION.
  o Removed volatile keyword in softc as bus_dmamap_sync(9) should
    take care of this.
  o Removed embedded union trick of a structure used to to access
    a pointer on LP64 systems.
  o Added a few TSO related definitions for struct txp_tcpseg_desc.
    However TSO is not used at all due to the limitation of hardware.
  o Redefined PKT_MAX_PKTLEN to theoretical maximum size of a frame.
  o Switched from bus_space_{read|write}_4 to bus_{read|write}_4.
  o Added a new macro TXP_DESC_INC to compute next descriptor index.
  
  Tested by:	don.nasco <> gmail dot com

Modified:
  head/sys/dev/txp/if_txp.c
  head/sys/dev/txp/if_txpreg.h

Modified: head/sys/dev/txp/if_txp.c
==============================================================================
--- head/sys/dev/txp/if_txp.c	Thu Mar 12 00:09:29 2009	(r189713)
+++ head/sys/dev/txp/if_txp.c	Thu Mar 12 01:14:47 2009	(r189714)
@@ -42,53 +42,82 @@ __FBSDID("$FreeBSD$");
  */
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/sockio.h>
-#include <sys/mbuf.h>
-#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
 #include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/rman.h>
 #include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
 
+#include <net/bpf.h>
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <net/ethernet.h>
 #include <net/if_dl.h>
+#include <net/if_media.h>
 #include <net/if_types.h>
 #include <net/if_vlan_var.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
-#include <netinet/in_var.h>
 #include <netinet/ip.h>
-#include <netinet/if_ether.h>
-#include <machine/in_cksum.h>
-
-#include <net/if_media.h>
-
-#include <net/bpf.h>
-
-#include <vm/vm.h>              /* for vtophys */
-#include <vm/pmap.h>            /* for vtophys */
-#include <machine/bus.h>
-#include <machine/resource.h>
-#include <sys/bus.h>
-#include <sys/rman.h>
 
 #include <dev/mii/mii.h>
-#include <dev/mii/miivar.h>
+
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 
-#define TXP_USEIOSPACE
-#define __STRICT_ALIGNMENT
+#include <machine/bus.h>
+#include <machine/in_cksum.h>
 
 #include <dev/txp/if_txpreg.h>
 #include <dev/txp/3c990img.h>
 
-#ifndef lint
-static const char rcsid[] =
-  "$FreeBSD$";
-#endif
+MODULE_DEPEND(txp, pci, 1, 1, 1);
+MODULE_DEPEND(txp, ether, 1, 1, 1);
+
+/*
+ * XXX Known Typhoon firmware issues.
+ *
+ * 1. It seems that firmware has Tx TCP/UDP checksum offloading bug.
+ *    The firmware hangs when it's told to compute TCP/UDP checksum.
+ *    I'm not sure whether the firmware requires special alignment to
+ *    do checksum offloading but datasheet says nothing about that.
+ * 2. Datasheet says nothing for maximum number of fragmented
+ *    descriptors supported. Experimentation shows up to 16 fragment
+ *    descriptors are supported in the firmware. For TSO case, upper
+ *    stack can send 64KB sized IP datagram plus link header size(
+ *    ethernet header + VLAN tag)  frame but controller can handle up
+ *    to 64KB frame given that PAGE_SIZE is 4KB(i.e. 16 * PAGE_SIZE).
+ *    Because frames that need TSO operation of hardware can be
+ *    larger than 64KB I disabled TSO capability. TSO operation for
+ *    less than or equal to 16 fragment descriptors works without
+ *    problems, though.
+ * 3. VLAN hardware tag stripping is always enabled in the firmware
+ *    even if it's explicitly told to not strip the tag. It's
+ *    possible to add the tag back in Rx handler if VLAN hardware
+ *    tag is not active but I didn't try that as it would be
+ *    layering violation.
+ * 4. TXP_CMD_RECV_BUFFER_CONTROL does not work as expected in
+ *    datasheet such that driver should handle the alignment
+ *    restriction by copying received frame to align the frame on
+ *    32bit boundary on strict-alignment architectures. This adds a
+ *    lot of CPU burden and it effectively reduce Rx performance on
+ *    strict-alignment architectures(e.g. sparc64, arm, mips and ia64).
+ *
+ * Unfortunately it seems that 3Com have no longer interests in
+ * releasing fixed firmware so we may have to live with these bugs.
+ */
+
+#define	TXP_CSUM_FEATURES	(CSUM_IP)
 
 /*
  * Various supported device vendors/types and their names.
@@ -112,25 +141,36 @@ static struct txp_type txp_devs[] = {
 static int txp_probe(device_t);
 static int txp_attach(device_t);
 static int txp_detach(device_t);
-static void txp_intr(void *);
-static void txp_tick(void *);
 static int txp_shutdown(device_t);
+static int txp_suspend(device_t);
+static int txp_resume(device_t);
+static int txp_intr(void *);
+static void txp_int_task(void *, int);
+static void txp_tick(void *);
 static int txp_ioctl(struct ifnet *, u_long, caddr_t);
 static void txp_start(struct ifnet *);
 static void txp_start_locked(struct ifnet *);
+static int txp_encap(struct txp_softc *, struct txp_tx_ring *, struct mbuf **);
 static void txp_stop(struct txp_softc *);
 static void txp_init(void *);
 static void txp_init_locked(struct txp_softc *);
-static void txp_watchdog(struct ifnet *);
+static void txp_watchdog(struct txp_softc *);
 
-static void txp_release_resources(struct txp_softc *);
-static int txp_chip_init(struct txp_softc *);
-static int txp_reset_adapter(struct txp_softc *);
+static int txp_reset(struct txp_softc *);
+static int txp_boot(struct txp_softc *, uint32_t);
+static int txp_sleep(struct txp_softc *, int);
+static int txp_wait(struct txp_softc *, uint32_t);
 static int txp_download_fw(struct txp_softc *);
 static int txp_download_fw_wait(struct txp_softc *);
 static int txp_download_fw_section(struct txp_softc *,
     struct txp_fw_section_header *, int);
 static int txp_alloc_rings(struct txp_softc *);
+static void txp_init_rings(struct txp_softc *);
+static int txp_dma_alloc(struct txp_softc *, char *, bus_dma_tag_t *,
+    bus_size_t, bus_size_t, bus_dmamap_t *, void **, bus_size_t, bus_addr_t *);
+static void txp_dma_free(struct txp_softc *, bus_dma_tag_t *, bus_dmamap_t *,
+    void **);
+static void txp_free_rings(struct txp_softc *);
 static int txp_rxring_fill(struct txp_softc *);
 static void txp_rxring_empty(struct txp_softc *);
 static void txp_set_filter(struct txp_softc *);
@@ -138,14 +178,14 @@ static void txp_set_filter(struct txp_so
 static int txp_cmd_desc_numfree(struct txp_softc *);
 static int txp_command(struct txp_softc *, uint16_t, uint16_t, uint32_t,
     uint32_t, uint16_t *, uint32_t *, uint32_t *, int);
-static int txp_command2(struct txp_softc *, uint16_t, uint16_t,
+static int txp_ext_command(struct txp_softc *, uint16_t, uint16_t,
     uint32_t, uint32_t, struct txp_ext_desc *, uint8_t,
     struct txp_rsp_desc **, int);
-static int txp_response(struct txp_softc *, uint32_t, uint16_t, uint16_t,
+static int txp_response(struct txp_softc *, uint16_t, uint16_t,
     struct txp_rsp_desc **);
 static void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
     struct txp_rsp_desc *);
-static void txp_capabilities(struct txp_softc *);
+static int txp_set_capabilities(struct txp_softc *);
 
 static void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
 static int txp_ifmedia_upd(struct ifnet *);
@@ -154,15 +194,18 @@ static void txp_show_descriptor(void *);
 #endif
 static void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *);
 static void txp_rxbuf_reclaim(struct txp_softc *);
-static void txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *);
-
-#ifdef TXP_USEIOSPACE
-#define TXP_RES			SYS_RES_IOPORT
-#define TXP_RID			TXP_PCI_LOIO
-#else
-#define TXP_RES			SYS_RES_MEMORY
-#define TXP_RID			TXP_PCI_LOMEM
+#ifndef __NO_STRICT_ALIGNMENT
+static __inline void txp_fixup_rx(struct mbuf *);
 #endif
+static int txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *, int);
+static void txp_stats_save(struct txp_softc *);
+static void txp_stats_update(struct txp_softc *, struct txp_rsp_desc *);
+static void txp_sysctl_node(struct txp_softc *);
+static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
+static int sysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS);
+
+static int prefer_iomap = 0;
+TUNABLE_INT("hw.txp.prefer_iomap", &prefer_iomap);
 
 static device_method_t txp_methods[] = {
         /* Device interface */
@@ -170,7 +213,10 @@ static device_method_t txp_methods[] = {
 	DEVMETHOD(device_attach,	txp_attach),
 	DEVMETHOD(device_detach,	txp_detach),
 	DEVMETHOD(device_shutdown,	txp_shutdown),
-	{ 0, 0 }
+	DEVMETHOD(device_suspend,	txp_suspend),
+	DEVMETHOD(device_resume,	txp_resume),
+
+	{ NULL, NULL }
 };
 
 static driver_t txp_driver = {
@@ -182,8 +228,6 @@ static driver_t txp_driver = {
 static devclass_t txp_devclass;
 
 DRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0);
-MODULE_DEPEND(txp, pci, 1, 1, 1);
-MODULE_DEPEND(txp, ether, 1, 1, 1);
 
 static int
 txp_probe(device_t dev)
@@ -209,36 +253,65 @@ txp_attach(device_t dev)
 {
 	struct txp_softc *sc;
 	struct ifnet *ifp;
+	struct txp_rsp_desc *rsp;
 	uint16_t p1;
-	uint32_t p2;
-	int error = 0, rid;
-	u_char eaddr[6];
+	uint32_t p2, reg;
+	int error = 0, pmc, rid;
+	uint8_t eaddr[ETHER_ADDR_LEN], *ver;
 
 	sc = device_get_softc(dev);
 	sc->sc_dev = dev;
-	sc->sc_cold = 1;
 
 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
 	    MTX_DEF);
 	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
+	TASK_INIT(&sc->sc_int_task, 0, txp_int_task, sc);
+	TAILQ_INIT(&sc->sc_busy_list);
+	TAILQ_INIT(&sc->sc_free_list);
 
-	/*
-	 * Map control/status registers.
-	 */
-	pci_enable_busmaster(dev);
-
-	rid = TXP_RID;
-	sc->sc_res = bus_alloc_resource_any(dev, TXP_RES, &rid,
-	    RF_ACTIVE);
+	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
+	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
 
+	pci_enable_busmaster(dev);
+	/* Prefer memory space register mapping over IO space. */
+	if (prefer_iomap == 0) {
+		sc->sc_res_id = PCIR_BAR(1);
+		sc->sc_res_type = SYS_RES_MEMORY;
+	} else {
+		sc->sc_res_id = PCIR_BAR(0);
+		sc->sc_res_type = SYS_RES_IOPORT;
+	}
+	sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
+	    &sc->sc_res_id, RF_ACTIVE);
+	if (sc->sc_res == NULL && prefer_iomap == 0) {
+		sc->sc_res_id = PCIR_BAR(0);
+		sc->sc_res_type = SYS_RES_IOPORT;
+		sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type,
+		    &sc->sc_res_id, RF_ACTIVE);
+	}
 	if (sc->sc_res == NULL) {
 		device_printf(dev, "couldn't map ports/memory\n");
-		error = ENXIO;
-		goto fail;
+		ifmedia_removeall(&sc->sc_ifmedia);
+		mtx_destroy(&sc->sc_mtx);
+		return (ENXIO);
 	}
 
-	sc->sc_bt = rman_get_bustag(sc->sc_res);
-	sc->sc_bh = rman_get_bushandle(sc->sc_res);
+	/* Enable MWI. */
+	reg = pci_read_config(dev, PCIR_COMMAND, 2);
+	reg |= PCIM_CMD_MWRICEN;
+	pci_write_config(dev, PCIR_COMMAND, reg, 2);
+	/* Check cache line size. */
+	reg = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+	reg <<= 4;
+	if (reg == 0 || (reg % 16) != 0)
+		device_printf(sc->sc_dev,
+		    "invalid cache line size : %u\n", reg);
 
 	/* Allocate interrupt */
 	rid = 0;
@@ -251,112 +324,155 @@ txp_attach(device_t dev)
 		goto fail;
 	}
 
-	if (txp_chip_init(sc)) {
-		error = ENXIO;
-		goto fail;
-	}
-
-	sc->sc_fwbuf = contigmalloc(32768, M_DEVBUF,
-	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
-	if (sc->sc_fwbuf == NULL) {
-		device_printf(dev, "no memory for firmware\n");
-		error = ENXIO;
-		goto fail;
-	}
-	error = txp_download_fw(sc);
-	contigfree(sc->sc_fwbuf, 32768, M_DEVBUF);
-	sc->sc_fwbuf = NULL;
-
-	if (error)
+	if ((error = txp_alloc_rings(sc)) != 0)
 		goto fail;
-
-	sc->sc_ldata = contigmalloc(sizeof(struct txp_ldata), M_DEVBUF,
-	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
-	if (sc->sc_ldata == NULL) {
-		device_printf(dev, "no memory for descriptor ring\n");
-		error = ENXIO;
-		goto fail;
-	}
-	bzero(sc->sc_ldata, sizeof(struct txp_ldata));
-
-	if (txp_alloc_rings(sc)) {
+	txp_init_rings(sc);
+	txp_sysctl_node(sc);
+	/* Reset controller and make it reload sleep image. */
+	if (txp_reset(sc) != 0) {
 		error = ENXIO;
 		goto fail;
 	}
 
-	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
-	    NULL, NULL, NULL, 1)) {
+	/* Let controller boot from sleep image. */
+	if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
+		device_printf(sc->sc_dev, "could not boot sleep image\n");
 		error = ENXIO;
 		goto fail;
 	}
 
+	/* Get station address. */
 	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
-	    &p1, &p2, NULL, 1)) {
+	    &p1, &p2, NULL, TXP_CMD_WAIT)) {
 		error = ENXIO;
 		goto fail;
 	}
 
+	p1 = le16toh(p1);
 	eaddr[0] = ((uint8_t *)&p1)[1];
 	eaddr[1] = ((uint8_t *)&p1)[0];
+	p2 = le32toh(p2);
 	eaddr[2] = ((uint8_t *)&p2)[3];
 	eaddr[3] = ((uint8_t *)&p2)[2];
 	eaddr[4] = ((uint8_t *)&p2)[1];
 	eaddr[5] = ((uint8_t *)&p2)[0];
 
-	sc->sc_cold = 0;
-
-	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
-	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
-
-	sc->sc_xcvr = TXP_XCVR_AUTO;
-	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
-	    NULL, NULL, NULL, 0);
-	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO);
-
 	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
 	if (ifp == NULL) {
-		device_printf(dev, "can not if_alloc()\n");
+		device_printf(dev, "can not allocate ifnet structure\n");
 		error = ENOSPC;
 		goto fail;
 	}
+
+	/*
+	 * Show sleep image version information which may help to
+	 * diagnose sleep image specific issues.
+	 */
+	rsp = NULL;
+	if (txp_ext_command(sc, TXP_CMD_READ_VERSION, 0, 0, 0, NULL, 0,
+	    &rsp, TXP_CMD_WAIT)) {
+		device_printf(dev, "can not read sleep image version\n");
+		error = ENXIO;
+		goto fail;
+	}
+	if (rsp->rsp_numdesc == 0) {
+		p2 = le32toh(rsp->rsp_par2) & 0xFFFF;
+		device_printf(dev, "Typhoon 1.0 sleep image (2000/%02u/%02u)\n",
+		    p2 >> 8, p2 & 0xFF);
+	} else if (rsp->rsp_numdesc == 2) {
+		p2 = le32toh(rsp->rsp_par2);
+		ver = (uint8_t *)(rsp + 1);
+		/*
+		 * Even if datasheet says the command returns a NULL
+		 * terminated version string, explicitly terminate
+		 * the string. Given that several bugs of firmware
+		 * I can't trust this simple one.
+		 */
+		ver[25] = '\0';
+		device_printf(dev,
+		    "Typhoon 1.1+ sleep image %02u.%03u.%03u %s\n",
+		    p2 >> 24, (p2 >> 12) & 0xFFF, p2 & 0xFFF, ver);
+	} else {
+		p2 = le32toh(rsp->rsp_par2);
+		device_printf(dev,
+		    "Unknown Typhoon sleep image version: %u:0x%08x\n",
+		    rsp->rsp_numdesc, p2);
+	}
+	if (rsp != NULL)
+		free(rsp, M_DEVBUF);
+
+	sc->sc_xcvr = TXP_XCVR_AUTO;
+	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
+	    NULL, NULL, NULL, TXP_CMD_NOWAIT);
+	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
+
 	ifp->if_softc = sc;
 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
-	ifp->if_mtu = ETHERMTU;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = txp_ioctl;
 	ifp->if_start = txp_start;
-	ifp->if_watchdog = txp_watchdog;
 	ifp->if_init = txp_init;
-	ifp->if_baudrate = 100000000;
-	ifp->if_snd.ifq_maxlen = TX_ENTRIES;
-	ifp->if_hwassist = 0;
-	txp_capabilities(sc);
-
+	ifp->if_snd.ifq_drv_maxlen = TX_ENTRIES - 1;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+	IFQ_SET_READY(&ifp->if_snd);
 	/*
-	 * Attach us everywhere
+	 * It's possible to read firmware's offload capability but
+	 * we have not downloaded the firmware yet so announce
+	 * working capability here. We're not interested in IPSec
+	 * capability and due to the lots of firmware bug we can't
+	 * advertise the whole capability anyway.
 	 */
+	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM;
+	if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0)
+		ifp->if_capabilities |= IFCAP_WOL_MAGIC;
+	/* Enable all capabilities. */
+	ifp->if_capenable = ifp->if_capabilities;
+
 	ether_ifattach(ifp, eaddr);
 
+	/* VLAN capability setup. */
+	ifp->if_capabilities |= IFCAP_VLAN_MTU;
+	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
+	ifp->if_capenable = ifp->if_capabilities;
+	/* Tell the upper layer(s) we support long frames. */
+	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+
+	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
+	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
+
+	/* Create local taskq. */
+	sc->sc_tq = taskqueue_create_fast("txp_taskq", M_WAITOK,
+	    taskqueue_thread_enqueue, &sc->sc_tq);
+	if (sc->sc_tq == NULL) {
+		device_printf(dev, "could not create taskqueue.\n");
+		ether_ifdetach(ifp);
+		error = ENXIO;
+		goto fail;
+	}
+	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
+	    device_get_nameunit(sc->sc_dev));
+
+	/* Put controller into sleep. */
+	if (txp_sleep(sc, 0) != 0) {
+		ether_ifdetach(ifp);
+		error = ENXIO;
+		goto fail;
+	}
+
 	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
-	    NULL, txp_intr, sc, &sc->sc_intrhand);
+	    txp_intr, NULL, sc, &sc->sc_intrhand);
 
-	if (error) {
+	if (error != 0) {
 		ether_ifdetach(ifp);
-		device_printf(dev, "couldn't set up irq\n");
+		device_printf(dev, "couldn't set up interrupt handler.\n");
 		goto fail;
 	}
 
 	return (0);
 
 fail:
-	txp_release_resources(sc);
-	mtx_destroy(&sc->sc_mtx);
+	if (error != 0)
+		txp_detach(dev);
 	return (error);
 }
 
@@ -365,101 +481,57 @@ txp_detach(device_t dev)
 {
 	struct txp_softc *sc;
 	struct ifnet *ifp;
-	int i;
 
 	sc = device_get_softc(dev);
-	ifp = sc->sc_ifp;
 
-	TXP_LOCK(sc);
-	txp_stop(sc);
-	TXP_UNLOCK(sc);
-	txp_shutdown(dev);
-	callout_drain(&sc->sc_tick);
+	ifp = sc->sc_ifp;
+	if (device_is_attached(dev)) {
+		TXP_LOCK(sc);
+		sc->sc_flags |= TXP_FLAG_DETACH;
+		txp_stop(sc);
+		TXP_UNLOCK(sc);
+		callout_drain(&sc->sc_tick);
+		taskqueue_drain(sc->sc_tq, &sc->sc_int_task);
+		ether_ifdetach(ifp);
+	}
+	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
 
 	ifmedia_removeall(&sc->sc_ifmedia);
-	ether_ifdetach(ifp);
-
-	for (i = 0; i < RXBUF_ENTRIES; i++)
-		free(sc->sc_rxbufs[i].rb_sd, M_DEVBUF);
-
-	txp_release_resources(sc);
-
-	mtx_destroy(&sc->sc_mtx);
-	return (0);
-}
-
-static void
-txp_release_resources(struct txp_softc *sc)
-{
-	device_t dev;
-
-	dev = sc->sc_dev;
-
 	if (sc->sc_intrhand != NULL)
 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
-
 	if (sc->sc_irq != NULL)
 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
-
 	if (sc->sc_res != NULL)
-		bus_release_resource(dev, TXP_RES, TXP_RID, sc->sc_res);
-
-	if (sc->sc_ldata != NULL)
-		contigfree(sc->sc_ldata, sizeof(struct txp_ldata), M_DEVBUF);
-
-	if (sc->sc_ifp)
+		bus_release_resource(dev, sc->sc_res_type, sc->sc_res_id,
+		    sc->sc_res);
+	if (sc->sc_ifp != NULL) {
 		if_free(sc->sc_ifp);
-}
-
-static int
-txp_chip_init(struct txp_softc *sc)
-{
-	/* disable interrupts */
-	WRITE_REG(sc, TXP_IER, 0);
-	WRITE_REG(sc, TXP_IMR,
-	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
-	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
-	    TXP_INT_LATCH);
-
-	/* ack all interrupts */
-	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
-	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
-	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
-	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
-	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
-
-	if (txp_reset_adapter(sc))
-		return (-1);
-
-	/* disable interrupts */
-	WRITE_REG(sc, TXP_IER, 0);
-	WRITE_REG(sc, TXP_IMR,
-	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
-	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
-	    TXP_INT_LATCH);
-
-	/* ack all interrupts */
-	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
-	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
-	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
-	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
-	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
+		sc->sc_ifp = NULL;
+	}
+	txp_free_rings(sc);
+	mtx_destroy(&sc->sc_mtx);
 
 	return (0);
 }
 
 static int
-txp_reset_adapter(struct txp_softc *sc)
+txp_reset(struct txp_softc *sc)
 {
 	uint32_t r;
 	int i;
 
+	/* Disable interrupts. */
+	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
+	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
+	/* Ack all pending interrupts. */
+	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
+
 	r = 0;
 	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
 	DELAY(1000);
 	WRITE_REG(sc, TXP_SRR, 0);
 
-	/* Should wait max 6 seconds */
+	/* Should wait max 6 seconds. */
 	for (i = 0; i < 6000; i++) {
 		r = READ_REG(sc, TXP_A2H_0);
 		if (r == STAT_WAITING_FOR_HOST_REQUEST)
@@ -467,11 +539,55 @@ txp_reset_adapter(struct txp_softc *sc)
 		DELAY(1000);
 	}
 
-	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
+	if (r != STAT_WAITING_FOR_HOST_REQUEST)
 		device_printf(sc->sc_dev, "reset hung\n");
-		return (-1);
+
+	WRITE_REG(sc, TXP_IER, TXP_INTR_NONE);
+	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
+	WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL);
+
+	/*
+	 * Give more time to complete loading sleep image before
+	 * trying to boot from sleep image.
+	 */
+	DELAY(5000);
+
+	return (0);
+}
+
+static int
+txp_boot(struct txp_softc *sc, uint32_t state)
+{
+
+	/* See if it's waiting for boot, and try to boot it. */
+	if (txp_wait(sc, state) != 0) {
+		device_printf(sc->sc_dev, "not waiting for boot\n");
+		return (ENXIO);
+	}
+
+	WRITE_REG(sc, TXP_H2A_2, TXP_ADDR_HI(sc->sc_ldata.txp_boot_paddr));
+	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_1, TXP_ADDR_LO(sc->sc_ldata.txp_boot_paddr));
+	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
+	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
+
+	/* See if it booted. */
+	if (txp_wait(sc, STAT_RUNNING) != 0) {
+		device_printf(sc->sc_dev, "firmware not running\n");
+		return (ENXIO);
 	}
 
+	/* Clear TX and CMD ring write registers. */
+	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
+	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
+	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
+	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
+	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
+
 	return (0);
 }
 
@@ -480,10 +596,11 @@ txp_download_fw(struct txp_softc *sc)
 {
 	struct txp_fw_file_header *fileheader;
 	struct txp_fw_section_header *secthead;
-	int error, sect;
-	uint32_t r, i, ier, imr;
+	int sect;
+	uint32_t error, ier, imr;
+
+	TXP_LOCK_ASSERT(sc);
 
-	r = 0;
 	error = 0;
 	ier = READ_REG(sc, TXP_IER);
 	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
@@ -491,68 +608,60 @@ txp_download_fw(struct txp_softc *sc)
 	imr = READ_REG(sc, TXP_IMR);
 	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
 
-	for (i = 0; i < 10000; i++) {
-		r = READ_REG(sc, TXP_A2H_0);
-		if (r == STAT_WAITING_FOR_HOST_REQUEST)
-			break;
-		DELAY(50);
-	}
-	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
+	if (txp_wait(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) {
 		device_printf(sc->sc_dev, "not waiting for host request\n");
-		error = -1;
+		error = ETIMEDOUT;
 		goto fail;
 	}
 
-	/* Ack the status */
+	/* Ack the status. */
 	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
 
 	fileheader = (struct txp_fw_file_header *)tc990image;
 	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
-		device_printf(sc->sc_dev, "fw invalid magic\n");
-		error = -1;
+		device_printf(sc->sc_dev, "firmware invalid magic\n");
 		goto fail;
 	}
 
-	/* Tell boot firmware to get ready for image */
-	WRITE_REG(sc, TXP_H2A_1, fileheader->addr);
-	WRITE_REG(sc, TXP_H2A_2, fileheader->hmac[0]);
-	WRITE_REG(sc, TXP_H2A_3, fileheader->hmac[1]);
-	WRITE_REG(sc, TXP_H2A_4, fileheader->hmac[2]);
-	WRITE_REG(sc, TXP_H2A_5, fileheader->hmac[3]);
-	WRITE_REG(sc, TXP_H2A_6, fileheader->hmac[4]);
+	/* Tell boot firmware to get ready for image. */
+	WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr));
+	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_2, le32toh(fileheader->hmac[0]));
+	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_3, le32toh(fileheader->hmac[1]));
+	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_4, le32toh(fileheader->hmac[2]));
+	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_5, le32toh(fileheader->hmac[3]));
+	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_6, le32toh(fileheader->hmac[4]));
+	TXP_BARRIER(sc, TXP_H2A_6, 4, BUS_SPACE_BARRIER_WRITE);
 	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
+	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
 
 	if (txp_download_fw_wait(sc)) {
-		device_printf(sc->sc_dev, "fw wait failed, initial\n");
-		error = -1;
+		device_printf(sc->sc_dev, "firmware wait failed, initial\n");
+		error = ETIMEDOUT;
 		goto fail;
 	}
 
 	secthead = (struct txp_fw_section_header *)(((uint8_t *)tc990image) +
 	    sizeof(struct txp_fw_file_header));
 
-	for (sect = 0; sect < fileheader->nsections; sect++) {
-
-		if (txp_download_fw_section(sc, secthead, sect)) {
-			error = -1;
+	for (sect = 0; sect < le32toh(fileheader->nsections); sect++) {
+		if ((error = txp_download_fw_section(sc, secthead, sect)) != 0)
 			goto fail;
-		}
 		secthead = (struct txp_fw_section_header *)
-		    (((uint8_t *)secthead) + secthead->nbytes +
+		    (((uint8_t *)secthead) + le32toh(secthead->nbytes) +
 		    sizeof(*secthead));
 	}
 
 	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
+	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
 
-	for (i = 0; i < 10000; i++) {
-		r = READ_REG(sc, TXP_A2H_0);
-		if (r == STAT_WAITING_FOR_BOOT)
-			break;
-		DELAY(50);
-	}
-	if (r != STAT_WAITING_FOR_BOOT) {
+	if (txp_wait(sc, STAT_WAITING_FOR_BOOT) != 0) {
 		device_printf(sc->sc_dev, "not waiting for boot\n");
-		error = -1;
+		error = ETIMEDOUT;
 		goto fail;
 	}
 
@@ -566,27 +675,26 @@ fail:
 static int
 txp_download_fw_wait(struct txp_softc *sc)
 {
-	uint32_t i, r;
+	uint32_t i;
 
-	r = 0;
-	for (i = 0; i < 10000; i++) {
-		r = READ_REG(sc, TXP_ISR);
-		if (r & TXP_INT_A2H_0)
+	TXP_LOCK_ASSERT(sc);
+
+	for (i = 0; i < TXP_TIMEOUT; i++) {
+		if ((READ_REG(sc, TXP_ISR) & TXP_INT_A2H_0) != 0)
 			break;
 		DELAY(50);
 	}
 
-	if (!(r & TXP_INT_A2H_0)) {
-		device_printf(sc->sc_dev, "fw wait failed comm0\n");
-		return (-1);
+	if (i == TXP_TIMEOUT) {
+		device_printf(sc->sc_dev, "firmware wait failed comm0\n");
+		return (ETIMEDOUT);
 	}
 
 	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
 
-	r = READ_REG(sc, TXP_A2H_0);
-	if (r != STAT_WAITING_FOR_SEGMENT) {
-		device_printf(sc->sc_dev, "fw not waiting for segment\n");
-		return (-1);
+	if (READ_REG(sc, TXP_A2H_0) != STAT_WAITING_FOR_SEGMENT) {
+		device_printf(sc->sc_dev, "firmware not waiting for segment\n");
+		return (ETIMEDOUT);
 	}
 	return (0);
 }
@@ -595,180 +703,262 @@ static int
 txp_download_fw_section(struct txp_softc *sc,
     struct txp_fw_section_header *sect, int sectnum)
 {
-	vm_offset_t dma;
+	bus_dma_tag_t sec_tag;
+	bus_dmamap_t sec_map;
+	bus_addr_t sec_paddr;
+	uint8_t *sec_buf;
 	int rseg, err = 0;
 	struct mbuf m;
 	uint16_t csum;
 
-	/* Skip zero length sections */
-	if (sect->nbytes == 0)
+	TXP_LOCK_ASSERT(sc);
+
+	/* Skip zero length sections. */
+	if (le32toh(sect->nbytes) == 0)
 		return (0);
 
-	/* Make sure we aren't past the end of the image */
+	/* Make sure we aren't past the end of the image. */
 	rseg = ((uint8_t *)sect) - ((uint8_t *)tc990image);
 	if (rseg >= sizeof(tc990image)) {
-		device_printf(sc->sc_dev, "fw invalid section address, "
-		    "section %d\n", sectnum);
-		return (-1);
+		device_printf(sc->sc_dev,
+		    "firmware invalid section address, section %d\n", sectnum);
+		return (EIO);
 	}
 
-	/* Make sure this section doesn't go past the end */
-	rseg += sect->nbytes;
+	/* Make sure this section doesn't go past the end. */
+	rseg += le32toh(sect->nbytes);
 	if (rseg >= sizeof(tc990image)) {
-		device_printf(sc->sc_dev, "fw truncated section %d\n",
+		device_printf(sc->sc_dev, "firmware truncated section %d\n",
 		    sectnum);
-		return (-1);
+		return (EIO);
 	}
 
-	bcopy(((uint8_t *)sect) + sizeof(*sect), sc->sc_fwbuf, sect->nbytes);
-	dma = vtophys(sc->sc_fwbuf);
+	sec_tag = NULL;
+	sec_map = NULL;
+	sec_buf = NULL;
+	/* XXX */
+	TXP_UNLOCK(sc);
+	err = txp_dma_alloc(sc, "firmware sections", &sec_tag, sizeof(uint32_t),
+	    0, &sec_map, (void **)&sec_buf, le32toh(sect->nbytes), &sec_paddr);
+	TXP_LOCK(sc);
+	if (err != 0)
+		goto bail;
+	bcopy(((uint8_t *)sect) + sizeof(*sect), sec_buf,
+	    le32toh(sect->nbytes));
 
 	/*
 	 * dummy up mbuf and verify section checksum
 	 */
 	m.m_type = MT_DATA;
 	m.m_next = m.m_nextpkt = NULL;
-	m.m_len = sect->nbytes;
-	m.m_data = sc->sc_fwbuf;
+	m.m_len = le32toh(sect->nbytes);
+	m.m_data = sec_buf;
 	m.m_flags = 0;
-	csum = in_cksum(&m, sect->nbytes);
+	csum = in_cksum(&m, le32toh(sect->nbytes));
 	if (csum != sect->cksum) {
-		device_printf(sc->sc_dev, "fw section %d, bad "
-		    "cksum (expected 0x%x got 0x%x)\n",
-		    sectnum, sect->cksum, csum);
-		err = -1;
+		device_printf(sc->sc_dev,
+		    "firmware section %d, bad cksum (expected 0x%x got 0x%x)\n",
+		    sectnum, le16toh(sect->cksum), csum);
+		err = EIO;
 		goto bail;
 	}
 
-	WRITE_REG(sc, TXP_H2A_1, sect->nbytes);
-	WRITE_REG(sc, TXP_H2A_2, sect->cksum);
-	WRITE_REG(sc, TXP_H2A_3, sect->addr);
-	WRITE_REG(sc, TXP_H2A_4, 0);
-	WRITE_REG(sc, TXP_H2A_5, dma & 0xffffffff);
+	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_PREWRITE);
+
+	WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes));
+	TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_2, le16toh(sect->cksum));
+	TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr));
+	TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_4, TXP_ADDR_HI(sec_paddr));
+	TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE);
+	WRITE_REG(sc, TXP_H2A_5, TXP_ADDR_LO(sec_paddr));
+	TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE);
 	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
+	TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE);
 
 	if (txp_download_fw_wait(sc)) {
-		device_printf(sc->sc_dev, "fw wait failed, "
-		    "section %d\n", sectnum);
-		err = -1;
+		device_printf(sc->sc_dev,
+		    "firmware wait failed, section %d\n", sectnum);
+		err = ETIMEDOUT;
 	}
 
+	bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_POSTWRITE);
 bail:
+	txp_dma_free(sc, &sec_tag, &sec_map, (void **)&sec_buf);
 	return (err);
 }
 
-static void
+static int
 txp_intr(void *vsc)
 {
-	struct txp_softc *sc = vsc;
-	struct txp_hostvar *hv = sc->sc_hostvar;
+	struct txp_softc *sc;
+	uint32_t status;
+
+	sc = vsc;
+	status = READ_REG(sc, TXP_ISR);
+	if ((status & TXP_INT_LATCH) == 0)
+		return (FILTER_STRAY);
+	WRITE_REG(sc, TXP_ISR, status);
+	WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL);
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task);
+
+	return (FILTER_HANDLED);
+}
+
+static void
+txp_int_task(void *arg, int pending)
+{
+	struct txp_softc *sc;
+	struct ifnet *ifp;
+	struct txp_hostvar *hv;
 	uint32_t isr;
+	int more;
 

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list