if_txp.c revision 152315
180219Swpaul/*	$OpenBSD: if_txp.c,v 1.48 2001/06/27 06:34:50 kjc Exp $	*/
280219Swpaul
3139749Simp/*-
480219Swpaul * Copyright (c) 2001
580219Swpaul *	Jason L. Wright <jason@thought.net>, Theo de Raadt, and
680219Swpaul *	Aaron Campbell <aaron@monkey.org>.  All rights reserved.
780219Swpaul *
880219Swpaul * Redistribution and use in source and binary forms, with or without
980219Swpaul * modification, are permitted provided that the following conditions
1080219Swpaul * are met:
1180219Swpaul * 1. Redistributions of source code must retain the above copyright
1280219Swpaul *    notice, this list of conditions and the following disclaimer.
1380219Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1480219Swpaul *    notice, this list of conditions and the following disclaimer in the
1580219Swpaul *    documentation and/or other materials provided with the distribution.
1680219Swpaul * 3. All advertising materials mentioning features or use of this software
1780219Swpaul *    must display the following acknowledgement:
1880219Swpaul *	This product includes software developed by Jason L. Wright,
1980219Swpaul *	Theo de Raadt and Aaron Campbell.
2080219Swpaul * 4. Neither the name of the author nor the names of any co-contributors
2180219Swpaul *    may be used to endorse or promote products derived from this software
2280219Swpaul *    without specific prior written permission.
2380219Swpaul *
2480219Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2580219Swpaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2680219Swpaul * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2780219Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2880219Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2980219Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3080219Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3180219Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3280219Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3380219Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3480219Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3580219Swpaul */
3680219Swpaul
37119418Sobrien#include <sys/cdefs.h>
38119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/txp/if_txp.c 152315 2005-11-11 16:04:59Z ru $");
39119418Sobrien
4080219Swpaul/*
4180219Swpaul * Driver for 3c990 (Typhoon) Ethernet ASIC
4280219Swpaul */
4380219Swpaul
44113038Sobrien#include <sys/cdefs.h>
45113038Sobrien__FBSDID("$FreeBSD: head/sys/dev/txp/if_txp.c 152315 2005-11-11 16:04:59Z ru $");
46113038Sobrien
4780219Swpaul#include <sys/param.h>
4880219Swpaul#include <sys/systm.h>
4980219Swpaul#include <sys/sockio.h>
5080219Swpaul#include <sys/mbuf.h>
5180219Swpaul#include <sys/malloc.h>
5280219Swpaul#include <sys/kernel.h>
53129879Sphk#include <sys/module.h>
5480219Swpaul#include <sys/socket.h>
5580219Swpaul
5680219Swpaul#include <net/if.h>
5780219Swpaul#include <net/if_arp.h>
5880219Swpaul#include <net/ethernet.h>
5980219Swpaul#include <net/if_dl.h>
6080219Swpaul#include <net/if_types.h>
6183115Sbrooks#include <net/if_vlan_var.h>
6280219Swpaul
6380219Swpaul#include <netinet/in.h>
6480219Swpaul#include <netinet/in_systm.h>
6580219Swpaul#include <netinet/in_var.h>
6680219Swpaul#include <netinet/ip.h>
6780219Swpaul#include <netinet/if_ether.h>
6880219Swpaul#include <machine/in_cksum.h>
6980219Swpaul
7080219Swpaul#include <net/if_media.h>
7180219Swpaul
7280219Swpaul#include <net/bpf.h>
7380219Swpaul
7480219Swpaul#include <vm/vm.h>              /* for vtophys */
7580219Swpaul#include <vm/pmap.h>            /* for vtophys */
7680219Swpaul#include <machine/clock.h>	/* for DELAY */
7780219Swpaul#include <machine/bus.h>
7880219Swpaul#include <machine/resource.h>
7980219Swpaul#include <sys/bus.h>
8080219Swpaul#include <sys/rman.h>
8180219Swpaul
8280219Swpaul#include <dev/mii/mii.h>
8380219Swpaul#include <dev/mii/miivar.h>
8480219Swpaul#include <dev/pci/pcireg.h>
8580219Swpaul#include <dev/pci/pcivar.h>
8680219Swpaul
8780219Swpaul#define TXP_USEIOSPACE
8880229Swpaul#define __STRICT_ALIGNMENT
8980219Swpaul
9080219Swpaul#include <dev/txp/if_txpreg.h>
9180219Swpaul#include <dev/txp/3c990img.h>
9280219Swpaul
9380219Swpaul#ifndef lint
9480219Swpaulstatic const char rcsid[] =
9580219Swpaul  "$FreeBSD: head/sys/dev/txp/if_txp.c 152315 2005-11-11 16:04:59Z ru $";
9680219Swpaul#endif
9780219Swpaul
9880219Swpaul/*
9980219Swpaul * Various supported device vendors/types and their names.
10080219Swpaul */
10180219Swpaulstatic struct txp_type txp_devs[] = {
10280219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95,
10380219Swpaul	    "3Com 3cR990-TX-95 Etherlink with 3XP Processor" },
10480219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97,
10580219Swpaul	    "3Com 3cR990-TX-97 Etherlink with 3XP Processor" },
10680219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM,
10780219Swpaul	    "3Com 3cR990B-TXM Etherlink with 3XP Processor" },
10880219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95,
10980219Swpaul	    "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" },
11080219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97,
11180219Swpaul	    "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" },
11280219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV,
11380219Swpaul	    "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" },
11480219Swpaul	{ 0, 0, NULL }
11580219Swpaul};
11680219Swpaul
117149678Sjhbstatic int txp_probe(device_t);
118149678Sjhbstatic int txp_attach(device_t);
119149678Sjhbstatic int txp_detach(device_t);
120149678Sjhbstatic void txp_intr(void *);
121149678Sjhbstatic void txp_tick(void *);
122149678Sjhbstatic int txp_shutdown(device_t);
123149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t);
124149678Sjhbstatic void txp_start(struct ifnet *);
125151772Sjhbstatic void txp_start_locked(struct ifnet *);
126149678Sjhbstatic void txp_stop(struct txp_softc *);
127149678Sjhbstatic void txp_init(void *);
128151772Sjhbstatic void txp_init_locked(struct txp_softc *);
129149678Sjhbstatic void txp_watchdog(struct ifnet *);
13080219Swpaul
13192739Salfredstatic void txp_release_resources(struct txp_softc *);
13292739Salfredstatic int txp_chip_init(struct txp_softc *);
13392739Salfredstatic int txp_reset_adapter(struct txp_softc *);
13492739Salfredstatic int txp_download_fw(struct txp_softc *);
13592739Salfredstatic int txp_download_fw_wait(struct txp_softc *);
136149678Sjhbstatic int txp_download_fw_section(struct txp_softc *,
13792739Salfred    struct txp_fw_section_header *, int);
13892739Salfredstatic int txp_alloc_rings(struct txp_softc *);
13992739Salfredstatic int txp_rxring_fill(struct txp_softc *);
14092739Salfredstatic void txp_rxring_empty(struct txp_softc *);
14192739Salfredstatic void txp_set_filter(struct txp_softc *);
14280219Swpaul
14392739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *);
144149678Sjhbstatic int txp_command(struct txp_softc *, u_int16_t, u_int16_t, u_int32_t,
14592739Salfred    u_int32_t, u_int16_t *, u_int32_t *, u_int32_t *, int);
146149678Sjhbstatic int txp_command2(struct txp_softc *, u_int16_t, u_int16_t,
14780219Swpaul    u_int32_t, u_int32_t, struct txp_ext_desc *, u_int8_t,
14892739Salfred    struct txp_rsp_desc **, int);
149149678Sjhbstatic int txp_response(struct txp_softc *, u_int32_t, u_int16_t, u_int16_t,
15092739Salfred    struct txp_rsp_desc **);
151149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
15292739Salfred    struct txp_rsp_desc *);
15392739Salfredstatic void txp_capabilities(struct txp_softc *);
15480219Swpaul
15592739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
15692739Salfredstatic int txp_ifmedia_upd(struct ifnet *);
15780219Swpaul#ifdef TXP_DEBUG
15892739Salfredstatic void txp_show_descriptor(void *);
15980219Swpaul#endif
16092739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *);
16192739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *);
16292739Salfredstatic void txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *);
16380219Swpaul
16480219Swpaul#ifdef TXP_USEIOSPACE
16580219Swpaul#define TXP_RES			SYS_RES_IOPORT
16680219Swpaul#define TXP_RID			TXP_PCI_LOIO
16780219Swpaul#else
16880219Swpaul#define TXP_RES			SYS_RES_MEMORY
16980219Swpaul#define TXP_RID			TXP_PCI_LOMEM
17080219Swpaul#endif
17180219Swpaul
17280219Swpaulstatic device_method_t txp_methods[] = {
17380219Swpaul        /* Device interface */
17480219Swpaul	DEVMETHOD(device_probe,		txp_probe),
17580219Swpaul	DEVMETHOD(device_attach,	txp_attach),
17680219Swpaul	DEVMETHOD(device_detach,	txp_detach),
17780219Swpaul	DEVMETHOD(device_shutdown,	txp_shutdown),
17880219Swpaul	{ 0, 0 }
17980219Swpaul};
18080219Swpaul
18180219Swpaulstatic driver_t txp_driver = {
18280219Swpaul	"txp",
18380219Swpaul	txp_methods,
18480219Swpaul	sizeof(struct txp_softc)
18580219Swpaul};
18680219Swpaul
18780219Swpaulstatic devclass_t txp_devclass;
18880219Swpaul
189113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0);
190113506SmdoddMODULE_DEPEND(txp, pci, 1, 1, 1);
191113506SmdoddMODULE_DEPEND(txp, ether, 1, 1, 1);
19280219Swpaul
19380219Swpaulstatic int
19480219Swpaultxp_probe(dev)
19580219Swpaul	device_t dev;
19680219Swpaul{
19780219Swpaul	struct txp_type *t;
19880219Swpaul
19980219Swpaul	t = txp_devs;
20080219Swpaul
20180219Swpaul	while(t->txp_name != NULL) {
20280219Swpaul		if ((pci_get_vendor(dev) == t->txp_vid) &&
20380219Swpaul		    (pci_get_device(dev) == t->txp_did)) {
20480219Swpaul			device_set_desc(dev, t->txp_name);
205142880Simp			return(BUS_PROBE_DEFAULT);
20680219Swpaul		}
20780219Swpaul		t++;
20880219Swpaul	}
20980219Swpaul
21080219Swpaul	return(ENXIO);
21180219Swpaul}
21280219Swpaul
21380219Swpaulstatic int
21480219Swpaultxp_attach(dev)
21580219Swpaul	device_t dev;
21680219Swpaul{
21780219Swpaul	struct txp_softc *sc;
21880219Swpaul	struct ifnet *ifp;
21980219Swpaul	u_int16_t p1;
22080219Swpaul	u_int32_t p2;
221151772Sjhb	int error = 0, rid;
222147256Sbrooks	u_char eaddr[6];
22380219Swpaul
22480219Swpaul	sc = device_get_softc(dev);
22580219Swpaul	sc->sc_dev = dev;
22680219Swpaul	sc->sc_cold = 1;
22780219Swpaul
22893818Sjhb	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
229151772Sjhb	    MTX_DEF);
230151772Sjhb	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
231151772Sjhb
23280219Swpaul	/*
23380219Swpaul	 * Map control/status registers.
23480219Swpaul	 */
23580219Swpaul	pci_enable_busmaster(dev);
23680219Swpaul
23780219Swpaul	rid = TXP_RID;
238127135Snjl	sc->sc_res = bus_alloc_resource_any(dev, TXP_RES, &rid,
239127135Snjl	    RF_ACTIVE);
24080219Swpaul
24180219Swpaul	if (sc->sc_res == NULL) {
24280219Swpaul		device_printf(dev, "couldn't map ports/memory\n");
24380219Swpaul		error = ENXIO;
24480219Swpaul		goto fail;
24580219Swpaul	}
24680219Swpaul
24780219Swpaul	sc->sc_bt = rman_get_bustag(sc->sc_res);
24880219Swpaul	sc->sc_bh = rman_get_bushandle(sc->sc_res);
24980219Swpaul
25080219Swpaul	/* Allocate interrupt */
25180219Swpaul	rid = 0;
252127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
25380219Swpaul	    RF_SHAREABLE | RF_ACTIVE);
25480219Swpaul
25580219Swpaul	if (sc->sc_irq == NULL) {
25680219Swpaul		device_printf(dev, "couldn't map interrupt\n");
25780219Swpaul		error = ENXIO;
25880219Swpaul		goto fail;
25980219Swpaul	}
26080219Swpaul
26180219Swpaul	if (txp_chip_init(sc)) {
262149678Sjhb		error = ENXIO;
26380219Swpaul		goto fail;
26480219Swpaul	}
26580219Swpaul
26680219Swpaul	sc->sc_fwbuf = contigmalloc(32768, M_DEVBUF,
26780219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
26880219Swpaul	error = txp_download_fw(sc);
26980219Swpaul	contigfree(sc->sc_fwbuf, 32768, M_DEVBUF);
27080219Swpaul	sc->sc_fwbuf = NULL;
27180219Swpaul
272149678Sjhb	if (error)
27380219Swpaul		goto fail;
27480219Swpaul
27580219Swpaul	sc->sc_ldata = contigmalloc(sizeof(struct txp_ldata), M_DEVBUF,
27680219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
27780457Swpaul	bzero(sc->sc_ldata, sizeof(struct txp_ldata));
27880219Swpaul
27980219Swpaul	if (txp_alloc_rings(sc)) {
280149678Sjhb		error = ENXIO;
28180219Swpaul		goto fail;
28280219Swpaul	}
28380219Swpaul
28480219Swpaul	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
28580219Swpaul	    NULL, NULL, NULL, 1)) {
286149678Sjhb		error = ENXIO;
28780219Swpaul		goto fail;
28880219Swpaul	}
28980219Swpaul
29080219Swpaul	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
29180219Swpaul	    &p1, &p2, NULL, 1)) {
292149678Sjhb		error = ENXIO;
29380219Swpaul		goto fail;
29480219Swpaul	}
29580219Swpaul
296147256Sbrooks	eaddr[0] = ((u_int8_t *)&p1)[1];
297147256Sbrooks	eaddr[1] = ((u_int8_t *)&p1)[0];
298147256Sbrooks	eaddr[2] = ((u_int8_t *)&p2)[3];
299147256Sbrooks	eaddr[3] = ((u_int8_t *)&p2)[2];
300147256Sbrooks	eaddr[4] = ((u_int8_t *)&p2)[1];
301147256Sbrooks	eaddr[5] = ((u_int8_t *)&p2)[0];
30280219Swpaul
30380219Swpaul	sc->sc_cold = 0;
30480219Swpaul
30580219Swpaul	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
30680219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
30780219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
30880219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
30980219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
31080219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);
31180219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
31280219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
31380219Swpaul
31480219Swpaul	sc->sc_xcvr = TXP_XCVR_AUTO;
31580219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
31680219Swpaul	    NULL, NULL, NULL, 0);
31780219Swpaul	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO);
31880219Swpaul
319147256Sbrooks	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
320147256Sbrooks	if (ifp == NULL) {
321150184Sru		device_printf(dev, "can not if_alloc()\n");
322147256Sbrooks		error = ENOSPC;
323147256Sbrooks		goto fail;
324147256Sbrooks	}
32580219Swpaul	ifp->if_softc = sc;
326121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
32780219Swpaul	ifp->if_mtu = ETHERMTU;
328151772Sjhb	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
32980219Swpaul	ifp->if_ioctl = txp_ioctl;
33080219Swpaul	ifp->if_start = txp_start;
33180219Swpaul	ifp->if_watchdog = txp_watchdog;
33280219Swpaul	ifp->if_init = txp_init;
33380219Swpaul	ifp->if_baudrate = 100000000;
33480219Swpaul	ifp->if_snd.ifq_maxlen = TX_ENTRIES;
33580219Swpaul	ifp->if_hwassist = 0;
33680219Swpaul	txp_capabilities(sc);
33780219Swpaul
33880219Swpaul	/*
33980219Swpaul	 * Attach us everywhere
34080219Swpaul	 */
341147256Sbrooks	ether_ifattach(ifp, eaddr);
342149678Sjhb
343151772Sjhb	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
344149678Sjhb	    txp_intr, sc, &sc->sc_intrhand);
345149678Sjhb
346149678Sjhb	if (error) {
347149678Sjhb		ether_ifdetach(ifp);
348149678Sjhb		device_printf(dev, "couldn't set up irq\n");
349149678Sjhb		goto fail;
350149678Sjhb	}
351149678Sjhb
35280219Swpaul	return(0);
35380219Swpaul
35480219Swpaulfail:
35580219Swpaul	txp_release_resources(sc);
35680219Swpaul	mtx_destroy(&sc->sc_mtx);
35780219Swpaul	return(error);
35880219Swpaul}
35980219Swpaul
36080219Swpaulstatic int
36180219Swpaultxp_detach(dev)
36280219Swpaul	device_t dev;
36380219Swpaul{
36480219Swpaul	struct txp_softc *sc;
36580219Swpaul	struct ifnet *ifp;
36680219Swpaul	int i;
36780219Swpaul
36880219Swpaul	sc = device_get_softc(dev);
369147256Sbrooks	ifp = sc->sc_ifp;
37080219Swpaul
371151772Sjhb	TXP_LOCK(sc);
37280219Swpaul	txp_stop(sc);
373151772Sjhb	TXP_UNLOCK(sc);
37480219Swpaul	txp_shutdown(dev);
375151772Sjhb	callout_drain(&sc->sc_tick);
37680219Swpaul
37780219Swpaul	ifmedia_removeall(&sc->sc_ifmedia);
378106937Ssam	ether_ifdetach(ifp);
37980219Swpaul
38080219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++)
38180219Swpaul		free(sc->sc_rxbufs[i].rb_sd, M_DEVBUF);
38280219Swpaul
38380219Swpaul	txp_release_resources(sc);
38480219Swpaul
38580219Swpaul	mtx_destroy(&sc->sc_mtx);
38680219Swpaul	return(0);
38780219Swpaul}
38880219Swpaul
38980219Swpaulstatic void
39080219Swpaultxp_release_resources(sc)
39180219Swpaul	struct txp_softc *sc;
39280219Swpaul{
39380219Swpaul	device_t dev;
39480219Swpaul
39580219Swpaul	dev = sc->sc_dev;
39680219Swpaul
39780219Swpaul	if (sc->sc_intrhand != NULL)
39880219Swpaul		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
39980219Swpaul
40080219Swpaul	if (sc->sc_irq != NULL)
40180219Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
40280219Swpaul
40380219Swpaul	if (sc->sc_res != NULL)
40480219Swpaul		bus_release_resource(dev, TXP_RES, TXP_RID, sc->sc_res);
40580219Swpaul
40680219Swpaul	if (sc->sc_ldata != NULL)
40780219Swpaul		contigfree(sc->sc_ldata, sizeof(struct txp_ldata), M_DEVBUF);
40880219Swpaul
409150306Simp	if (sc->sc_ifp)
410150306Simp		if_free(sc->sc_ifp);
411150306Simp
41280219Swpaul	return;
41380219Swpaul}
41480219Swpaul
41580219Swpaulstatic int
41680219Swpaultxp_chip_init(sc)
41780219Swpaul	struct txp_softc *sc;
41880219Swpaul{
41980219Swpaul	/* disable interrupts */
42080219Swpaul	WRITE_REG(sc, TXP_IER, 0);
42180219Swpaul	WRITE_REG(sc, TXP_IMR,
42280219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
42380219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
42480219Swpaul	    TXP_INT_LATCH);
42580219Swpaul
42680219Swpaul	/* ack all interrupts */
42780219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
42880219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
42980219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
43080219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
43180219Swpaul	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
43280219Swpaul
43380219Swpaul	if (txp_reset_adapter(sc))
43480219Swpaul		return (-1);
43580219Swpaul
43680219Swpaul	/* disable interrupts */
43780219Swpaul	WRITE_REG(sc, TXP_IER, 0);
43880219Swpaul	WRITE_REG(sc, TXP_IMR,
43980219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
44080219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
44180219Swpaul	    TXP_INT_LATCH);
44280219Swpaul
44380219Swpaul	/* ack all interrupts */
44480219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
44580219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
44680219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
44780219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
44880219Swpaul	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
44980219Swpaul
45080219Swpaul	return (0);
45180219Swpaul}
45280219Swpaul
45380219Swpaulstatic int
45480219Swpaultxp_reset_adapter(sc)
45580219Swpaul	struct txp_softc *sc;
45680219Swpaul{
45780219Swpaul	u_int32_t r;
45880219Swpaul	int i;
45980219Swpaul
46092643Sjeff	r = 0;
46180219Swpaul	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
46280219Swpaul	DELAY(1000);
46380219Swpaul	WRITE_REG(sc, TXP_SRR, 0);
46480219Swpaul
46580219Swpaul	/* Should wait max 6 seconds */
46680219Swpaul	for (i = 0; i < 6000; i++) {
46780219Swpaul		r = READ_REG(sc, TXP_A2H_0);
46880219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
46980219Swpaul			break;
47080219Swpaul		DELAY(1000);
47180219Swpaul	}
47280219Swpaul
47380219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
47480219Swpaul		device_printf(sc->sc_dev, "reset hung\n");
47580219Swpaul		return (-1);
47680219Swpaul	}
47780219Swpaul
47880219Swpaul	return (0);
47980219Swpaul}
48080219Swpaul
48180219Swpaulstatic int
48280219Swpaultxp_download_fw(sc)
48380219Swpaul	struct txp_softc *sc;
48480219Swpaul{
48580219Swpaul	struct txp_fw_file_header *fileheader;
48680219Swpaul	struct txp_fw_section_header *secthead;
48780219Swpaul	int sect;
48880219Swpaul	u_int32_t r, i, ier, imr;
48980219Swpaul
49092643Sjeff	r = 0;
49180219Swpaul	ier = READ_REG(sc, TXP_IER);
49280219Swpaul	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
49380219Swpaul
49480219Swpaul	imr = READ_REG(sc, TXP_IMR);
49580219Swpaul	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
49680219Swpaul
49780219Swpaul	for (i = 0; i < 10000; i++) {
49880219Swpaul		r = READ_REG(sc, TXP_A2H_0);
49980219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
50080219Swpaul			break;
50180219Swpaul		DELAY(50);
50280219Swpaul	}
50380219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
50480219Swpaul		device_printf(sc->sc_dev, "not waiting for host request\n");
50580219Swpaul		return (-1);
50680219Swpaul	}
50780219Swpaul
50880219Swpaul	/* Ack the status */
50980219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
51080219Swpaul
51180219Swpaul	fileheader = (struct txp_fw_file_header *)tc990image;
51280219Swpaul	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
51380219Swpaul		device_printf(sc->sc_dev, "fw invalid magic\n");
51480219Swpaul		return (-1);
51580219Swpaul	}
51680219Swpaul
51780219Swpaul	/* Tell boot firmware to get ready for image */
51880219Swpaul	WRITE_REG(sc, TXP_H2A_1, fileheader->addr);
51980219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
52080219Swpaul
52180219Swpaul	if (txp_download_fw_wait(sc)) {
52280219Swpaul		device_printf(sc->sc_dev, "fw wait failed, initial\n");
52380219Swpaul		return (-1);
52480219Swpaul	}
52580219Swpaul
52680219Swpaul	secthead = (struct txp_fw_section_header *)(((u_int8_t *)tc990image) +
52780219Swpaul	    sizeof(struct txp_fw_file_header));
52880219Swpaul
52980219Swpaul	for (sect = 0; sect < fileheader->nsections; sect++) {
53080219Swpaul		if (txp_download_fw_section(sc, secthead, sect))
53180219Swpaul			return (-1);
53280219Swpaul		secthead = (struct txp_fw_section_header *)
53380219Swpaul		    (((u_int8_t *)secthead) + secthead->nbytes +
53480219Swpaul		    sizeof(*secthead));
53580219Swpaul	}
53680219Swpaul
53780219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
53880219Swpaul
53980219Swpaul	for (i = 0; i < 10000; i++) {
54080219Swpaul		r = READ_REG(sc, TXP_A2H_0);
54180219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
54280219Swpaul			break;
54380219Swpaul		DELAY(50);
54480219Swpaul	}
54580219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
54680219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
54780219Swpaul		return (-1);
54880219Swpaul	}
54980219Swpaul
55080219Swpaul	WRITE_REG(sc, TXP_IER, ier);
55180219Swpaul	WRITE_REG(sc, TXP_IMR, imr);
55280219Swpaul
55380219Swpaul	return (0);
55480219Swpaul}
55580219Swpaul
55680219Swpaulstatic int
55780219Swpaultxp_download_fw_wait(sc)
55880219Swpaul	struct txp_softc *sc;
55980219Swpaul{
56080219Swpaul	u_int32_t i, r;
56180219Swpaul
56292643Sjeff	r = 0;
56380219Swpaul	for (i = 0; i < 10000; i++) {
56480219Swpaul		r = READ_REG(sc, TXP_ISR);
56580219Swpaul		if (r & TXP_INT_A2H_0)
56680219Swpaul			break;
56780219Swpaul		DELAY(50);
56880219Swpaul	}
56980219Swpaul
57080219Swpaul	if (!(r & TXP_INT_A2H_0)) {
57180219Swpaul		device_printf(sc->sc_dev, "fw wait failed comm0\n");
57280219Swpaul		return (-1);
57380219Swpaul	}
57480219Swpaul
57580219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
57680219Swpaul
57780219Swpaul	r = READ_REG(sc, TXP_A2H_0);
57880219Swpaul	if (r != STAT_WAITING_FOR_SEGMENT) {
57980219Swpaul		device_printf(sc->sc_dev, "fw not waiting for segment\n");
58080219Swpaul		return (-1);
58180219Swpaul	}
58280219Swpaul	return (0);
58380219Swpaul}
58480219Swpaul
58580219Swpaulstatic int
58680219Swpaultxp_download_fw_section(sc, sect, sectnum)
58780219Swpaul	struct txp_softc *sc;
58880219Swpaul	struct txp_fw_section_header *sect;
58980219Swpaul	int sectnum;
59080219Swpaul{
59180219Swpaul	vm_offset_t dma;
59280219Swpaul	int rseg, err = 0;
59380219Swpaul	struct mbuf m;
59480219Swpaul	u_int16_t csum;
59580219Swpaul
59680219Swpaul	/* Skip zero length sections */
59780219Swpaul	if (sect->nbytes == 0)
59880219Swpaul		return (0);
59980219Swpaul
60080219Swpaul	/* Make sure we aren't past the end of the image */
60180219Swpaul	rseg = ((u_int8_t *)sect) - ((u_int8_t *)tc990image);
60280219Swpaul	if (rseg >= sizeof(tc990image)) {
60380219Swpaul		device_printf(sc->sc_dev, "fw invalid section address, "
60480219Swpaul		    "section %d\n", sectnum);
60580219Swpaul		return (-1);
60680219Swpaul	}
60780219Swpaul
60880219Swpaul	/* Make sure this section doesn't go past the end */
60980219Swpaul	rseg += sect->nbytes;
61080219Swpaul	if (rseg >= sizeof(tc990image)) {
61180219Swpaul		device_printf(sc->sc_dev, "fw truncated section %d\n",
61280219Swpaul		    sectnum);
61380219Swpaul		return (-1);
61480219Swpaul	}
61580219Swpaul
61680219Swpaul	bcopy(((u_int8_t *)sect) + sizeof(*sect), sc->sc_fwbuf, sect->nbytes);
61780219Swpaul	dma = vtophys(sc->sc_fwbuf);
61880219Swpaul
61980219Swpaul	/*
62080219Swpaul	 * dummy up mbuf and verify section checksum
62180219Swpaul	 */
62280219Swpaul	m.m_type = MT_DATA;
62380219Swpaul	m.m_next = m.m_nextpkt = NULL;
62480219Swpaul	m.m_len = sect->nbytes;
62580219Swpaul	m.m_data = sc->sc_fwbuf;
62680219Swpaul	m.m_flags = 0;
62780219Swpaul	csum = in_cksum(&m, sect->nbytes);
62880219Swpaul	if (csum != sect->cksum) {
62980219Swpaul		device_printf(sc->sc_dev, "fw section %d, bad "
63080219Swpaul		    "cksum (expected 0x%x got 0x%x)\n",
63180219Swpaul		    sectnum, sect->cksum, csum);
63280219Swpaul		err = -1;
63380219Swpaul		goto bail;
63480219Swpaul	}
63580219Swpaul
63680219Swpaul	WRITE_REG(sc, TXP_H2A_1, sect->nbytes);
63780219Swpaul	WRITE_REG(sc, TXP_H2A_2, sect->cksum);
63880219Swpaul	WRITE_REG(sc, TXP_H2A_3, sect->addr);
63980219Swpaul	WRITE_REG(sc, TXP_H2A_4, 0);
64080219Swpaul	WRITE_REG(sc, TXP_H2A_5, dma & 0xffffffff);
64180219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
64280219Swpaul
64380219Swpaul	if (txp_download_fw_wait(sc)) {
64480219Swpaul		device_printf(sc->sc_dev, "fw wait failed, "
64580219Swpaul		    "section %d\n", sectnum);
64680219Swpaul		err = -1;
64780219Swpaul	}
64880219Swpaul
64980219Swpaulbail:
65080219Swpaul	return (err);
65180219Swpaul}
65280219Swpaul
65380219Swpaulstatic void
65480219Swpaultxp_intr(vsc)
65580219Swpaul	void *vsc;
65680219Swpaul{
65780219Swpaul	struct txp_softc *sc = vsc;
65880219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
65980219Swpaul	u_int32_t isr;
66080219Swpaul
66180219Swpaul	/* mask all interrupts */
662151772Sjhb	TXP_LOCK(sc);
66380219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_RESERVED | TXP_INT_SELF |
66480219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
66580219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
66680219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
66780219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
66880219Swpaul
66980219Swpaul	isr = READ_REG(sc, TXP_ISR);
67080219Swpaul	while (isr) {
67180219Swpaul		WRITE_REG(sc, TXP_ISR, isr);
67280219Swpaul
67380219Swpaul		if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
67480219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxhir);
67580219Swpaul		if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
67680219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxlor);
67780219Swpaul
67880219Swpaul		if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
67980219Swpaul			txp_rxbuf_reclaim(sc);
68080219Swpaul
68180219Swpaul		if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
68280219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txhir.r_off))))
68380219Swpaul			txp_tx_reclaim(sc, &sc->sc_txhir);
68480219Swpaul
68580219Swpaul		if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
68680219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txlor.r_off))))
68780219Swpaul			txp_tx_reclaim(sc, &sc->sc_txlor);
68880219Swpaul
68980219Swpaul		isr = READ_REG(sc, TXP_ISR);
69080219Swpaul	}
69180219Swpaul
69280219Swpaul	/* unmask all interrupts */
69380219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
69480219Swpaul
695151772Sjhb	txp_start_locked(sc->sc_ifp);
696151772Sjhb	TXP_UNLOCK(sc);
69780219Swpaul
69880219Swpaul	return;
69980219Swpaul}
70080219Swpaul
70180219Swpaulstatic void
70280219Swpaultxp_rx_reclaim(sc, r)
70380219Swpaul	struct txp_softc *sc;
70480219Swpaul	struct txp_rx_ring *r;
70580219Swpaul{
706147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
70780219Swpaul	struct txp_rx_desc *rxd;
70880219Swpaul	struct mbuf *m;
70980219Swpaul	struct txp_swdesc *sd = NULL;
71080219Swpaul	u_int32_t roff, woff;
71180219Swpaul
712151772Sjhb	TXP_LOCK_ASSERT(sc);
71380219Swpaul	roff = *r->r_roff;
71480219Swpaul	woff = *r->r_woff;
71580219Swpaul	rxd = r->r_desc + (roff / sizeof(struct txp_rx_desc));
71680219Swpaul
71780219Swpaul	while (roff != woff) {
71880219Swpaul
71980219Swpaul		if (rxd->rx_flags & RX_FLAGS_ERROR) {
72080219Swpaul			device_printf(sc->sc_dev, "error 0x%x\n",
72180219Swpaul			    rxd->rx_stat);
72280219Swpaul			ifp->if_ierrors++;
72380219Swpaul			goto next;
72480219Swpaul		}
72580219Swpaul
72680219Swpaul		/* retrieve stashed pointer */
72780219Swpaul		sd = rxd->rx_sd;
72880219Swpaul
72980219Swpaul		m = sd->sd_mbuf;
73080219Swpaul		sd->sd_mbuf = NULL;
73180219Swpaul
73280219Swpaul		m->m_pkthdr.len = m->m_len = rxd->rx_len;
73380219Swpaul
73480219Swpaul#ifdef __STRICT_ALIGNMENT
73580219Swpaul		{
73680219Swpaul			/*
73780219Swpaul			 * XXX Nice chip, except it won't accept "off by 2"
73880219Swpaul			 * buffers, so we're force to copy.  Supposedly
73980219Swpaul			 * this will be fixed in a newer firmware rev
74080219Swpaul			 * and this will be temporary.
74180219Swpaul			 */
74280219Swpaul			struct mbuf *mnew;
74380219Swpaul
744151772Sjhb			mnew = m_devget(mtod(m, caddr_t), rxd->rx_len,
745151772Sjhb			    ETHER_ALIGN, ifp, NULL);
746151772Sjhb			m_freem(m);
74780219Swpaul			if (mnew == NULL) {
748151772Sjhb				ifp->if_ierrors++;
74980219Swpaul				goto next;
75080219Swpaul			}
75180219Swpaul			m = mnew;
75280219Swpaul		}
75380219Swpaul#endif
75480219Swpaul
75580219Swpaul		if (rxd->rx_stat & RX_STAT_IPCKSUMBAD)
75680219Swpaul			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
75780219Swpaul		else if (rxd->rx_stat & RX_STAT_IPCKSUMGOOD)
75880219Swpaul		 	m->m_pkthdr.csum_flags |=
75980219Swpaul			    CSUM_IP_CHECKED|CSUM_IP_VALID;
76080219Swpaul
76180219Swpaul		if ((rxd->rx_stat & RX_STAT_TCPCKSUMGOOD) ||
76280219Swpaul		    (rxd->rx_stat & RX_STAT_UDPCKSUMGOOD)) {
76380219Swpaul			m->m_pkthdr.csum_flags |=
76480219Swpaul			    CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
76580219Swpaul			m->m_pkthdr.csum_data = 0xffff;
76680219Swpaul		}
76780219Swpaul
76880219Swpaul		if (rxd->rx_stat & RX_STAT_VLAN) {
769106937Ssam			VLAN_INPUT_TAG(ifp,
770106937Ssam				m, htons(rxd->rx_vlan >> 16), goto next);
77180219Swpaul		}
77283115Sbrooks
773151772Sjhb		TXP_UNLOCK(sc);
774106937Ssam		(*ifp->if_input)(ifp, m);
775151772Sjhb		TXP_LOCK(sc);
77680219Swpaul
77780219Swpaulnext:
77880219Swpaul
77980219Swpaul		roff += sizeof(struct txp_rx_desc);
78080219Swpaul		if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
78180219Swpaul			roff = 0;
78280219Swpaul			rxd = r->r_desc;
78380219Swpaul		} else
78480219Swpaul			rxd++;
78580219Swpaul		woff = *r->r_woff;
78680219Swpaul	}
78780219Swpaul
78880219Swpaul	*r->r_roff = woff;
78980219Swpaul
79080219Swpaul	return;
79180219Swpaul}
79280219Swpaul
79380219Swpaulstatic void
79480219Swpaultxp_rxbuf_reclaim(sc)
79580219Swpaul	struct txp_softc *sc;
79680219Swpaul{
797147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
79880219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
79980219Swpaul	struct txp_rxbuf_desc *rbd;
80080219Swpaul	struct txp_swdesc *sd;
80180219Swpaul	u_int32_t i;
80280219Swpaul
803151772Sjhb	TXP_LOCK_ASSERT(sc);
804148887Srwatson	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
80580219Swpaul		return;
80680219Swpaul
80780219Swpaul	i = sc->sc_rxbufprod;
80880219Swpaul	rbd = sc->sc_rxbufs + i;
80980219Swpaul
81080219Swpaul	while (1) {
81180219Swpaul		sd = rbd->rb_sd;
81280219Swpaul		if (sd->sd_mbuf != NULL)
81380219Swpaul			break;
81480219Swpaul
815151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
81680219Swpaul		if (sd->sd_mbuf == NULL)
817151772Sjhb			return;
81880219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
81980219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
82080219Swpaul
82180219Swpaul		rbd->rb_paddrlo = vtophys(mtod(sd->sd_mbuf, vm_offset_t))
82280219Swpaul		    & 0xffffffff;
82380219Swpaul		rbd->rb_paddrhi = 0;
82480219Swpaul
82580219Swpaul		hv->hv_rx_buf_write_idx = TXP_IDX2OFFSET(i);
82680219Swpaul
82780219Swpaul		if (++i == RXBUF_ENTRIES) {
82880219Swpaul			i = 0;
82980219Swpaul			rbd = sc->sc_rxbufs;
83080219Swpaul		} else
83180219Swpaul			rbd++;
83280219Swpaul	}
83380219Swpaul
83480219Swpaul	sc->sc_rxbufprod = i;
83580219Swpaul
83680219Swpaul	return;
83780219Swpaul}
83880219Swpaul
83980219Swpaul/*
84080219Swpaul * Reclaim mbufs and entries from a transmit ring.
84180219Swpaul */
84280219Swpaulstatic void
84380219Swpaultxp_tx_reclaim(sc, r)
84480219Swpaul	struct txp_softc *sc;
84580219Swpaul	struct txp_tx_ring *r;
84680219Swpaul{
847147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
84880219Swpaul	u_int32_t idx = TXP_OFFSET2IDX(*(r->r_off));
84980219Swpaul	u_int32_t cons = r->r_cons, cnt = r->r_cnt;
85080219Swpaul	struct txp_tx_desc *txd = r->r_desc + cons;
85180219Swpaul	struct txp_swdesc *sd = sc->sc_txd + cons;
85280219Swpaul	struct mbuf *m;
85380219Swpaul
854151772Sjhb	TXP_LOCK_ASSERT(sc);
85580219Swpaul	while (cons != idx) {
85680219Swpaul		if (cnt == 0)
85780219Swpaul			break;
85880219Swpaul
85980219Swpaul		if ((txd->tx_flags & TX_FLAGS_TYPE_M) ==
86080219Swpaul		    TX_FLAGS_TYPE_DATA) {
86180219Swpaul			m = sd->sd_mbuf;
86280219Swpaul			if (m != NULL) {
86380219Swpaul				m_freem(m);
86480219Swpaul				txd->tx_addrlo = 0;
86580219Swpaul				txd->tx_addrhi = 0;
86680219Swpaul				ifp->if_opackets++;
86780219Swpaul			}
86880219Swpaul		}
869148887Srwatson		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
87080219Swpaul
87180219Swpaul		if (++cons == TX_ENTRIES) {
87280219Swpaul			txd = r->r_desc;
87380219Swpaul			cons = 0;
87480219Swpaul			sd = sc->sc_txd;
87580219Swpaul		} else {
87680219Swpaul			txd++;
87780219Swpaul			sd++;
87880219Swpaul		}
87980219Swpaul
88080219Swpaul		cnt--;
88180219Swpaul	}
88280219Swpaul
88380219Swpaul	r->r_cons = cons;
88480219Swpaul	r->r_cnt = cnt;
88580219Swpaul	if (cnt == 0)
88680219Swpaul		ifp->if_timer = 0;
88780219Swpaul}
88880219Swpaul
88980219Swpaulstatic int
89080219Swpaultxp_shutdown(dev)
89180219Swpaul	device_t dev;
89280219Swpaul{
89380219Swpaul	struct txp_softc *sc;
89480219Swpaul
89580219Swpaul	sc = device_get_softc(dev);
89680219Swpaul
897151772Sjhb	TXP_LOCK(sc);
898151772Sjhb
89980219Swpaul	/* mask all interrupts */
90080219Swpaul	WRITE_REG(sc, TXP_IMR,
90180219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
90280219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
90380219Swpaul	    TXP_INT_LATCH);
90480219Swpaul
90580219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
90680219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
90780219Swpaul	txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 0);
908151772Sjhb	TXP_UNLOCK(sc);
90980219Swpaul
91080219Swpaul	return(0);
91180219Swpaul}
91280219Swpaul
91380219Swpaulstatic int
91480219Swpaultxp_alloc_rings(sc)
91580219Swpaul	struct txp_softc *sc;
91680219Swpaul{
91780219Swpaul	struct txp_boot_record *boot;
91880219Swpaul	struct txp_ldata *ld;
91980219Swpaul	u_int32_t r;
92080219Swpaul	int i;
92180219Swpaul
92292643Sjeff	r = 0;
92380219Swpaul	ld = sc->sc_ldata;
92480219Swpaul	boot = &ld->txp_boot;
92580219Swpaul
92680219Swpaul	/* boot record */
92780219Swpaul	sc->sc_boot = boot;
92880219Swpaul
92980219Swpaul	/* host variables */
93080219Swpaul	bzero(&ld->txp_hostvar, sizeof(struct txp_hostvar));
93180219Swpaul	boot->br_hostvar_lo = vtophys(&ld->txp_hostvar);
93280219Swpaul	boot->br_hostvar_hi = 0;
93380219Swpaul	sc->sc_hostvar = (struct txp_hostvar *)&ld->txp_hostvar;
93480219Swpaul
93580219Swpaul	/* hi priority tx ring */
93680219Swpaul	boot->br_txhipri_lo = vtophys(&ld->txp_txhiring);;
93780219Swpaul	boot->br_txhipri_hi = 0;
93880219Swpaul	boot->br_txhipri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
93980219Swpaul	sc->sc_txhir.r_reg = TXP_H2A_1;
94080219Swpaul	sc->sc_txhir.r_desc = (struct txp_tx_desc *)&ld->txp_txhiring;
94180219Swpaul	sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
94280219Swpaul	sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
94380219Swpaul
94480219Swpaul	/* lo priority tx ring */
94580219Swpaul	boot->br_txlopri_lo = vtophys(&ld->txp_txloring);
94680219Swpaul	boot->br_txlopri_hi = 0;
94780219Swpaul	boot->br_txlopri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
94880219Swpaul	sc->sc_txlor.r_reg = TXP_H2A_3;
94980219Swpaul	sc->sc_txlor.r_desc = (struct txp_tx_desc *)&ld->txp_txloring;
95080219Swpaul	sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
95180219Swpaul	sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
95280219Swpaul
95380219Swpaul	/* high priority rx ring */
95480219Swpaul	boot->br_rxhipri_lo = vtophys(&ld->txp_rxhiring);
95580219Swpaul	boot->br_rxhipri_hi = 0;
95680219Swpaul	boot->br_rxhipri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
95780219Swpaul	sc->sc_rxhir.r_desc = (struct txp_rx_desc *)&ld->txp_rxhiring;
95880219Swpaul	sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
95980219Swpaul	sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
96080219Swpaul
96180219Swpaul	/* low priority rx ring */
96280219Swpaul	boot->br_rxlopri_lo = vtophys(&ld->txp_rxloring);
96380219Swpaul	boot->br_rxlopri_hi = 0;
96480219Swpaul	boot->br_rxlopri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
96580219Swpaul	sc->sc_rxlor.r_desc = (struct txp_rx_desc *)&ld->txp_rxloring;
96680219Swpaul	sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
96780219Swpaul	sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
96880219Swpaul
96980219Swpaul	/* command ring */
97080219Swpaul	bzero(&ld->txp_cmdring, sizeof(struct txp_cmd_desc) * CMD_ENTRIES);
97180219Swpaul	boot->br_cmd_lo = vtophys(&ld->txp_cmdring);
97280219Swpaul	boot->br_cmd_hi = 0;
97380219Swpaul	boot->br_cmd_siz = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
97480219Swpaul	sc->sc_cmdring.base = (struct txp_cmd_desc *)&ld->txp_cmdring;
97580219Swpaul	sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
97680219Swpaul	sc->sc_cmdring.lastwrite = 0;
97780219Swpaul
97880219Swpaul	/* response ring */
97980219Swpaul	bzero(&ld->txp_rspring, sizeof(struct txp_rsp_desc) * RSP_ENTRIES);
98080219Swpaul	boot->br_resp_lo = vtophys(&ld->txp_rspring);
98180219Swpaul	boot->br_resp_hi = 0;
98280219Swpaul	boot->br_resp_siz = CMD_ENTRIES * sizeof(struct txp_rsp_desc);
98380219Swpaul	sc->sc_rspring.base = (struct txp_rsp_desc *)&ld->txp_rspring;
98480219Swpaul	sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
98580219Swpaul	sc->sc_rspring.lastwrite = 0;
98680219Swpaul
98780219Swpaul	/* receive buffer ring */
98880219Swpaul	boot->br_rxbuf_lo = vtophys(&ld->txp_rxbufs);
98980219Swpaul	boot->br_rxbuf_hi = 0;
99080219Swpaul	boot->br_rxbuf_siz = RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc);
99180219Swpaul	sc->sc_rxbufs = (struct txp_rxbuf_desc *)&ld->txp_rxbufs;
99280219Swpaul
99380219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
99480457Swpaul		struct txp_swdesc *sd;
99580219Swpaul		if (sc->sc_rxbufs[i].rb_sd != NULL)
99680219Swpaul			continue;
99780219Swpaul		sc->sc_rxbufs[i].rb_sd = malloc(sizeof(struct txp_swdesc),
99880219Swpaul		    M_DEVBUF, M_NOWAIT);
99980219Swpaul		if (sc->sc_rxbufs[i].rb_sd == NULL)
100080219Swpaul			return(ENOBUFS);
100180457Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
100280457Swpaul		sd->sd_mbuf = NULL;
100380219Swpaul	}
100480219Swpaul	sc->sc_rxbufprod = 0;
100580219Swpaul
100680219Swpaul	/* zero dma */
100780219Swpaul	bzero(&ld->txp_zero, sizeof(u_int32_t));
100880219Swpaul	boot->br_zero_lo = vtophys(&ld->txp_zero);
100980219Swpaul	boot->br_zero_hi = 0;
101080219Swpaul
101180219Swpaul	/* See if it's waiting for boot, and try to boot it */
101280219Swpaul	for (i = 0; i < 10000; i++) {
101380219Swpaul		r = READ_REG(sc, TXP_A2H_0);
101480219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
101580219Swpaul			break;
101680219Swpaul		DELAY(50);
101780219Swpaul	}
101880219Swpaul
101980219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
102080219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
102180455Swpaul		return(ENXIO);
102280219Swpaul	}
102380219Swpaul
102480219Swpaul	WRITE_REG(sc, TXP_H2A_2, 0);
102580219Swpaul	WRITE_REG(sc, TXP_H2A_1, vtophys(sc->sc_boot));
102680219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
102780219Swpaul
102880219Swpaul	/* See if it booted */
102980219Swpaul	for (i = 0; i < 10000; i++) {
103080219Swpaul		r = READ_REG(sc, TXP_A2H_0);
103180219Swpaul		if (r == STAT_RUNNING)
103280219Swpaul			break;
103380219Swpaul		DELAY(50);
103480219Swpaul	}
103580219Swpaul	if (r != STAT_RUNNING) {
103680219Swpaul		device_printf(sc->sc_dev, "fw not running\n");
103780219Swpaul		return(ENXIO);
103880219Swpaul	}
103980219Swpaul
104080219Swpaul	/* Clear TX and CMD ring write registers */
104180219Swpaul	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
104280219Swpaul	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
104380219Swpaul	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
104480219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
104580219Swpaul
104680219Swpaul	return (0);
104780219Swpaul}
104880219Swpaul
104980219Swpaulstatic int
105080219Swpaultxp_ioctl(ifp, command, data)
105180219Swpaul	struct ifnet *ifp;
105280219Swpaul	u_long command;
105380219Swpaul	caddr_t data;
105480219Swpaul{
105580219Swpaul	struct txp_softc *sc = ifp->if_softc;
105680219Swpaul	struct ifreq *ifr = (struct ifreq *)data;
1057151772Sjhb	int error = 0;
105880219Swpaul
105980219Swpaul	switch(command) {
106080219Swpaul	case SIOCSIFFLAGS:
1061151772Sjhb		TXP_LOCK(sc);
106280219Swpaul		if (ifp->if_flags & IFF_UP) {
1063151772Sjhb			txp_init_locked(sc);
106480219Swpaul		} else {
1065148887Srwatson			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
106680219Swpaul				txp_stop(sc);
106780219Swpaul		}
1068151772Sjhb		TXP_UNLOCK(sc);
106980219Swpaul		break;
107080219Swpaul	case SIOCADDMULTI:
107180219Swpaul	case SIOCDELMULTI:
107280219Swpaul		/*
107380219Swpaul		 * Multicast list has changed; set the hardware
107480219Swpaul		 * filter accordingly.
107580219Swpaul		 */
1076151772Sjhb		TXP_LOCK(sc);
107780219Swpaul		txp_set_filter(sc);
1078151772Sjhb		TXP_UNLOCK(sc);
107980219Swpaul		error = 0;
108080219Swpaul		break;
108180219Swpaul	case SIOCGIFMEDIA:
108280219Swpaul	case SIOCSIFMEDIA:
108380219Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
108480219Swpaul		break;
108580219Swpaul	default:
1086106937Ssam		error = ether_ioctl(ifp, command, data);
108780219Swpaul		break;
108880219Swpaul	}
108980219Swpaul
109080219Swpaul	return(error);
109180219Swpaul}
109280219Swpaul
109380219Swpaulstatic int
109480219Swpaultxp_rxring_fill(sc)
109580219Swpaul	struct txp_softc *sc;
109680219Swpaul{
109780219Swpaul	int i;
109880219Swpaul	struct ifnet *ifp;
109980219Swpaul	struct txp_swdesc *sd;
110080219Swpaul
1101151772Sjhb	TXP_LOCK_ASSERT(sc);
1102147256Sbrooks	ifp = sc->sc_ifp;
110380219Swpaul
110480219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
110580219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
1106151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
110780219Swpaul		if (sd->sd_mbuf == NULL)
110880219Swpaul			return(ENOBUFS);
110980219Swpaul
111080219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
111180219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
111280219Swpaul
111380219Swpaul		sc->sc_rxbufs[i].rb_paddrlo =
111480219Swpaul		    vtophys(mtod(sd->sd_mbuf, vm_offset_t));
111580219Swpaul		sc->sc_rxbufs[i].rb_paddrhi = 0;
111680219Swpaul	}
111780219Swpaul
111880219Swpaul	sc->sc_hostvar->hv_rx_buf_write_idx = (RXBUF_ENTRIES - 1) *
111980219Swpaul	    sizeof(struct txp_rxbuf_desc);
112080219Swpaul
112180219Swpaul	return(0);
112280219Swpaul}
112380219Swpaul
112480219Swpaulstatic void
112580219Swpaultxp_rxring_empty(sc)
112680219Swpaul	struct txp_softc *sc;
112780219Swpaul{
112880219Swpaul	int i;
112980219Swpaul	struct txp_swdesc *sd;
113080219Swpaul
1131151772Sjhb	TXP_LOCK_ASSERT(sc);
113280219Swpaul	if (sc->sc_rxbufs == NULL)
113380219Swpaul		return;
113480219Swpaul
113580219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
113680219Swpaul		if (&sc->sc_rxbufs[i] == NULL)
113780219Swpaul			continue;
113880219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
113980219Swpaul		if (sd == NULL)
114080219Swpaul			continue;
114180219Swpaul		if (sd->sd_mbuf != NULL) {
114280219Swpaul			m_freem(sd->sd_mbuf);
114380219Swpaul			sd->sd_mbuf = NULL;
114480219Swpaul		}
114580219Swpaul	}
114680219Swpaul
114780219Swpaul	return;
114880219Swpaul}
114980219Swpaul
115080219Swpaulstatic void
115180219Swpaultxp_init(xsc)
115280219Swpaul	void *xsc;
115380219Swpaul{
115480219Swpaul	struct txp_softc *sc;
1155151772Sjhb
1156151772Sjhb	sc = xsc;
1157151772Sjhb	TXP_LOCK(sc);
1158151772Sjhb	txp_init_locked(sc);
1159151772Sjhb	TXP_UNLOCK(sc);
1160151772Sjhb}
1161151772Sjhb
1162151772Sjhbstatic void
1163151772Sjhbtxp_init_locked(sc)
1164151772Sjhb	struct txp_softc *sc;
1165151772Sjhb{
116680219Swpaul	struct ifnet *ifp;
116780219Swpaul	u_int16_t p1;
116880219Swpaul	u_int32_t p2;
116980219Swpaul
1170151772Sjhb	TXP_LOCK_ASSERT(sc);
1171147256Sbrooks	ifp = sc->sc_ifp;
117280219Swpaul
1173148887Srwatson	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
117480219Swpaul		return;
117580219Swpaul
117680219Swpaul	txp_stop(sc);
117780219Swpaul
117880219Swpaul	txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
117980219Swpaul	    NULL, NULL, NULL, 1);
118080219Swpaul
118180219Swpaul	/* Set station address. */
1182152315Sru	((u_int8_t *)&p1)[1] = IF_LLADDR(sc->sc_ifp)[0];
1183152315Sru	((u_int8_t *)&p1)[0] = IF_LLADDR(sc->sc_ifp)[1];
1184152315Sru	((u_int8_t *)&p2)[3] = IF_LLADDR(sc->sc_ifp)[2];
1185152315Sru	((u_int8_t *)&p2)[2] = IF_LLADDR(sc->sc_ifp)[3];
1186152315Sru	((u_int8_t *)&p2)[1] = IF_LLADDR(sc->sc_ifp)[4];
1187152315Sru	((u_int8_t *)&p2)[0] = IF_LLADDR(sc->sc_ifp)[5];
118880219Swpaul	txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0,
118980219Swpaul	    NULL, NULL, NULL, 1);
119080219Swpaul
119180219Swpaul	txp_set_filter(sc);
119280219Swpaul
119380219Swpaul	txp_rxring_fill(sc);
119480219Swpaul
119580219Swpaul	txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
119680219Swpaul	txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
119780219Swpaul
119880219Swpaul	WRITE_REG(sc, TXP_IER, TXP_INT_RESERVED | TXP_INT_SELF |
119980219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
120080219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
120180219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
120280219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
120380219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
120480219Swpaul
1205148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1206148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
120780219Swpaul	ifp->if_timer = 0;
120880219Swpaul
1209151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
121080219Swpaul}
121180219Swpaul
121280219Swpaulstatic void
121380219Swpaultxp_tick(vsc)
121480219Swpaul	void *vsc;
121580219Swpaul{
121680219Swpaul	struct txp_softc *sc = vsc;
1217147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
121880219Swpaul	struct txp_rsp_desc *rsp = NULL;
121980219Swpaul	struct txp_ext_desc *ext;
122080219Swpaul
1221151772Sjhb	TXP_LOCK_ASSERT(sc);
122280219Swpaul	txp_rxbuf_reclaim(sc);
122380219Swpaul
122480219Swpaul	if (txp_command2(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
122580219Swpaul	    &rsp, 1))
122680219Swpaul		goto out;
122780219Swpaul	if (rsp->rsp_numdesc != 6)
122880219Swpaul		goto out;
122980219Swpaul	if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
123080219Swpaul	    NULL, NULL, NULL, 1))
123180219Swpaul		goto out;
123280219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
123380219Swpaul
123480219Swpaul	ifp->if_ierrors += ext[3].ext_2 + ext[3].ext_3 + ext[3].ext_4 +
123580219Swpaul	    ext[4].ext_1 + ext[4].ext_4;
123680219Swpaul	ifp->if_oerrors += ext[0].ext_1 + ext[1].ext_1 + ext[1].ext_4 +
123780219Swpaul	    ext[2].ext_1;
123880219Swpaul	ifp->if_collisions += ext[0].ext_2 + ext[0].ext_3 + ext[1].ext_2 +
123980219Swpaul	    ext[1].ext_3;
124080219Swpaul	ifp->if_opackets += rsp->rsp_par2;
124180219Swpaul	ifp->if_ipackets += ext[2].ext_3;
124280219Swpaul
124380219Swpaulout:
124480219Swpaul	if (rsp != NULL)
124580219Swpaul		free(rsp, M_DEVBUF);
124680219Swpaul
1247151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
124880219Swpaul
124980219Swpaul	return;
125080219Swpaul}
125180219Swpaul
125280219Swpaulstatic void
125380219Swpaultxp_start(ifp)
125480219Swpaul	struct ifnet *ifp;
125580219Swpaul{
1256151772Sjhb	struct txp_softc *sc;
1257151772Sjhb
1258151772Sjhb	sc = ifp->if_softc;
1259151772Sjhb	TXP_LOCK(sc);
1260151772Sjhb	txp_start_locked(ifp);
1261151772Sjhb	TXP_UNLOCK(sc);
1262151772Sjhb}
1263151772Sjhb
1264151772Sjhbstatic void
1265151772Sjhbtxp_start_locked(ifp)
1266151772Sjhb	struct ifnet *ifp;
1267151772Sjhb{
126880219Swpaul	struct txp_softc *sc = ifp->if_softc;
126980219Swpaul	struct txp_tx_ring *r = &sc->sc_txhir;
127080219Swpaul	struct txp_tx_desc *txd;
127180219Swpaul	struct txp_frag_desc *fxd;
127280219Swpaul	struct mbuf *m, *m0;
127380219Swpaul	struct txp_swdesc *sd;
127480219Swpaul	u_int32_t firstprod, firstcnt, prod, cnt;
1275106937Ssam	struct m_tag *mtag;
127680219Swpaul
1277151772Sjhb	TXP_LOCK_ASSERT(sc);
1278148887Srwatson	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
1279148887Srwatson	   IFF_DRV_RUNNING)
128080219Swpaul		return;
128180219Swpaul
128280219Swpaul	prod = r->r_prod;
128380219Swpaul	cnt = r->r_cnt;
128480219Swpaul
128580219Swpaul	while (1) {
128680219Swpaul		IF_DEQUEUE(&ifp->if_snd, m);
128780219Swpaul		if (m == NULL)
128880219Swpaul			break;
128980219Swpaul
129080219Swpaul		firstprod = prod;
129180219Swpaul		firstcnt = cnt;
129280219Swpaul
129380219Swpaul		sd = sc->sc_txd + prod;
129480219Swpaul		sd->sd_mbuf = m;
129580219Swpaul
129680219Swpaul		if ((TX_ENTRIES - cnt) < 4)
129780219Swpaul			goto oactive;
129880219Swpaul
129980219Swpaul		txd = r->r_desc + prod;
130080219Swpaul
130180219Swpaul		txd->tx_flags = TX_FLAGS_TYPE_DATA;
130280219Swpaul		txd->tx_numdesc = 0;
130380219Swpaul		txd->tx_addrlo = 0;
130480219Swpaul		txd->tx_addrhi = 0;
130580219Swpaul		txd->tx_totlen = 0;
130680219Swpaul		txd->tx_pflags = 0;
130780219Swpaul
130880219Swpaul		if (++prod == TX_ENTRIES)
130980219Swpaul			prod = 0;
131080219Swpaul
131180219Swpaul		if (++cnt >= (TX_ENTRIES - 4))
131280219Swpaul			goto oactive;
131380219Swpaul
1314106937Ssam		mtag = VLAN_OUTPUT_TAG(ifp, m);
1315106937Ssam		if (mtag != NULL) {
131680219Swpaul			txd->tx_pflags = TX_PFLAGS_VLAN |
1317106937Ssam			    (htons(VLAN_TAG_VALUE(mtag)) << TX_PFLAGS_VLANTAG_S);
131880219Swpaul		}
131983115Sbrooks
132080219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_IP)
132180219Swpaul			txd->tx_pflags |= TX_PFLAGS_IPCKSUM;
132280219Swpaul
132380219Swpaul#if 0
132480219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_TCP)
132580219Swpaul			txd->tx_pflags |= TX_PFLAGS_TCPCKSUM;
132680219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_UDP)
132780219Swpaul			txd->tx_pflags |= TX_PFLAGS_UDPCKSUM;
132880219Swpaul#endif
132980219Swpaul
133080219Swpaul		fxd = (struct txp_frag_desc *)(r->r_desc + prod);
133180219Swpaul		for (m0 = m; m0 != NULL; m0 = m0->m_next) {
133280219Swpaul			if (m0->m_len == 0)
133380219Swpaul				continue;
133480219Swpaul			if (++cnt >= (TX_ENTRIES - 4))
133580219Swpaul				goto oactive;
133680219Swpaul
133780219Swpaul			txd->tx_numdesc++;
133880219Swpaul
133980219Swpaul			fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG;
134080219Swpaul			fxd->frag_rsvd1 = 0;
134180219Swpaul			fxd->frag_len = m0->m_len;
134280219Swpaul			fxd->frag_addrlo = vtophys(mtod(m0, vm_offset_t));
134380219Swpaul			fxd->frag_addrhi = 0;
134480219Swpaul			fxd->frag_rsvd2 = 0;
134580219Swpaul
134680219Swpaul			if (++prod == TX_ENTRIES) {
134780219Swpaul				fxd = (struct txp_frag_desc *)r->r_desc;
134880219Swpaul				prod = 0;
134980219Swpaul			} else
135080219Swpaul				fxd++;
135180219Swpaul
135280219Swpaul		}
135380219Swpaul
135480219Swpaul		ifp->if_timer = 5;
135580219Swpaul
1356106937Ssam		BPF_MTAP(ifp, m);
135780219Swpaul		WRITE_REG(sc, r->r_reg, TXP_IDX2OFFSET(prod));
135880219Swpaul	}
135980219Swpaul
136080219Swpaul	r->r_prod = prod;
136180219Swpaul	r->r_cnt = cnt;
136280219Swpaul	return;
136380219Swpaul
136480219Swpauloactive:
1365148887Srwatson	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
136680219Swpaul	r->r_prod = firstprod;
136780219Swpaul	r->r_cnt = firstcnt;
136880219Swpaul	IF_PREPEND(&ifp->if_snd, m);
136980219Swpaul	return;
137080219Swpaul}
137180219Swpaul
137280219Swpaul/*
137380219Swpaul * Handle simple commands sent to the typhoon
137480219Swpaul */
137580219Swpaulstatic int
137680219Swpaultxp_command(sc, id, in1, in2, in3, out1, out2, out3, wait)
137780219Swpaul	struct txp_softc *sc;
137880219Swpaul	u_int16_t id, in1, *out1;
137980219Swpaul	u_int32_t in2, in3, *out2, *out3;
138080219Swpaul	int wait;
138180219Swpaul{
138280219Swpaul	struct txp_rsp_desc *rsp = NULL;
138380219Swpaul
138480219Swpaul	if (txp_command2(sc, id, in1, in2, in3, NULL, 0, &rsp, wait))
138580219Swpaul		return (-1);
138680219Swpaul
138780219Swpaul	if (!wait)
138880219Swpaul		return (0);
138980219Swpaul
139080219Swpaul	if (out1 != NULL)
139180219Swpaul		*out1 = rsp->rsp_par1;
139280219Swpaul	if (out2 != NULL)
139380219Swpaul		*out2 = rsp->rsp_par2;
139480219Swpaul	if (out3 != NULL)
139580219Swpaul		*out3 = rsp->rsp_par3;
139680219Swpaul	free(rsp, M_DEVBUF);
139780219Swpaul	return (0);
139880219Swpaul}
139980219Swpaul
140080219Swpaulstatic int
140180219Swpaultxp_command2(sc, id, in1, in2, in3, in_extp, in_extn, rspp, wait)
140280219Swpaul	struct txp_softc *sc;
140380219Swpaul	u_int16_t id, in1;
140480219Swpaul	u_int32_t in2, in3;
140580219Swpaul	struct txp_ext_desc *in_extp;
140680219Swpaul	u_int8_t in_extn;
140780219Swpaul	struct txp_rsp_desc **rspp;
140880219Swpaul	int wait;
140980219Swpaul{
141080219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
141180219Swpaul	struct txp_cmd_desc *cmd;
141280219Swpaul	struct txp_ext_desc *ext;
141380219Swpaul	u_int32_t idx, i;
141480219Swpaul	u_int16_t seq;
141580219Swpaul
141680219Swpaul	if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
141780219Swpaul		device_printf(sc->sc_dev, "no free cmd descriptors\n");
141880219Swpaul		return (-1);
141980219Swpaul	}
142080219Swpaul
142180219Swpaul	idx = sc->sc_cmdring.lastwrite;
142280219Swpaul	cmd = (struct txp_cmd_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
142380219Swpaul	bzero(cmd, sizeof(*cmd));
142480219Swpaul
142580219Swpaul	cmd->cmd_numdesc = in_extn;
142680219Swpaul	cmd->cmd_seq = seq = sc->sc_seq++;
142780219Swpaul	cmd->cmd_id = id;
142880219Swpaul	cmd->cmd_par1 = in1;
142980219Swpaul	cmd->cmd_par2 = in2;
143080219Swpaul	cmd->cmd_par3 = in3;
143180219Swpaul	cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
143280219Swpaul	    (wait ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
143380219Swpaul
143480219Swpaul	idx += sizeof(struct txp_cmd_desc);
143580219Swpaul	if (idx == sc->sc_cmdring.size)
143680219Swpaul		idx = 0;
143780219Swpaul
143880219Swpaul	for (i = 0; i < in_extn; i++) {
143980219Swpaul		ext = (struct txp_ext_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
144080219Swpaul		bcopy(in_extp, ext, sizeof(struct txp_ext_desc));
144180219Swpaul		in_extp++;
144280219Swpaul		idx += sizeof(struct txp_cmd_desc);
144380219Swpaul		if (idx == sc->sc_cmdring.size)
144480219Swpaul			idx = 0;
144580219Swpaul	}
144680219Swpaul
144780219Swpaul	sc->sc_cmdring.lastwrite = idx;
144880219Swpaul
144980219Swpaul	WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
145080219Swpaul
145180219Swpaul	if (!wait)
145280219Swpaul		return (0);
145380219Swpaul
145480219Swpaul	for (i = 0; i < 10000; i++) {
145580219Swpaul		idx = hv->hv_resp_read_idx;
145680219Swpaul		if (idx != hv->hv_resp_write_idx) {
145780219Swpaul			*rspp = NULL;
145880219Swpaul			if (txp_response(sc, idx, id, seq, rspp))
145980219Swpaul				return (-1);
146080219Swpaul			if (*rspp != NULL)
146180219Swpaul				break;
146280219Swpaul		}
146380219Swpaul		DELAY(50);
146480219Swpaul	}
146580219Swpaul	if (i == 1000 || (*rspp) == NULL) {
146680219Swpaul		device_printf(sc->sc_dev, "0x%x command failed\n", id);
146780219Swpaul		return (-1);
146880219Swpaul	}
146980219Swpaul
147080219Swpaul	return (0);
147180219Swpaul}
147280219Swpaul
147380219Swpaulstatic int
147480219Swpaultxp_response(sc, ridx, id, seq, rspp)
147580219Swpaul	struct txp_softc *sc;
147680219Swpaul	u_int32_t ridx;
147780219Swpaul	u_int16_t id;
147880219Swpaul	u_int16_t seq;
147980219Swpaul	struct txp_rsp_desc **rspp;
148080219Swpaul{
148180219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
148280219Swpaul	struct txp_rsp_desc *rsp;
148380219Swpaul
148480219Swpaul	while (ridx != hv->hv_resp_write_idx) {
148580219Swpaul		rsp = (struct txp_rsp_desc *)(((u_int8_t *)sc->sc_rspring.base) + ridx);
148680219Swpaul
148780219Swpaul		if (id == rsp->rsp_id && rsp->rsp_seq == seq) {
148880219Swpaul			*rspp = (struct txp_rsp_desc *)malloc(
148980219Swpaul			    sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
149080219Swpaul			    M_DEVBUF, M_NOWAIT);
149180219Swpaul			if ((*rspp) == NULL)
149280219Swpaul				return (-1);
149380219Swpaul			txp_rsp_fixup(sc, rsp, *rspp);
149480219Swpaul			return (0);
149580219Swpaul		}
149680219Swpaul
149780219Swpaul		if (rsp->rsp_flags & RSP_FLAGS_ERROR) {
149880219Swpaul			device_printf(sc->sc_dev, "response error!\n");
149980219Swpaul			txp_rsp_fixup(sc, rsp, NULL);
150080219Swpaul			ridx = hv->hv_resp_read_idx;
150180219Swpaul			continue;
150280219Swpaul		}
150380219Swpaul
150480219Swpaul		switch (rsp->rsp_id) {
150580219Swpaul		case TXP_CMD_CYCLE_STATISTICS:
150680219Swpaul		case TXP_CMD_MEDIA_STATUS_READ:
150780219Swpaul			break;
150880219Swpaul		case TXP_CMD_HELLO_RESPONSE:
150980219Swpaul			device_printf(sc->sc_dev, "hello\n");
151080219Swpaul			break;
151180219Swpaul		default:
151280219Swpaul			device_printf(sc->sc_dev, "unknown id(0x%x)\n",
151380219Swpaul			    rsp->rsp_id);
151480219Swpaul		}
151580219Swpaul
151680219Swpaul		txp_rsp_fixup(sc, rsp, NULL);
151780219Swpaul		ridx = hv->hv_resp_read_idx;
151880219Swpaul		hv->hv_resp_read_idx = ridx;
151980219Swpaul	}
152080219Swpaul
152180219Swpaul	return (0);
152280219Swpaul}
152380219Swpaul
152480219Swpaulstatic void
152580219Swpaultxp_rsp_fixup(sc, rsp, dst)
152680219Swpaul	struct txp_softc *sc;
152780219Swpaul	struct txp_rsp_desc *rsp, *dst;
152880219Swpaul{
152980219Swpaul	struct txp_rsp_desc *src = rsp;
153080219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
153180219Swpaul	u_int32_t i, ridx;
153280219Swpaul
153380219Swpaul	ridx = hv->hv_resp_read_idx;
153480219Swpaul
153580219Swpaul	for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
153680219Swpaul		if (dst != NULL)
153780219Swpaul			bcopy(src, dst++, sizeof(struct txp_rsp_desc));
153880219Swpaul		ridx += sizeof(struct txp_rsp_desc);
153980219Swpaul		if (ridx == sc->sc_rspring.size) {
154080219Swpaul			src = sc->sc_rspring.base;
154180219Swpaul			ridx = 0;
154280219Swpaul		} else
154380219Swpaul			src++;
154480219Swpaul		sc->sc_rspring.lastwrite = hv->hv_resp_read_idx = ridx;
154580219Swpaul	}
154680219Swpaul
154780219Swpaul	hv->hv_resp_read_idx = ridx;
154880219Swpaul}
154980219Swpaul
155080219Swpaulstatic int
155180219Swpaultxp_cmd_desc_numfree(sc)
155280219Swpaul	struct txp_softc *sc;
155380219Swpaul{
155480219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
155580219Swpaul	struct txp_boot_record *br = sc->sc_boot;
155680219Swpaul	u_int32_t widx, ridx, nfree;
155780219Swpaul
155880219Swpaul	widx = sc->sc_cmdring.lastwrite;
155980219Swpaul	ridx = hv->hv_cmd_read_idx;
156080219Swpaul
156180219Swpaul	if (widx == ridx) {
156280219Swpaul		/* Ring is completely free */
156380219Swpaul		nfree = br->br_cmd_siz - sizeof(struct txp_cmd_desc);
156480219Swpaul	} else {
156580219Swpaul		if (widx > ridx)
156680219Swpaul			nfree = br->br_cmd_siz -
156780219Swpaul			    (widx - ridx + sizeof(struct txp_cmd_desc));
156880219Swpaul		else
156980219Swpaul			nfree = ridx - widx - sizeof(struct txp_cmd_desc);
157080219Swpaul	}
157180219Swpaul
157280219Swpaul	return (nfree / sizeof(struct txp_cmd_desc));
157380219Swpaul}
157480219Swpaul
157580219Swpaulstatic void
157680219Swpaultxp_stop(sc)
157780219Swpaul	struct txp_softc *sc;
157880219Swpaul{
157980219Swpaul	struct ifnet *ifp;
158080219Swpaul
1581151772Sjhb	TXP_LOCK_ASSERT(sc);
1582147256Sbrooks	ifp = sc->sc_ifp;
158380219Swpaul
1584148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
158580219Swpaul
1586151772Sjhb	callout_stop(&sc->sc_tick);
158780219Swpaul
158880219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
158980219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
159080219Swpaul
159180219Swpaul	txp_rxring_empty(sc);
159280219Swpaul
159380219Swpaul	return;
159480219Swpaul}
159580219Swpaul
159680219Swpaulstatic void
159780219Swpaultxp_watchdog(ifp)
159880219Swpaul	struct ifnet *ifp;
159980219Swpaul{
160080219Swpaul	return;
160180219Swpaul}
160280219Swpaul
160380219Swpaulstatic int
160480219Swpaultxp_ifmedia_upd(ifp)
160580219Swpaul	struct ifnet *ifp;
160680219Swpaul{
160780219Swpaul	struct txp_softc *sc = ifp->if_softc;
160880219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
160980219Swpaul	u_int16_t new_xcvr;
161080219Swpaul
1611151772Sjhb	TXP_LOCK(sc);
1612151772Sjhb	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) {
1613151772Sjhb		TXP_UNLOCK(sc);
161480219Swpaul		return (EINVAL);
1615151772Sjhb	}
161680219Swpaul
161780219Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
161880219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
161980219Swpaul			new_xcvr = TXP_XCVR_10_FDX;
162080219Swpaul		else
162180219Swpaul			new_xcvr = TXP_XCVR_10_HDX;
162280219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
162380219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
162480219Swpaul			new_xcvr = TXP_XCVR_100_FDX;
162580219Swpaul		else
162680219Swpaul			new_xcvr = TXP_XCVR_100_HDX;
162780219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
162880219Swpaul		new_xcvr = TXP_XCVR_AUTO;
1629151772Sjhb	} else {
1630151772Sjhb		TXP_UNLOCK(sc);
163180219Swpaul		return (EINVAL);
1632151772Sjhb	}
163380219Swpaul
163480219Swpaul	/* nothing to do */
1635151772Sjhb	if (sc->sc_xcvr == new_xcvr) {
1636151772Sjhb		TXP_UNLOCK(sc);
163780219Swpaul		return (0);
1638151772Sjhb	}
163980219Swpaul
164080219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
164180219Swpaul	    NULL, NULL, NULL, 0);
164280219Swpaul	sc->sc_xcvr = new_xcvr;
1643151772Sjhb	TXP_UNLOCK(sc);
164480219Swpaul
164580219Swpaul	return (0);
164680219Swpaul}
164780219Swpaul
164880219Swpaulstatic void
164980219Swpaultxp_ifmedia_sts(ifp, ifmr)
165080219Swpaul	struct ifnet *ifp;
165180219Swpaul	struct ifmediareq *ifmr;
165280219Swpaul{
165380219Swpaul	struct txp_softc *sc = ifp->if_softc;
165480219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
165580219Swpaul	u_int16_t bmsr, bmcr, anlpar;
165680219Swpaul
165780219Swpaul	ifmr->ifm_status = IFM_AVALID;
165880219Swpaul	ifmr->ifm_active = IFM_ETHER;
165980219Swpaul
1660151772Sjhb	TXP_LOCK(sc);
166180219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
166280219Swpaul	    &bmsr, NULL, NULL, 1))
166380219Swpaul		goto bail;
166480219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
166580219Swpaul	    &bmsr, NULL, NULL, 1))
166680219Swpaul		goto bail;
166780219Swpaul
166880219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
166980219Swpaul	    &bmcr, NULL, NULL, 1))
167080219Swpaul		goto bail;
167180219Swpaul
167280219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
167380219Swpaul	    &anlpar, NULL, NULL, 1))
167480219Swpaul		goto bail;
1675151772Sjhb	TXP_UNLOCK(sc);
167680219Swpaul
167780219Swpaul	if (bmsr & BMSR_LINK)
167880219Swpaul		ifmr->ifm_status |= IFM_ACTIVE;
167980219Swpaul
168080219Swpaul	if (bmcr & BMCR_ISO) {
168180219Swpaul		ifmr->ifm_active |= IFM_NONE;
168280219Swpaul		ifmr->ifm_status = 0;
168380219Swpaul		return;
168480219Swpaul	}
168580219Swpaul
168680219Swpaul	if (bmcr & BMCR_LOOP)
168780219Swpaul		ifmr->ifm_active |= IFM_LOOP;
168880219Swpaul
168980219Swpaul	if (bmcr & BMCR_AUTOEN) {
169080219Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
169180219Swpaul			ifmr->ifm_active |= IFM_NONE;
169280219Swpaul			return;
169380219Swpaul		}
169480219Swpaul
169580219Swpaul		if (anlpar & ANLPAR_T4)
169680219Swpaul			ifmr->ifm_active |= IFM_100_T4;
169780219Swpaul		else if (anlpar & ANLPAR_TX_FD)
169880219Swpaul			ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
169980219Swpaul		else if (anlpar & ANLPAR_TX)
170080219Swpaul			ifmr->ifm_active |= IFM_100_TX;
170180219Swpaul		else if (anlpar & ANLPAR_10_FD)
170280219Swpaul			ifmr->ifm_active |= IFM_10_T|IFM_FDX;
170380219Swpaul		else if (anlpar & ANLPAR_10)
170480219Swpaul			ifmr->ifm_active |= IFM_10_T;
170580219Swpaul		else
170680219Swpaul			ifmr->ifm_active |= IFM_NONE;
170780219Swpaul	} else
170880219Swpaul		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
170980219Swpaul	return;
171080219Swpaul
171180219Swpaulbail:
1712151772Sjhb	TXP_UNLOCK(sc);
171380219Swpaul	ifmr->ifm_active |= IFM_NONE;
171480219Swpaul	ifmr->ifm_status &= ~IFM_AVALID;
171580219Swpaul}
171680219Swpaul
171780219Swpaul#ifdef TXP_DEBUG
171880219Swpaulstatic void
171980219Swpaultxp_show_descriptor(d)
172080219Swpaul	void *d;
172180219Swpaul{
172280219Swpaul	struct txp_cmd_desc *cmd = d;
172380219Swpaul	struct txp_rsp_desc *rsp = d;
172480219Swpaul	struct txp_tx_desc *txd = d;
172580219Swpaul	struct txp_frag_desc *frgd = d;
172680219Swpaul
172780219Swpaul	switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
172880219Swpaul	case CMD_FLAGS_TYPE_CMD:
172980219Swpaul		/* command descriptor */
173080219Swpaul		printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
173180219Swpaul		    cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq,
173280219Swpaul		    cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3);
173380219Swpaul		break;
173480219Swpaul	case CMD_FLAGS_TYPE_RESP:
173580219Swpaul		/* response descriptor */
173680219Swpaul		printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
173780219Swpaul		    rsp->rsp_flags, rsp->rsp_numdesc, rsp->rsp_id, rsp->rsp_seq,
173880219Swpaul		    rsp->rsp_par1, rsp->rsp_par2, rsp->rsp_par3);
173980219Swpaul		break;
174080219Swpaul	case CMD_FLAGS_TYPE_DATA:
174180219Swpaul		/* data header (assuming tx for now) */
174280219Swpaul		printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
174380219Swpaul		    txd->tx_flags, txd->tx_numdesc, txd->tx_totlen,
174480219Swpaul		    txd->tx_addrlo, txd->tx_addrhi, txd->tx_pflags);
174580219Swpaul		break;
174680219Swpaul	case CMD_FLAGS_TYPE_FRAG:
174780219Swpaul		/* fragment descriptor */
174880219Swpaul		printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
174980219Swpaul		    frgd->frag_flags, frgd->frag_rsvd1, frgd->frag_len,
175080219Swpaul		    frgd->frag_addrlo, frgd->frag_addrhi, frgd->frag_rsvd2);
175180219Swpaul		break;
175280219Swpaul	default:
175380219Swpaul		printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
175480219Swpaul		    cmd->cmd_flags & CMD_FLAGS_TYPE_M,
175580219Swpaul		    cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq,
175680219Swpaul		    cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3);
175780219Swpaul		break;
175880219Swpaul	}
175980219Swpaul}
176080219Swpaul#endif
176180219Swpaul
176280219Swpaulstatic void
176380219Swpaultxp_set_filter(sc)
176480219Swpaul	struct txp_softc *sc;
176580219Swpaul{
1766147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
176780219Swpaul	u_int32_t crc, carry, hashbit, hash[2];
176880219Swpaul	u_int16_t filter;
176980219Swpaul	u_int8_t octet;
177080219Swpaul	int i, j, mcnt = 0;
177180219Swpaul	struct ifmultiaddr *ifma;
177280219Swpaul	char *enm;
177380219Swpaul
177480219Swpaul	if (ifp->if_flags & IFF_PROMISC) {
177580219Swpaul		filter = TXP_RXFILT_PROMISC;
177680219Swpaul		goto setit;
177780219Swpaul	}
177880219Swpaul
177980219Swpaul	filter = TXP_RXFILT_DIRECT;
178080219Swpaul
178180219Swpaul	if (ifp->if_flags & IFF_BROADCAST)
178280219Swpaul		filter |= TXP_RXFILT_BROADCAST;
178380219Swpaul
178480219Swpaul	if (ifp->if_flags & IFF_ALLMULTI)
178580219Swpaul		filter |= TXP_RXFILT_ALLMULTI;
178680219Swpaul	else {
178780219Swpaul		hash[0] = hash[1] = 0;
178880219Swpaul
1789148654Srwatson		IF_ADDR_LOCK(ifp);
179080219Swpaul		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
179180219Swpaul			if (ifma->ifma_addr->sa_family != AF_LINK)
179280219Swpaul				continue;
179380219Swpaul
179480219Swpaul			enm = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
179580219Swpaul			mcnt++;
179680219Swpaul			crc = 0xffffffff;
179780219Swpaul
179880219Swpaul			for (i = 0; i < ETHER_ADDR_LEN; i++) {
179980219Swpaul				octet = enm[i];
180080219Swpaul				for (j = 0; j < 8; j++) {
180180219Swpaul					carry = ((crc & 0x80000000) ? 1 : 0) ^
180280219Swpaul					    (octet & 1);
180380219Swpaul					crc <<= 1;
180480219Swpaul					octet >>= 1;
180580219Swpaul					if (carry)
180680219Swpaul						crc = (crc ^ TXP_POLYNOMIAL) |
180780219Swpaul						    carry;
180880219Swpaul				}
180980219Swpaul			}
181080219Swpaul			hashbit = (u_int16_t)(crc & (64 - 1));
181180219Swpaul			hash[hashbit / 32] |= (1 << hashbit % 32);
181280219Swpaul		}
1813148654Srwatson		IF_ADDR_UNLOCK(ifp);
181480219Swpaul
181580219Swpaul		if (mcnt > 0) {
181680219Swpaul			filter |= TXP_RXFILT_HASHMULTI;
181780219Swpaul			txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE,
181880219Swpaul			    2, hash[0], hash[1], NULL, NULL, NULL, 0);
181980219Swpaul		}
182080219Swpaul	}
182180219Swpaul
182280219Swpaulsetit:
182380219Swpaul
182480219Swpaul	txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
182580219Swpaul	    NULL, NULL, NULL, 1);
182680219Swpaul
182780219Swpaul	return;
182880219Swpaul}
182980219Swpaul
183080219Swpaulstatic void
183180219Swpaultxp_capabilities(sc)
183280219Swpaul	struct txp_softc *sc;
183380219Swpaul{
1834147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
183580219Swpaul	struct txp_rsp_desc *rsp = NULL;
183680219Swpaul	struct txp_ext_desc *ext;
183780219Swpaul
183880219Swpaul	if (txp_command2(sc, TXP_CMD_OFFLOAD_READ, 0, 0, 0, NULL, 0, &rsp, 1))
183980219Swpaul		goto out;
184080219Swpaul
184180219Swpaul	if (rsp->rsp_numdesc != 1)
184280219Swpaul		goto out;
184380219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
184480219Swpaul
184580219Swpaul	sc->sc_tx_capability = ext->ext_1 & OFFLOAD_MASK;
184680219Swpaul	sc->sc_rx_capability = ext->ext_2 & OFFLOAD_MASK;
184783631Sjlemon	ifp->if_capabilities = 0;
184880219Swpaul
184980219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_VLAN) {
185080219Swpaul		sc->sc_tx_capability |= OFFLOAD_VLAN;
185180219Swpaul		sc->sc_rx_capability |= OFFLOAD_VLAN;
1852106937Ssam		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
185380219Swpaul	}
185480219Swpaul
185580219Swpaul#if 0
185680219Swpaul	/* not ready yet */
185780219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPSEC) {
185880219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPSEC;
185980219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPSEC;
186080219Swpaul		ifp->if_capabilities |= IFCAP_IPSEC;
186180219Swpaul	}
186280219Swpaul#endif
186380219Swpaul
186480219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPCKSUM) {
186580219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPCKSUM;
186680219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPCKSUM;
186783631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
186880219Swpaul		ifp->if_hwassist |= CSUM_IP;
186980219Swpaul	}
187080219Swpaul
187180219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_TCPCKSUM) {
187280219Swpaul#if 0
187380219Swpaul		sc->sc_tx_capability |= OFFLOAD_TCPCKSUM;
187480219Swpaul#endif
187580219Swpaul		sc->sc_rx_capability |= OFFLOAD_TCPCKSUM;
187683631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
187780219Swpaul	}
187880219Swpaul
187980219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_UDPCKSUM) {
188080219Swpaul#if 0
188180219Swpaul		sc->sc_tx_capability |= OFFLOAD_UDPCKSUM;
188280219Swpaul#endif
188380219Swpaul		sc->sc_rx_capability |= OFFLOAD_UDPCKSUM;
188483631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
188580219Swpaul	}
188683631Sjlemon	ifp->if_capenable = ifp->if_capabilities;
188780219Swpaul
188880219Swpaul	if (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0,
188980219Swpaul	    sc->sc_tx_capability, sc->sc_rx_capability, NULL, NULL, NULL, 1))
189080219Swpaul		goto out;
189180219Swpaul
189280219Swpaulout:
189380219Swpaul	if (rsp != NULL)
189480219Swpaul		free(rsp, M_DEVBUF);
189580219Swpaul
189680219Swpaul	return;
189780219Swpaul}
1898