if_txp.c revision 189687
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 189687 2009-03-11 08:41:57Z yongari $");
39119418Sobrien
4080219Swpaul/*
4180219Swpaul * Driver for 3c990 (Typhoon) Ethernet ASIC
4280219Swpaul */
4380219Swpaul#include <sys/param.h>
4480219Swpaul#include <sys/systm.h>
4580219Swpaul#include <sys/sockio.h>
4680219Swpaul#include <sys/mbuf.h>
4780219Swpaul#include <sys/malloc.h>
4880219Swpaul#include <sys/kernel.h>
49129879Sphk#include <sys/module.h>
5080219Swpaul#include <sys/socket.h>
5180219Swpaul
5280219Swpaul#include <net/if.h>
5380219Swpaul#include <net/if_arp.h>
5480219Swpaul#include <net/ethernet.h>
5580219Swpaul#include <net/if_dl.h>
5680219Swpaul#include <net/if_types.h>
5783115Sbrooks#include <net/if_vlan_var.h>
5880219Swpaul
5980219Swpaul#include <netinet/in.h>
6080219Swpaul#include <netinet/in_systm.h>
6180219Swpaul#include <netinet/in_var.h>
6280219Swpaul#include <netinet/ip.h>
6380219Swpaul#include <netinet/if_ether.h>
6480219Swpaul#include <machine/in_cksum.h>
6580219Swpaul
6680219Swpaul#include <net/if_media.h>
6780219Swpaul
6880219Swpaul#include <net/bpf.h>
6980219Swpaul
7080219Swpaul#include <vm/vm.h>              /* for vtophys */
7180219Swpaul#include <vm/pmap.h>            /* for vtophys */
7280219Swpaul#include <machine/bus.h>
7380219Swpaul#include <machine/resource.h>
7480219Swpaul#include <sys/bus.h>
7580219Swpaul#include <sys/rman.h>
7680219Swpaul
7780219Swpaul#include <dev/mii/mii.h>
7880219Swpaul#include <dev/mii/miivar.h>
7980219Swpaul#include <dev/pci/pcireg.h>
8080219Swpaul#include <dev/pci/pcivar.h>
8180219Swpaul
8280219Swpaul#define TXP_USEIOSPACE
8380229Swpaul#define __STRICT_ALIGNMENT
8480219Swpaul
8580219Swpaul#include <dev/txp/if_txpreg.h>
8680219Swpaul#include <dev/txp/3c990img.h>
8780219Swpaul
8880219Swpaul#ifndef lint
8980219Swpaulstatic const char rcsid[] =
9080219Swpaul  "$FreeBSD: head/sys/dev/txp/if_txp.c 189687 2009-03-11 08:41:57Z yongari $";
9180219Swpaul#endif
9280219Swpaul
9380219Swpaul/*
9480219Swpaul * Various supported device vendors/types and their names.
9580219Swpaul */
9680219Swpaulstatic struct txp_type txp_devs[] = {
9780219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95,
9880219Swpaul	    "3Com 3cR990-TX-95 Etherlink with 3XP Processor" },
9980219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97,
10080219Swpaul	    "3Com 3cR990-TX-97 Etherlink with 3XP Processor" },
10180219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM,
10280219Swpaul	    "3Com 3cR990B-TXM Etherlink with 3XP Processor" },
10380219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95,
10480219Swpaul	    "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" },
10580219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97,
10680219Swpaul	    "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" },
10780219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV,
10880219Swpaul	    "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" },
10980219Swpaul	{ 0, 0, NULL }
11080219Swpaul};
11180219Swpaul
112149678Sjhbstatic int txp_probe(device_t);
113149678Sjhbstatic int txp_attach(device_t);
114149678Sjhbstatic int txp_detach(device_t);
115149678Sjhbstatic void txp_intr(void *);
116149678Sjhbstatic void txp_tick(void *);
117149678Sjhbstatic int txp_shutdown(device_t);
118149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t);
119149678Sjhbstatic void txp_start(struct ifnet *);
120151772Sjhbstatic void txp_start_locked(struct ifnet *);
121149678Sjhbstatic void txp_stop(struct txp_softc *);
122149678Sjhbstatic void txp_init(void *);
123151772Sjhbstatic void txp_init_locked(struct txp_softc *);
124149678Sjhbstatic void txp_watchdog(struct ifnet *);
12580219Swpaul
12692739Salfredstatic void txp_release_resources(struct txp_softc *);
12792739Salfredstatic int txp_chip_init(struct txp_softc *);
12892739Salfredstatic int txp_reset_adapter(struct txp_softc *);
12992739Salfredstatic int txp_download_fw(struct txp_softc *);
13092739Salfredstatic int txp_download_fw_wait(struct txp_softc *);
131149678Sjhbstatic int txp_download_fw_section(struct txp_softc *,
13292739Salfred    struct txp_fw_section_header *, int);
13392739Salfredstatic int txp_alloc_rings(struct txp_softc *);
13492739Salfredstatic int txp_rxring_fill(struct txp_softc *);
13592739Salfredstatic void txp_rxring_empty(struct txp_softc *);
13692739Salfredstatic void txp_set_filter(struct txp_softc *);
13780219Swpaul
13892739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *);
139149678Sjhbstatic int txp_command(struct txp_softc *, u_int16_t, u_int16_t, u_int32_t,
14092739Salfred    u_int32_t, u_int16_t *, u_int32_t *, u_int32_t *, int);
141149678Sjhbstatic int txp_command2(struct txp_softc *, u_int16_t, u_int16_t,
14280219Swpaul    u_int32_t, u_int32_t, struct txp_ext_desc *, u_int8_t,
14392739Salfred    struct txp_rsp_desc **, int);
144149678Sjhbstatic int txp_response(struct txp_softc *, u_int32_t, u_int16_t, u_int16_t,
14592739Salfred    struct txp_rsp_desc **);
146149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
14792739Salfred    struct txp_rsp_desc *);
14892739Salfredstatic void txp_capabilities(struct txp_softc *);
14980219Swpaul
15092739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
15192739Salfredstatic int txp_ifmedia_upd(struct ifnet *);
15280219Swpaul#ifdef TXP_DEBUG
15392739Salfredstatic void txp_show_descriptor(void *);
15480219Swpaul#endif
15592739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *);
15692739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *);
15792739Salfredstatic void txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *);
15880219Swpaul
15980219Swpaul#ifdef TXP_USEIOSPACE
16080219Swpaul#define TXP_RES			SYS_RES_IOPORT
16180219Swpaul#define TXP_RID			TXP_PCI_LOIO
16280219Swpaul#else
16380219Swpaul#define TXP_RES			SYS_RES_MEMORY
16480219Swpaul#define TXP_RID			TXP_PCI_LOMEM
16580219Swpaul#endif
16680219Swpaul
16780219Swpaulstatic device_method_t txp_methods[] = {
16880219Swpaul        /* Device interface */
16980219Swpaul	DEVMETHOD(device_probe,		txp_probe),
17080219Swpaul	DEVMETHOD(device_attach,	txp_attach),
17180219Swpaul	DEVMETHOD(device_detach,	txp_detach),
17280219Swpaul	DEVMETHOD(device_shutdown,	txp_shutdown),
17380219Swpaul	{ 0, 0 }
17480219Swpaul};
17580219Swpaul
17680219Swpaulstatic driver_t txp_driver = {
17780219Swpaul	"txp",
17880219Swpaul	txp_methods,
17980219Swpaul	sizeof(struct txp_softc)
18080219Swpaul};
18180219Swpaul
18280219Swpaulstatic devclass_t txp_devclass;
18380219Swpaul
184113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0);
185113506SmdoddMODULE_DEPEND(txp, pci, 1, 1, 1);
186113506SmdoddMODULE_DEPEND(txp, ether, 1, 1, 1);
18780219Swpaul
18880219Swpaulstatic int
189189685Syongaritxp_probe(device_t dev)
19080219Swpaul{
19180219Swpaul	struct txp_type *t;
19280219Swpaul
19380219Swpaul	t = txp_devs;
19480219Swpaul
19580219Swpaul	while(t->txp_name != NULL) {
19680219Swpaul		if ((pci_get_vendor(dev) == t->txp_vid) &&
19780219Swpaul		    (pci_get_device(dev) == t->txp_did)) {
19880219Swpaul			device_set_desc(dev, t->txp_name);
199142880Simp			return(BUS_PROBE_DEFAULT);
20080219Swpaul		}
20180219Swpaul		t++;
20280219Swpaul	}
20380219Swpaul
20480219Swpaul	return(ENXIO);
20580219Swpaul}
20680219Swpaul
20780219Swpaulstatic int
208189685Syongaritxp_attach(device_t dev)
20980219Swpaul{
21080219Swpaul	struct txp_softc *sc;
21180219Swpaul	struct ifnet *ifp;
21280219Swpaul	u_int16_t p1;
21380219Swpaul	u_int32_t p2;
214151772Sjhb	int error = 0, rid;
215147256Sbrooks	u_char eaddr[6];
21680219Swpaul
21780219Swpaul	sc = device_get_softc(dev);
21880219Swpaul	sc->sc_dev = dev;
21980219Swpaul	sc->sc_cold = 1;
22080219Swpaul
22193818Sjhb	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
222151772Sjhb	    MTX_DEF);
223151772Sjhb	callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0);
224151772Sjhb
22580219Swpaul	/*
22680219Swpaul	 * Map control/status registers.
22780219Swpaul	 */
22880219Swpaul	pci_enable_busmaster(dev);
22980219Swpaul
23080219Swpaul	rid = TXP_RID;
231127135Snjl	sc->sc_res = bus_alloc_resource_any(dev, TXP_RES, &rid,
232127135Snjl	    RF_ACTIVE);
23380219Swpaul
23480219Swpaul	if (sc->sc_res == NULL) {
23580219Swpaul		device_printf(dev, "couldn't map ports/memory\n");
23680219Swpaul		error = ENXIO;
23780219Swpaul		goto fail;
23880219Swpaul	}
23980219Swpaul
24080219Swpaul	sc->sc_bt = rman_get_bustag(sc->sc_res);
24180219Swpaul	sc->sc_bh = rman_get_bushandle(sc->sc_res);
24280219Swpaul
24380219Swpaul	/* Allocate interrupt */
24480219Swpaul	rid = 0;
245127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
24680219Swpaul	    RF_SHAREABLE | RF_ACTIVE);
24780219Swpaul
24880219Swpaul	if (sc->sc_irq == NULL) {
24980219Swpaul		device_printf(dev, "couldn't map interrupt\n");
25080219Swpaul		error = ENXIO;
25180219Swpaul		goto fail;
25280219Swpaul	}
25380219Swpaul
25480219Swpaul	if (txp_chip_init(sc)) {
255149678Sjhb		error = ENXIO;
25680219Swpaul		goto fail;
25780219Swpaul	}
25880219Swpaul
25980219Swpaul	sc->sc_fwbuf = contigmalloc(32768, M_DEVBUF,
26080219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
261170596Syongari	if (sc->sc_fwbuf == NULL) {
262170596Syongari		device_printf(dev, "no memory for firmware\n");
263170596Syongari		error = ENXIO;
264170596Syongari		goto fail;
265170596Syongari	}
26680219Swpaul	error = txp_download_fw(sc);
26780219Swpaul	contigfree(sc->sc_fwbuf, 32768, M_DEVBUF);
26880219Swpaul	sc->sc_fwbuf = NULL;
26980219Swpaul
270149678Sjhb	if (error)
27180219Swpaul		goto fail;
27280219Swpaul
27380219Swpaul	sc->sc_ldata = contigmalloc(sizeof(struct txp_ldata), M_DEVBUF,
27480219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
275170596Syongari	if (sc->sc_ldata == NULL) {
276170596Syongari		device_printf(dev, "no memory for descriptor ring\n");
277170596Syongari		error = ENXIO;
278170596Syongari		goto fail;
279170596Syongari	}
28080457Swpaul	bzero(sc->sc_ldata, sizeof(struct txp_ldata));
28180219Swpaul
28280219Swpaul	if (txp_alloc_rings(sc)) {
283149678Sjhb		error = ENXIO;
28480219Swpaul		goto fail;
28580219Swpaul	}
28680219Swpaul
28780219Swpaul	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
28880219Swpaul	    NULL, NULL, NULL, 1)) {
289149678Sjhb		error = ENXIO;
29080219Swpaul		goto fail;
29180219Swpaul	}
29280219Swpaul
29380219Swpaul	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
29480219Swpaul	    &p1, &p2, NULL, 1)) {
295149678Sjhb		error = ENXIO;
29680219Swpaul		goto fail;
29780219Swpaul	}
29880219Swpaul
299147256Sbrooks	eaddr[0] = ((u_int8_t *)&p1)[1];
300147256Sbrooks	eaddr[1] = ((u_int8_t *)&p1)[0];
301147256Sbrooks	eaddr[2] = ((u_int8_t *)&p2)[3];
302147256Sbrooks	eaddr[3] = ((u_int8_t *)&p2)[2];
303147256Sbrooks	eaddr[4] = ((u_int8_t *)&p2)[1];
304147256Sbrooks	eaddr[5] = ((u_int8_t *)&p2)[0];
30580219Swpaul
30680219Swpaul	sc->sc_cold = 0;
30780219Swpaul
30880219Swpaul	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
30980219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
31080219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
31180219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
31280219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
31380219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);
31480219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
31580219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
31680219Swpaul
31780219Swpaul	sc->sc_xcvr = TXP_XCVR_AUTO;
31880219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
31980219Swpaul	    NULL, NULL, NULL, 0);
32080219Swpaul	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO);
32180219Swpaul
322147256Sbrooks	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
323147256Sbrooks	if (ifp == NULL) {
324150184Sru		device_printf(dev, "can not if_alloc()\n");
325147256Sbrooks		error = ENOSPC;
326147256Sbrooks		goto fail;
327147256Sbrooks	}
32880219Swpaul	ifp->if_softc = sc;
329121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
33080219Swpaul	ifp->if_mtu = ETHERMTU;
331151772Sjhb	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
33280219Swpaul	ifp->if_ioctl = txp_ioctl;
33380219Swpaul	ifp->if_start = txp_start;
33480219Swpaul	ifp->if_watchdog = txp_watchdog;
33580219Swpaul	ifp->if_init = txp_init;
33680219Swpaul	ifp->if_baudrate = 100000000;
33780219Swpaul	ifp->if_snd.ifq_maxlen = TX_ENTRIES;
33880219Swpaul	ifp->if_hwassist = 0;
33980219Swpaul	txp_capabilities(sc);
34080219Swpaul
34180219Swpaul	/*
34280219Swpaul	 * Attach us everywhere
34380219Swpaul	 */
344147256Sbrooks	ether_ifattach(ifp, eaddr);
345149678Sjhb
346151772Sjhb	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
347166901Spiso	    NULL, txp_intr, sc, &sc->sc_intrhand);
348149678Sjhb
349149678Sjhb	if (error) {
350149678Sjhb		ether_ifdetach(ifp);
351149678Sjhb		device_printf(dev, "couldn't set up irq\n");
352149678Sjhb		goto fail;
353149678Sjhb	}
354149678Sjhb
35580219Swpaul	return(0);
35680219Swpaul
35780219Swpaulfail:
35880219Swpaul	txp_release_resources(sc);
35980219Swpaul	mtx_destroy(&sc->sc_mtx);
36080219Swpaul	return(error);
36180219Swpaul}
36280219Swpaul
36380219Swpaulstatic int
364189685Syongaritxp_detach(device_t dev)
36580219Swpaul{
36680219Swpaul	struct txp_softc *sc;
36780219Swpaul	struct ifnet *ifp;
36880219Swpaul	int i;
36980219Swpaul
37080219Swpaul	sc = device_get_softc(dev);
371147256Sbrooks	ifp = sc->sc_ifp;
37280219Swpaul
373151772Sjhb	TXP_LOCK(sc);
37480219Swpaul	txp_stop(sc);
375151772Sjhb	TXP_UNLOCK(sc);
37680219Swpaul	txp_shutdown(dev);
377151772Sjhb	callout_drain(&sc->sc_tick);
37880219Swpaul
37980219Swpaul	ifmedia_removeall(&sc->sc_ifmedia);
380106937Ssam	ether_ifdetach(ifp);
38180219Swpaul
38280219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++)
38380219Swpaul		free(sc->sc_rxbufs[i].rb_sd, M_DEVBUF);
38480219Swpaul
38580219Swpaul	txp_release_resources(sc);
38680219Swpaul
38780219Swpaul	mtx_destroy(&sc->sc_mtx);
38880219Swpaul	return(0);
38980219Swpaul}
39080219Swpaul
39180219Swpaulstatic void
392189685Syongaritxp_release_resources(struct txp_softc *sc)
39380219Swpaul{
39480219Swpaul	device_t dev;
39580219Swpaul
39680219Swpaul	dev = sc->sc_dev;
39780219Swpaul
39880219Swpaul	if (sc->sc_intrhand != NULL)
39980219Swpaul		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
40080219Swpaul
40180219Swpaul	if (sc->sc_irq != NULL)
40280219Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
40380219Swpaul
40480219Swpaul	if (sc->sc_res != NULL)
40580219Swpaul		bus_release_resource(dev, TXP_RES, TXP_RID, sc->sc_res);
40680219Swpaul
40780219Swpaul	if (sc->sc_ldata != NULL)
40880219Swpaul		contigfree(sc->sc_ldata, sizeof(struct txp_ldata), M_DEVBUF);
40980219Swpaul
410150306Simp	if (sc->sc_ifp)
411150306Simp		if_free(sc->sc_ifp);
41280219Swpaul}
41380219Swpaul
41480219Swpaulstatic int
415189685Syongaritxp_chip_init(struct txp_softc *sc)
41680219Swpaul{
41780219Swpaul	/* disable interrupts */
41880219Swpaul	WRITE_REG(sc, TXP_IER, 0);
41980219Swpaul	WRITE_REG(sc, TXP_IMR,
42080219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
42180219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
42280219Swpaul	    TXP_INT_LATCH);
42380219Swpaul
42480219Swpaul	/* ack all interrupts */
42580219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
42680219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
42780219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
42880219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
42980219Swpaul	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
43080219Swpaul
43180219Swpaul	if (txp_reset_adapter(sc))
43280219Swpaul		return (-1);
43380219Swpaul
43480219Swpaul	/* disable interrupts */
43580219Swpaul	WRITE_REG(sc, TXP_IER, 0);
43680219Swpaul	WRITE_REG(sc, TXP_IMR,
43780219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
43880219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
43980219Swpaul	    TXP_INT_LATCH);
44080219Swpaul
44180219Swpaul	/* ack all interrupts */
44280219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
44380219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
44480219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
44580219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
44680219Swpaul	    TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
44780219Swpaul
44880219Swpaul	return (0);
44980219Swpaul}
45080219Swpaul
45180219Swpaulstatic int
452189685Syongaritxp_reset_adapter(struct txp_softc *sc)
45380219Swpaul{
45480219Swpaul	u_int32_t r;
45580219Swpaul	int i;
45680219Swpaul
45792643Sjeff	r = 0;
45880219Swpaul	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
45980219Swpaul	DELAY(1000);
46080219Swpaul	WRITE_REG(sc, TXP_SRR, 0);
46180219Swpaul
46280219Swpaul	/* Should wait max 6 seconds */
46380219Swpaul	for (i = 0; i < 6000; i++) {
46480219Swpaul		r = READ_REG(sc, TXP_A2H_0);
46580219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
46680219Swpaul			break;
46780219Swpaul		DELAY(1000);
46880219Swpaul	}
46980219Swpaul
47080219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
47180219Swpaul		device_printf(sc->sc_dev, "reset hung\n");
47280219Swpaul		return (-1);
47380219Swpaul	}
47480219Swpaul
47580219Swpaul	return (0);
47680219Swpaul}
47780219Swpaul
47880219Swpaulstatic int
479189685Syongaritxp_download_fw(struct txp_softc *sc)
48080219Swpaul{
48180219Swpaul	struct txp_fw_file_header *fileheader;
48280219Swpaul	struct txp_fw_section_header *secthead;
483189022Syongari	int error, sect;
48480219Swpaul	u_int32_t r, i, ier, imr;
48580219Swpaul
48692643Sjeff	r = 0;
487189022Syongari	error = 0;
48880219Swpaul	ier = READ_REG(sc, TXP_IER);
48980219Swpaul	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
49080219Swpaul
49180219Swpaul	imr = READ_REG(sc, TXP_IMR);
49280219Swpaul	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
49380219Swpaul
49480219Swpaul	for (i = 0; i < 10000; i++) {
49580219Swpaul		r = READ_REG(sc, TXP_A2H_0);
49680219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
49780219Swpaul			break;
49880219Swpaul		DELAY(50);
49980219Swpaul	}
50080219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
50180219Swpaul		device_printf(sc->sc_dev, "not waiting for host request\n");
502189022Syongari		error = -1;
503189022Syongari		goto fail;
50480219Swpaul	}
50580219Swpaul
50680219Swpaul	/* Ack the status */
50780219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
50880219Swpaul
50980219Swpaul	fileheader = (struct txp_fw_file_header *)tc990image;
51080219Swpaul	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
51180219Swpaul		device_printf(sc->sc_dev, "fw invalid magic\n");
512189022Syongari		error = -1;
513189022Syongari		goto fail;
51480219Swpaul	}
51580219Swpaul
51680219Swpaul	/* Tell boot firmware to get ready for image */
51780219Swpaul	WRITE_REG(sc, TXP_H2A_1, fileheader->addr);
518189022Syongari	WRITE_REG(sc, TXP_H2A_2, fileheader->hmac[0]);
519189022Syongari	WRITE_REG(sc, TXP_H2A_3, fileheader->hmac[1]);
520189022Syongari	WRITE_REG(sc, TXP_H2A_4, fileheader->hmac[2]);
521189022Syongari	WRITE_REG(sc, TXP_H2A_5, fileheader->hmac[3]);
522189022Syongari	WRITE_REG(sc, TXP_H2A_6, fileheader->hmac[4]);
52380219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
52480219Swpaul
52580219Swpaul	if (txp_download_fw_wait(sc)) {
52680219Swpaul		device_printf(sc->sc_dev, "fw wait failed, initial\n");
527189022Syongari		error = -1;
528189022Syongari		goto fail;
52980219Swpaul	}
53080219Swpaul
53180219Swpaul	secthead = (struct txp_fw_section_header *)(((u_int8_t *)tc990image) +
53280219Swpaul	    sizeof(struct txp_fw_file_header));
53380219Swpaul
53480219Swpaul	for (sect = 0; sect < fileheader->nsections; sect++) {
535189686Syongari
536189022Syongari		if (txp_download_fw_section(sc, secthead, sect)) {
537189022Syongari			error = -1;
538189022Syongari			goto fail;
539189022Syongari		}
54080219Swpaul		secthead = (struct txp_fw_section_header *)
54180219Swpaul		    (((u_int8_t *)secthead) + secthead->nbytes +
54280219Swpaul		    sizeof(*secthead));
54380219Swpaul	}
54480219Swpaul
54580219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
54680219Swpaul
54780219Swpaul	for (i = 0; i < 10000; i++) {
54880219Swpaul		r = READ_REG(sc, TXP_A2H_0);
54980219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
55080219Swpaul			break;
55180219Swpaul		DELAY(50);
55280219Swpaul	}
55380219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
55480219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
555189022Syongari		error = -1;
556189022Syongari		goto fail;
55780219Swpaul	}
55880219Swpaul
559189022Syongarifail:
56080219Swpaul	WRITE_REG(sc, TXP_IER, ier);
56180219Swpaul	WRITE_REG(sc, TXP_IMR, imr);
56280219Swpaul
563189022Syongari	return (error);
56480219Swpaul}
56580219Swpaul
56680219Swpaulstatic int
567189685Syongaritxp_download_fw_wait(struct txp_softc *sc)
56880219Swpaul{
56980219Swpaul	u_int32_t i, r;
57080219Swpaul
57192643Sjeff	r = 0;
57280219Swpaul	for (i = 0; i < 10000; i++) {
57380219Swpaul		r = READ_REG(sc, TXP_ISR);
57480219Swpaul		if (r & TXP_INT_A2H_0)
57580219Swpaul			break;
57680219Swpaul		DELAY(50);
57780219Swpaul	}
57880219Swpaul
57980219Swpaul	if (!(r & TXP_INT_A2H_0)) {
58080219Swpaul		device_printf(sc->sc_dev, "fw wait failed comm0\n");
58180219Swpaul		return (-1);
58280219Swpaul	}
58380219Swpaul
58480219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
58580219Swpaul
58680219Swpaul	r = READ_REG(sc, TXP_A2H_0);
58780219Swpaul	if (r != STAT_WAITING_FOR_SEGMENT) {
58880219Swpaul		device_printf(sc->sc_dev, "fw not waiting for segment\n");
58980219Swpaul		return (-1);
59080219Swpaul	}
59180219Swpaul	return (0);
59280219Swpaul}
59380219Swpaul
59480219Swpaulstatic int
595189685Syongaritxp_download_fw_section(struct txp_softc *sc,
596189685Syongari    struct txp_fw_section_header *sect, int sectnum)
59780219Swpaul{
59880219Swpaul	vm_offset_t dma;
59980219Swpaul	int rseg, err = 0;
60080219Swpaul	struct mbuf m;
60180219Swpaul	u_int16_t csum;
60280219Swpaul
60380219Swpaul	/* Skip zero length sections */
60480219Swpaul	if (sect->nbytes == 0)
60580219Swpaul		return (0);
60680219Swpaul
60780219Swpaul	/* Make sure we aren't past the end of the image */
60880219Swpaul	rseg = ((u_int8_t *)sect) - ((u_int8_t *)tc990image);
60980219Swpaul	if (rseg >= sizeof(tc990image)) {
61080219Swpaul		device_printf(sc->sc_dev, "fw invalid section address, "
61180219Swpaul		    "section %d\n", sectnum);
61280219Swpaul		return (-1);
61380219Swpaul	}
61480219Swpaul
61580219Swpaul	/* Make sure this section doesn't go past the end */
61680219Swpaul	rseg += sect->nbytes;
61780219Swpaul	if (rseg >= sizeof(tc990image)) {
61880219Swpaul		device_printf(sc->sc_dev, "fw truncated section %d\n",
61980219Swpaul		    sectnum);
62080219Swpaul		return (-1);
62180219Swpaul	}
62280219Swpaul
62380219Swpaul	bcopy(((u_int8_t *)sect) + sizeof(*sect), sc->sc_fwbuf, sect->nbytes);
62480219Swpaul	dma = vtophys(sc->sc_fwbuf);
62580219Swpaul
62680219Swpaul	/*
62780219Swpaul	 * dummy up mbuf and verify section checksum
62880219Swpaul	 */
62980219Swpaul	m.m_type = MT_DATA;
63080219Swpaul	m.m_next = m.m_nextpkt = NULL;
63180219Swpaul	m.m_len = sect->nbytes;
63280219Swpaul	m.m_data = sc->sc_fwbuf;
63380219Swpaul	m.m_flags = 0;
63480219Swpaul	csum = in_cksum(&m, sect->nbytes);
63580219Swpaul	if (csum != sect->cksum) {
63680219Swpaul		device_printf(sc->sc_dev, "fw section %d, bad "
63780219Swpaul		    "cksum (expected 0x%x got 0x%x)\n",
63880219Swpaul		    sectnum, sect->cksum, csum);
63980219Swpaul		err = -1;
64080219Swpaul		goto bail;
64180219Swpaul	}
64280219Swpaul
64380219Swpaul	WRITE_REG(sc, TXP_H2A_1, sect->nbytes);
64480219Swpaul	WRITE_REG(sc, TXP_H2A_2, sect->cksum);
64580219Swpaul	WRITE_REG(sc, TXP_H2A_3, sect->addr);
64680219Swpaul	WRITE_REG(sc, TXP_H2A_4, 0);
64780219Swpaul	WRITE_REG(sc, TXP_H2A_5, dma & 0xffffffff);
64880219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
64980219Swpaul
65080219Swpaul	if (txp_download_fw_wait(sc)) {
65180219Swpaul		device_printf(sc->sc_dev, "fw wait failed, "
65280219Swpaul		    "section %d\n", sectnum);
65380219Swpaul		err = -1;
65480219Swpaul	}
65580219Swpaul
65680219Swpaulbail:
65780219Swpaul	return (err);
65880219Swpaul}
65980219Swpaul
660189686Syongaristatic void
661189685Syongaritxp_intr(void *vsc)
66280219Swpaul{
66380219Swpaul	struct txp_softc *sc = vsc;
66480219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
66580219Swpaul	u_int32_t isr;
66680219Swpaul
66780219Swpaul	/* mask all interrupts */
668151772Sjhb	TXP_LOCK(sc);
66980219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_RESERVED | TXP_INT_SELF |
67080219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
67180219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
67280219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
67380219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
67480219Swpaul
67580219Swpaul	isr = READ_REG(sc, TXP_ISR);
67680219Swpaul	while (isr) {
67780219Swpaul		WRITE_REG(sc, TXP_ISR, isr);
67880219Swpaul
67980219Swpaul		if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
68080219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxhir);
68180219Swpaul		if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
68280219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxlor);
68380219Swpaul
68480219Swpaul		if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
68580219Swpaul			txp_rxbuf_reclaim(sc);
68680219Swpaul
68780219Swpaul		if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
68880219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txhir.r_off))))
68980219Swpaul			txp_tx_reclaim(sc, &sc->sc_txhir);
69080219Swpaul
69180219Swpaul		if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
69280219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txlor.r_off))))
69380219Swpaul			txp_tx_reclaim(sc, &sc->sc_txlor);
69480219Swpaul
69580219Swpaul		isr = READ_REG(sc, TXP_ISR);
69680219Swpaul	}
69780219Swpaul
69880219Swpaul	/* unmask all interrupts */
69980219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
70080219Swpaul
701151772Sjhb	txp_start_locked(sc->sc_ifp);
702151772Sjhb	TXP_UNLOCK(sc);
70380219Swpaul}
70480219Swpaul
70580219Swpaulstatic void
706189685Syongaritxp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r)
70780219Swpaul{
708147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
70980219Swpaul	struct txp_rx_desc *rxd;
71080219Swpaul	struct mbuf *m;
71180219Swpaul	struct txp_swdesc *sd = NULL;
71280219Swpaul	u_int32_t roff, woff;
71380219Swpaul
714151772Sjhb	TXP_LOCK_ASSERT(sc);
71580219Swpaul	roff = *r->r_roff;
71680219Swpaul	woff = *r->r_woff;
71780219Swpaul	rxd = r->r_desc + (roff / sizeof(struct txp_rx_desc));
71880219Swpaul
71980219Swpaul	while (roff != woff) {
72080219Swpaul
72180219Swpaul		if (rxd->rx_flags & RX_FLAGS_ERROR) {
72280219Swpaul			device_printf(sc->sc_dev, "error 0x%x\n",
72380219Swpaul			    rxd->rx_stat);
72480219Swpaul			ifp->if_ierrors++;
72580219Swpaul			goto next;
72680219Swpaul		}
72780219Swpaul
72880219Swpaul		/* retrieve stashed pointer */
72980219Swpaul		sd = rxd->rx_sd;
73080219Swpaul
73180219Swpaul		m = sd->sd_mbuf;
73280219Swpaul		sd->sd_mbuf = NULL;
73380219Swpaul
73480219Swpaul		m->m_pkthdr.len = m->m_len = rxd->rx_len;
73580219Swpaul
73680219Swpaul#ifdef __STRICT_ALIGNMENT
73780219Swpaul		{
73880219Swpaul			/*
73980219Swpaul			 * XXX Nice chip, except it won't accept "off by 2"
74080219Swpaul			 * buffers, so we're force to copy.  Supposedly
74180219Swpaul			 * this will be fixed in a newer firmware rev
74280219Swpaul			 * and this will be temporary.
74380219Swpaul			 */
74480219Swpaul			struct mbuf *mnew;
74580219Swpaul
746151772Sjhb			mnew = m_devget(mtod(m, caddr_t), rxd->rx_len,
747151772Sjhb			    ETHER_ALIGN, ifp, NULL);
748151772Sjhb			m_freem(m);
74980219Swpaul			if (mnew == NULL) {
750151772Sjhb				ifp->if_ierrors++;
75180219Swpaul				goto next;
75280219Swpaul			}
75380219Swpaul			m = mnew;
75480219Swpaul		}
75580219Swpaul#endif
75680219Swpaul
75780219Swpaul		if (rxd->rx_stat & RX_STAT_IPCKSUMBAD)
75880219Swpaul			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
75980219Swpaul		else if (rxd->rx_stat & RX_STAT_IPCKSUMGOOD)
76080219Swpaul		 	m->m_pkthdr.csum_flags |=
76180219Swpaul			    CSUM_IP_CHECKED|CSUM_IP_VALID;
76280219Swpaul
76380219Swpaul		if ((rxd->rx_stat & RX_STAT_TCPCKSUMGOOD) ||
76480219Swpaul		    (rxd->rx_stat & RX_STAT_UDPCKSUMGOOD)) {
76580219Swpaul			m->m_pkthdr.csum_flags |=
76680219Swpaul			    CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
76780219Swpaul			m->m_pkthdr.csum_data = 0xffff;
76880219Swpaul		}
76980219Swpaul
77080219Swpaul		if (rxd->rx_stat & RX_STAT_VLAN) {
771162375Sandre			m->m_pkthdr.ether_vtag = htons(rxd->rx_vlan >> 16);
772162375Sandre			m->m_flags |= M_VLANTAG;
77380219Swpaul		}
77483115Sbrooks
775151772Sjhb		TXP_UNLOCK(sc);
776106937Ssam		(*ifp->if_input)(ifp, m);
777151772Sjhb		TXP_LOCK(sc);
77880219Swpaul
77980219Swpaulnext:
78080219Swpaul
78180219Swpaul		roff += sizeof(struct txp_rx_desc);
78280219Swpaul		if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
78380219Swpaul			roff = 0;
78480219Swpaul			rxd = r->r_desc;
78580219Swpaul		} else
78680219Swpaul			rxd++;
78780219Swpaul		woff = *r->r_woff;
78880219Swpaul	}
78980219Swpaul
79080219Swpaul	*r->r_roff = woff;
79180219Swpaul}
79280219Swpaul
79380219Swpaulstatic void
794189685Syongaritxp_rxbuf_reclaim(struct txp_softc *sc)
79580219Swpaul{
796147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
79780219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
79880219Swpaul	struct txp_rxbuf_desc *rbd;
79980219Swpaul	struct txp_swdesc *sd;
80080219Swpaul	u_int32_t i;
80180219Swpaul
802151772Sjhb	TXP_LOCK_ASSERT(sc);
803148887Srwatson	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
80480219Swpaul		return;
80580219Swpaul
80680219Swpaul	i = sc->sc_rxbufprod;
80780219Swpaul	rbd = sc->sc_rxbufs + i;
80880219Swpaul
80980219Swpaul	while (1) {
81080219Swpaul		sd = rbd->rb_sd;
81180219Swpaul		if (sd->sd_mbuf != NULL)
81280219Swpaul			break;
81380219Swpaul
814151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
81580219Swpaul		if (sd->sd_mbuf == NULL)
816151772Sjhb			return;
81780219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
81880219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
81980219Swpaul
82080219Swpaul		rbd->rb_paddrlo = vtophys(mtod(sd->sd_mbuf, vm_offset_t))
82180219Swpaul		    & 0xffffffff;
82280219Swpaul		rbd->rb_paddrhi = 0;
82380219Swpaul
82480219Swpaul		hv->hv_rx_buf_write_idx = TXP_IDX2OFFSET(i);
82580219Swpaul
82680219Swpaul		if (++i == RXBUF_ENTRIES) {
82780219Swpaul			i = 0;
82880219Swpaul			rbd = sc->sc_rxbufs;
82980219Swpaul		} else
83080219Swpaul			rbd++;
83180219Swpaul	}
83280219Swpaul
83380219Swpaul	sc->sc_rxbufprod = i;
83480219Swpaul}
83580219Swpaul
83680219Swpaul/*
83780219Swpaul * Reclaim mbufs and entries from a transmit ring.
83880219Swpaul */
83980219Swpaulstatic void
840189685Syongaritxp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r)
84180219Swpaul{
842147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
84380219Swpaul	u_int32_t idx = TXP_OFFSET2IDX(*(r->r_off));
84480219Swpaul	u_int32_t cons = r->r_cons, cnt = r->r_cnt;
84580219Swpaul	struct txp_tx_desc *txd = r->r_desc + cons;
84680219Swpaul	struct txp_swdesc *sd = sc->sc_txd + cons;
84780219Swpaul	struct mbuf *m;
84880219Swpaul
849151772Sjhb	TXP_LOCK_ASSERT(sc);
85080219Swpaul	while (cons != idx) {
85180219Swpaul		if (cnt == 0)
85280219Swpaul			break;
85380219Swpaul
85480219Swpaul		if ((txd->tx_flags & TX_FLAGS_TYPE_M) ==
85580219Swpaul		    TX_FLAGS_TYPE_DATA) {
85680219Swpaul			m = sd->sd_mbuf;
85780219Swpaul			if (m != NULL) {
85880219Swpaul				m_freem(m);
85980219Swpaul				txd->tx_addrlo = 0;
86080219Swpaul				txd->tx_addrhi = 0;
86180219Swpaul				ifp->if_opackets++;
86280219Swpaul			}
86380219Swpaul		}
864148887Srwatson		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
86580219Swpaul
86680219Swpaul		if (++cons == TX_ENTRIES) {
86780219Swpaul			txd = r->r_desc;
86880219Swpaul			cons = 0;
86980219Swpaul			sd = sc->sc_txd;
87080219Swpaul		} else {
87180219Swpaul			txd++;
87280219Swpaul			sd++;
87380219Swpaul		}
87480219Swpaul
87580219Swpaul		cnt--;
87680219Swpaul	}
87780219Swpaul
87880219Swpaul	r->r_cons = cons;
87980219Swpaul	r->r_cnt = cnt;
88080219Swpaul	if (cnt == 0)
88180219Swpaul		ifp->if_timer = 0;
88280219Swpaul}
88380219Swpaul
88480219Swpaulstatic int
885189685Syongaritxp_shutdown(device_t dev)
88680219Swpaul{
88780219Swpaul	struct txp_softc *sc;
88880219Swpaul
88980219Swpaul	sc = device_get_softc(dev);
89080219Swpaul
891151772Sjhb	TXP_LOCK(sc);
892151772Sjhb
89380219Swpaul	/* mask all interrupts */
89480219Swpaul	WRITE_REG(sc, TXP_IMR,
89580219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
89680219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
89780219Swpaul	    TXP_INT_LATCH);
89880219Swpaul
89980219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
90080219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
90180219Swpaul	txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 0);
902151772Sjhb	TXP_UNLOCK(sc);
90380219Swpaul
90480219Swpaul	return(0);
90580219Swpaul}
90680219Swpaul
90780219Swpaulstatic int
908189685Syongaritxp_alloc_rings(struct txp_softc *sc)
90980219Swpaul{
91080219Swpaul	struct txp_boot_record *boot;
91180219Swpaul	struct txp_ldata *ld;
91280219Swpaul	u_int32_t r;
91380219Swpaul	int i;
91480219Swpaul
91592643Sjeff	r = 0;
91680219Swpaul	ld = sc->sc_ldata;
91780219Swpaul	boot = &ld->txp_boot;
91880219Swpaul
91980219Swpaul	/* boot record */
92080219Swpaul	sc->sc_boot = boot;
92180219Swpaul
92280219Swpaul	/* host variables */
92380219Swpaul	bzero(&ld->txp_hostvar, sizeof(struct txp_hostvar));
92480219Swpaul	boot->br_hostvar_lo = vtophys(&ld->txp_hostvar);
92580219Swpaul	boot->br_hostvar_hi = 0;
92680219Swpaul	sc->sc_hostvar = (struct txp_hostvar *)&ld->txp_hostvar;
92780219Swpaul
92880219Swpaul	/* hi priority tx ring */
92980219Swpaul	boot->br_txhipri_lo = vtophys(&ld->txp_txhiring);;
93080219Swpaul	boot->br_txhipri_hi = 0;
93180219Swpaul	boot->br_txhipri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
93280219Swpaul	sc->sc_txhir.r_reg = TXP_H2A_1;
93380219Swpaul	sc->sc_txhir.r_desc = (struct txp_tx_desc *)&ld->txp_txhiring;
93480219Swpaul	sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
93580219Swpaul	sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
93680219Swpaul
93780219Swpaul	/* lo priority tx ring */
93880219Swpaul	boot->br_txlopri_lo = vtophys(&ld->txp_txloring);
93980219Swpaul	boot->br_txlopri_hi = 0;
94080219Swpaul	boot->br_txlopri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
94180219Swpaul	sc->sc_txlor.r_reg = TXP_H2A_3;
94280219Swpaul	sc->sc_txlor.r_desc = (struct txp_tx_desc *)&ld->txp_txloring;
94380219Swpaul	sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
94480219Swpaul	sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
94580219Swpaul
94680219Swpaul	/* high priority rx ring */
94780219Swpaul	boot->br_rxhipri_lo = vtophys(&ld->txp_rxhiring);
94880219Swpaul	boot->br_rxhipri_hi = 0;
94980219Swpaul	boot->br_rxhipri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
95080219Swpaul	sc->sc_rxhir.r_desc = (struct txp_rx_desc *)&ld->txp_rxhiring;
95180219Swpaul	sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
95280219Swpaul	sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
95380219Swpaul
95480219Swpaul	/* low priority rx ring */
95580219Swpaul	boot->br_rxlopri_lo = vtophys(&ld->txp_rxloring);
95680219Swpaul	boot->br_rxlopri_hi = 0;
95780219Swpaul	boot->br_rxlopri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
95880219Swpaul	sc->sc_rxlor.r_desc = (struct txp_rx_desc *)&ld->txp_rxloring;
95980219Swpaul	sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
96080219Swpaul	sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
96180219Swpaul
96280219Swpaul	/* command ring */
96380219Swpaul	bzero(&ld->txp_cmdring, sizeof(struct txp_cmd_desc) * CMD_ENTRIES);
96480219Swpaul	boot->br_cmd_lo = vtophys(&ld->txp_cmdring);
96580219Swpaul	boot->br_cmd_hi = 0;
96680219Swpaul	boot->br_cmd_siz = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
96780219Swpaul	sc->sc_cmdring.base = (struct txp_cmd_desc *)&ld->txp_cmdring;
96880219Swpaul	sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
96980219Swpaul	sc->sc_cmdring.lastwrite = 0;
97080219Swpaul
97180219Swpaul	/* response ring */
97280219Swpaul	bzero(&ld->txp_rspring, sizeof(struct txp_rsp_desc) * RSP_ENTRIES);
97380219Swpaul	boot->br_resp_lo = vtophys(&ld->txp_rspring);
97480219Swpaul	boot->br_resp_hi = 0;
97580219Swpaul	boot->br_resp_siz = CMD_ENTRIES * sizeof(struct txp_rsp_desc);
97680219Swpaul	sc->sc_rspring.base = (struct txp_rsp_desc *)&ld->txp_rspring;
97780219Swpaul	sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
97880219Swpaul	sc->sc_rspring.lastwrite = 0;
97980219Swpaul
98080219Swpaul	/* receive buffer ring */
98180219Swpaul	boot->br_rxbuf_lo = vtophys(&ld->txp_rxbufs);
98280219Swpaul	boot->br_rxbuf_hi = 0;
98380219Swpaul	boot->br_rxbuf_siz = RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc);
98480219Swpaul	sc->sc_rxbufs = (struct txp_rxbuf_desc *)&ld->txp_rxbufs;
98580219Swpaul
98680219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
98780457Swpaul		struct txp_swdesc *sd;
98880219Swpaul		if (sc->sc_rxbufs[i].rb_sd != NULL)
98980219Swpaul			continue;
99080219Swpaul		sc->sc_rxbufs[i].rb_sd = malloc(sizeof(struct txp_swdesc),
99180219Swpaul		    M_DEVBUF, M_NOWAIT);
99280219Swpaul		if (sc->sc_rxbufs[i].rb_sd == NULL)
99380219Swpaul			return(ENOBUFS);
99480457Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
99580457Swpaul		sd->sd_mbuf = NULL;
99680219Swpaul	}
99780219Swpaul	sc->sc_rxbufprod = 0;
99880219Swpaul
99980219Swpaul	/* zero dma */
100080219Swpaul	bzero(&ld->txp_zero, sizeof(u_int32_t));
100180219Swpaul	boot->br_zero_lo = vtophys(&ld->txp_zero);
100280219Swpaul	boot->br_zero_hi = 0;
100380219Swpaul
100480219Swpaul	/* See if it's waiting for boot, and try to boot it */
100580219Swpaul	for (i = 0; i < 10000; i++) {
100680219Swpaul		r = READ_REG(sc, TXP_A2H_0);
100780219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
100880219Swpaul			break;
100980219Swpaul		DELAY(50);
101080219Swpaul	}
101180219Swpaul
101280219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
101380219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
101480455Swpaul		return(ENXIO);
101580219Swpaul	}
101680219Swpaul
101780219Swpaul	WRITE_REG(sc, TXP_H2A_2, 0);
101880219Swpaul	WRITE_REG(sc, TXP_H2A_1, vtophys(sc->sc_boot));
101980219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
102080219Swpaul
102180219Swpaul	/* See if it booted */
102280219Swpaul	for (i = 0; i < 10000; i++) {
102380219Swpaul		r = READ_REG(sc, TXP_A2H_0);
102480219Swpaul		if (r == STAT_RUNNING)
102580219Swpaul			break;
102680219Swpaul		DELAY(50);
102780219Swpaul	}
102880219Swpaul	if (r != STAT_RUNNING) {
102980219Swpaul		device_printf(sc->sc_dev, "fw not running\n");
103080219Swpaul		return(ENXIO);
103180219Swpaul	}
103280219Swpaul
103380219Swpaul	/* Clear TX and CMD ring write registers */
103480219Swpaul	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
103580219Swpaul	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
103680219Swpaul	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
103780219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
103880219Swpaul
103980219Swpaul	return (0);
104080219Swpaul}
104180219Swpaul
104280219Swpaulstatic int
1043189685Syongaritxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
104480219Swpaul{
104580219Swpaul	struct txp_softc *sc = ifp->if_softc;
104680219Swpaul	struct ifreq *ifr = (struct ifreq *)data;
1047151772Sjhb	int error = 0;
104880219Swpaul
104980219Swpaul	switch(command) {
105080219Swpaul	case SIOCSIFFLAGS:
1051151772Sjhb		TXP_LOCK(sc);
105280219Swpaul		if (ifp->if_flags & IFF_UP) {
1053151772Sjhb			txp_init_locked(sc);
105480219Swpaul		} else {
1055148887Srwatson			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
105680219Swpaul				txp_stop(sc);
105780219Swpaul		}
1058151772Sjhb		TXP_UNLOCK(sc);
105980219Swpaul		break;
106080219Swpaul	case SIOCADDMULTI:
106180219Swpaul	case SIOCDELMULTI:
106280219Swpaul		/*
106380219Swpaul		 * Multicast list has changed; set the hardware
106480219Swpaul		 * filter accordingly.
106580219Swpaul		 */
1066151772Sjhb		TXP_LOCK(sc);
106780219Swpaul		txp_set_filter(sc);
1068151772Sjhb		TXP_UNLOCK(sc);
106980219Swpaul		error = 0;
107080219Swpaul		break;
107180219Swpaul	case SIOCGIFMEDIA:
107280219Swpaul	case SIOCSIFMEDIA:
107380219Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
107480219Swpaul		break;
107580219Swpaul	default:
1076106937Ssam		error = ether_ioctl(ifp, command, data);
107780219Swpaul		break;
107880219Swpaul	}
107980219Swpaul
108080219Swpaul	return(error);
108180219Swpaul}
108280219Swpaul
108380219Swpaulstatic int
1084189685Syongaritxp_rxring_fill(struct txp_softc *sc)
108580219Swpaul{
108680219Swpaul	int i;
108780219Swpaul	struct ifnet *ifp;
108880219Swpaul	struct txp_swdesc *sd;
108980219Swpaul
1090151772Sjhb	TXP_LOCK_ASSERT(sc);
1091147256Sbrooks	ifp = sc->sc_ifp;
109280219Swpaul
109380219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
109480219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
1095151772Sjhb		sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
109680219Swpaul		if (sd->sd_mbuf == NULL)
109780219Swpaul			return(ENOBUFS);
109880219Swpaul
109980219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
110080219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
110180219Swpaul
110280219Swpaul		sc->sc_rxbufs[i].rb_paddrlo =
110380219Swpaul		    vtophys(mtod(sd->sd_mbuf, vm_offset_t));
110480219Swpaul		sc->sc_rxbufs[i].rb_paddrhi = 0;
110580219Swpaul	}
110680219Swpaul
110780219Swpaul	sc->sc_hostvar->hv_rx_buf_write_idx = (RXBUF_ENTRIES - 1) *
110880219Swpaul	    sizeof(struct txp_rxbuf_desc);
110980219Swpaul
111080219Swpaul	return(0);
111180219Swpaul}
111280219Swpaul
111380219Swpaulstatic void
1114189685Syongaritxp_rxring_empty(struct txp_softc *sc)
111580219Swpaul{
111680219Swpaul	int i;
111780219Swpaul	struct txp_swdesc *sd;
111880219Swpaul
1119151772Sjhb	TXP_LOCK_ASSERT(sc);
112080219Swpaul	if (sc->sc_rxbufs == NULL)
112180219Swpaul		return;
112280219Swpaul
112380219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
112480219Swpaul		if (&sc->sc_rxbufs[i] == NULL)
112580219Swpaul			continue;
112680219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
112780219Swpaul		if (sd == NULL)
112880219Swpaul			continue;
112980219Swpaul		if (sd->sd_mbuf != NULL) {
113080219Swpaul			m_freem(sd->sd_mbuf);
113180219Swpaul			sd->sd_mbuf = NULL;
113280219Swpaul		}
113380219Swpaul	}
113480219Swpaul}
113580219Swpaul
113680219Swpaulstatic void
1137189685Syongaritxp_init(void *xsc)
113880219Swpaul{
113980219Swpaul	struct txp_softc *sc;
1140151772Sjhb
1141151772Sjhb	sc = xsc;
1142151772Sjhb	TXP_LOCK(sc);
1143151772Sjhb	txp_init_locked(sc);
1144151772Sjhb	TXP_UNLOCK(sc);
1145151772Sjhb}
1146151772Sjhb
1147151772Sjhbstatic void
1148189685Syongaritxp_init_locked(struct txp_softc *sc)
1149151772Sjhb{
115080219Swpaul	struct ifnet *ifp;
115180219Swpaul	u_int16_t p1;
115280219Swpaul	u_int32_t p2;
115380219Swpaul
1154151772Sjhb	TXP_LOCK_ASSERT(sc);
1155147256Sbrooks	ifp = sc->sc_ifp;
115680219Swpaul
1157148887Srwatson	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
115880219Swpaul		return;
115980219Swpaul
116080219Swpaul	txp_stop(sc);
116180219Swpaul
116280219Swpaul	txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
116380219Swpaul	    NULL, NULL, NULL, 1);
116480219Swpaul
116580219Swpaul	/* Set station address. */
1166152315Sru	((u_int8_t *)&p1)[1] = IF_LLADDR(sc->sc_ifp)[0];
1167152315Sru	((u_int8_t *)&p1)[0] = IF_LLADDR(sc->sc_ifp)[1];
1168152315Sru	((u_int8_t *)&p2)[3] = IF_LLADDR(sc->sc_ifp)[2];
1169152315Sru	((u_int8_t *)&p2)[2] = IF_LLADDR(sc->sc_ifp)[3];
1170152315Sru	((u_int8_t *)&p2)[1] = IF_LLADDR(sc->sc_ifp)[4];
1171152315Sru	((u_int8_t *)&p2)[0] = IF_LLADDR(sc->sc_ifp)[5];
117280219Swpaul	txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0,
117380219Swpaul	    NULL, NULL, NULL, 1);
117480219Swpaul
117580219Swpaul	txp_set_filter(sc);
117680219Swpaul
117780219Swpaul	txp_rxring_fill(sc);
117880219Swpaul
117980219Swpaul	txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
118080219Swpaul	txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
118180219Swpaul
118280219Swpaul	WRITE_REG(sc, TXP_IER, TXP_INT_RESERVED | TXP_INT_SELF |
118380219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
118480219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
118580219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
118680219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
118780219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
118880219Swpaul
1189148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1190148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
119180219Swpaul	ifp->if_timer = 0;
119280219Swpaul
1193151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
119480219Swpaul}
119580219Swpaul
119680219Swpaulstatic void
1197189685Syongaritxp_tick(void *vsc)
119880219Swpaul{
119980219Swpaul	struct txp_softc *sc = vsc;
1200147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
120180219Swpaul	struct txp_rsp_desc *rsp = NULL;
120280219Swpaul	struct txp_ext_desc *ext;
120380219Swpaul
1204151772Sjhb	TXP_LOCK_ASSERT(sc);
120580219Swpaul	txp_rxbuf_reclaim(sc);
120680219Swpaul
120780219Swpaul	if (txp_command2(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
120880219Swpaul	    &rsp, 1))
120980219Swpaul		goto out;
121080219Swpaul	if (rsp->rsp_numdesc != 6)
121180219Swpaul		goto out;
121280219Swpaul	if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
121380219Swpaul	    NULL, NULL, NULL, 1))
121480219Swpaul		goto out;
121580219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
121680219Swpaul
121780219Swpaul	ifp->if_ierrors += ext[3].ext_2 + ext[3].ext_3 + ext[3].ext_4 +
121880219Swpaul	    ext[4].ext_1 + ext[4].ext_4;
121980219Swpaul	ifp->if_oerrors += ext[0].ext_1 + ext[1].ext_1 + ext[1].ext_4 +
122080219Swpaul	    ext[2].ext_1;
122180219Swpaul	ifp->if_collisions += ext[0].ext_2 + ext[0].ext_3 + ext[1].ext_2 +
122280219Swpaul	    ext[1].ext_3;
122380219Swpaul	ifp->if_opackets += rsp->rsp_par2;
122480219Swpaul	ifp->if_ipackets += ext[2].ext_3;
122580219Swpaul
122680219Swpaulout:
122780219Swpaul	if (rsp != NULL)
122880219Swpaul		free(rsp, M_DEVBUF);
122980219Swpaul
1230151772Sjhb	callout_reset(&sc->sc_tick, hz, txp_tick, sc);
123180219Swpaul}
123280219Swpaul
123380219Swpaulstatic void
1234189685Syongaritxp_start(struct ifnet *ifp)
123580219Swpaul{
1236151772Sjhb	struct txp_softc *sc;
1237151772Sjhb
1238151772Sjhb	sc = ifp->if_softc;
1239151772Sjhb	TXP_LOCK(sc);
1240151772Sjhb	txp_start_locked(ifp);
1241151772Sjhb	TXP_UNLOCK(sc);
1242151772Sjhb}
1243151772Sjhb
1244151772Sjhbstatic void
1245189685Syongaritxp_start_locked(struct ifnet *ifp)
1246151772Sjhb{
124780219Swpaul	struct txp_softc *sc = ifp->if_softc;
124880219Swpaul	struct txp_tx_ring *r = &sc->sc_txhir;
124980219Swpaul	struct txp_tx_desc *txd;
125080219Swpaul	struct txp_frag_desc *fxd;
125180219Swpaul	struct mbuf *m, *m0;
125280219Swpaul	struct txp_swdesc *sd;
125380219Swpaul	u_int32_t firstprod, firstcnt, prod, cnt;
125480219Swpaul
1255151772Sjhb	TXP_LOCK_ASSERT(sc);
1256148887Srwatson	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
1257148887Srwatson	   IFF_DRV_RUNNING)
125880219Swpaul		return;
125980219Swpaul
126080219Swpaul	prod = r->r_prod;
126180219Swpaul	cnt = r->r_cnt;
126280219Swpaul
126380219Swpaul	while (1) {
126480219Swpaul		IF_DEQUEUE(&ifp->if_snd, m);
126580219Swpaul		if (m == NULL)
126680219Swpaul			break;
126780219Swpaul
126880219Swpaul		firstprod = prod;
126980219Swpaul		firstcnt = cnt;
127080219Swpaul
127180219Swpaul		sd = sc->sc_txd + prod;
127280219Swpaul		sd->sd_mbuf = m;
127380219Swpaul
127480219Swpaul		if ((TX_ENTRIES - cnt) < 4)
127580219Swpaul			goto oactive;
127680219Swpaul
127780219Swpaul		txd = r->r_desc + prod;
127880219Swpaul
127980219Swpaul		txd->tx_flags = TX_FLAGS_TYPE_DATA;
128080219Swpaul		txd->tx_numdesc = 0;
128180219Swpaul		txd->tx_addrlo = 0;
128280219Swpaul		txd->tx_addrhi = 0;
128380219Swpaul		txd->tx_totlen = 0;
128480219Swpaul		txd->tx_pflags = 0;
128580219Swpaul
128680219Swpaul		if (++prod == TX_ENTRIES)
128780219Swpaul			prod = 0;
128880219Swpaul
128980219Swpaul		if (++cnt >= (TX_ENTRIES - 4))
129080219Swpaul			goto oactive;
129180219Swpaul
1292162375Sandre		if (m->m_flags & M_VLANTAG) {
129380219Swpaul			txd->tx_pflags = TX_PFLAGS_VLAN |
1294162375Sandre			    (htons(m->m_pkthdr.ether_vtag) << TX_PFLAGS_VLANTAG_S);
129580219Swpaul		}
129683115Sbrooks
129780219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_IP)
129880219Swpaul			txd->tx_pflags |= TX_PFLAGS_IPCKSUM;
129980219Swpaul
130080219Swpaul#if 0
130180219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_TCP)
130280219Swpaul			txd->tx_pflags |= TX_PFLAGS_TCPCKSUM;
130380219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_UDP)
130480219Swpaul			txd->tx_pflags |= TX_PFLAGS_UDPCKSUM;
130580219Swpaul#endif
130680219Swpaul
130780219Swpaul		fxd = (struct txp_frag_desc *)(r->r_desc + prod);
130880219Swpaul		for (m0 = m; m0 != NULL; m0 = m0->m_next) {
130980219Swpaul			if (m0->m_len == 0)
131080219Swpaul				continue;
131180219Swpaul			if (++cnt >= (TX_ENTRIES - 4))
131280219Swpaul				goto oactive;
131380219Swpaul
131480219Swpaul			txd->tx_numdesc++;
131580219Swpaul
131680219Swpaul			fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG;
131780219Swpaul			fxd->frag_rsvd1 = 0;
131880219Swpaul			fxd->frag_len = m0->m_len;
131980219Swpaul			fxd->frag_addrlo = vtophys(mtod(m0, vm_offset_t));
132080219Swpaul			fxd->frag_addrhi = 0;
132180219Swpaul			fxd->frag_rsvd2 = 0;
132280219Swpaul
132380219Swpaul			if (++prod == TX_ENTRIES) {
132480219Swpaul				fxd = (struct txp_frag_desc *)r->r_desc;
132580219Swpaul				prod = 0;
132680219Swpaul			} else
132780219Swpaul				fxd++;
132880219Swpaul
132980219Swpaul		}
133080219Swpaul
133180219Swpaul		ifp->if_timer = 5;
133280219Swpaul
1333167190Scsjp		ETHER_BPF_MTAP(ifp, m);
133480219Swpaul		WRITE_REG(sc, r->r_reg, TXP_IDX2OFFSET(prod));
133580219Swpaul	}
133680219Swpaul
133780219Swpaul	r->r_prod = prod;
133880219Swpaul	r->r_cnt = cnt;
133980219Swpaul	return;
134080219Swpaul
134180219Swpauloactive:
1342148887Srwatson	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
134380219Swpaul	r->r_prod = firstprod;
134480219Swpaul	r->r_cnt = firstcnt;
134580219Swpaul	IF_PREPEND(&ifp->if_snd, m);
134680219Swpaul}
134780219Swpaul
134880219Swpaul/*
134980219Swpaul * Handle simple commands sent to the typhoon
135080219Swpaul */
135180219Swpaulstatic int
1352189004Srdivackytxp_command(struct txp_softc *sc, u_int16_t id, u_int16_t in1, u_int32_t in2,
1353189004Srdivacky    u_int32_t in3, u_int16_t *out1, u_int32_t *out2, u_int32_t *out3, int wait)
135480219Swpaul{
135580219Swpaul	struct txp_rsp_desc *rsp = NULL;
135680219Swpaul
135780219Swpaul	if (txp_command2(sc, id, in1, in2, in3, NULL, 0, &rsp, wait))
135880219Swpaul		return (-1);
135980219Swpaul
136080219Swpaul	if (!wait)
136180219Swpaul		return (0);
136280219Swpaul
136380219Swpaul	if (out1 != NULL)
136480219Swpaul		*out1 = rsp->rsp_par1;
136580219Swpaul	if (out2 != NULL)
136680219Swpaul		*out2 = rsp->rsp_par2;
136780219Swpaul	if (out3 != NULL)
136880219Swpaul		*out3 = rsp->rsp_par3;
136980219Swpaul	free(rsp, M_DEVBUF);
137080219Swpaul	return (0);
137180219Swpaul}
137280219Swpaul
137380219Swpaulstatic int
1374189004Srdivackytxp_command2(struct txp_softc *sc, u_int16_t id, u_int16_t in1, u_int32_t in2,
1375189004Srdivacky    u_int32_t in3, struct txp_ext_desc *in_extp, u_int8_t in_extn,
1376189004Srdivacky    struct txp_rsp_desc **rspp, int wait)
137780219Swpaul{
137880219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
137980219Swpaul	struct txp_cmd_desc *cmd;
138080219Swpaul	struct txp_ext_desc *ext;
138180219Swpaul	u_int32_t idx, i;
138280219Swpaul	u_int16_t seq;
138380219Swpaul
138480219Swpaul	if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
138580219Swpaul		device_printf(sc->sc_dev, "no free cmd descriptors\n");
138680219Swpaul		return (-1);
138780219Swpaul	}
138880219Swpaul
138980219Swpaul	idx = sc->sc_cmdring.lastwrite;
139080219Swpaul	cmd = (struct txp_cmd_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
139180219Swpaul	bzero(cmd, sizeof(*cmd));
139280219Swpaul
139380219Swpaul	cmd->cmd_numdesc = in_extn;
139480219Swpaul	cmd->cmd_seq = seq = sc->sc_seq++;
139580219Swpaul	cmd->cmd_id = id;
139680219Swpaul	cmd->cmd_par1 = in1;
139780219Swpaul	cmd->cmd_par2 = in2;
139880219Swpaul	cmd->cmd_par3 = in3;
139980219Swpaul	cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
140080219Swpaul	    (wait ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
140180219Swpaul
140280219Swpaul	idx += sizeof(struct txp_cmd_desc);
140380219Swpaul	if (idx == sc->sc_cmdring.size)
140480219Swpaul		idx = 0;
140580219Swpaul
140680219Swpaul	for (i = 0; i < in_extn; i++) {
140780219Swpaul		ext = (struct txp_ext_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
140880219Swpaul		bcopy(in_extp, ext, sizeof(struct txp_ext_desc));
140980219Swpaul		in_extp++;
141080219Swpaul		idx += sizeof(struct txp_cmd_desc);
141180219Swpaul		if (idx == sc->sc_cmdring.size)
141280219Swpaul			idx = 0;
141380219Swpaul	}
141480219Swpaul
141580219Swpaul	sc->sc_cmdring.lastwrite = idx;
141680219Swpaul
141780219Swpaul	WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
141880219Swpaul
141980219Swpaul	if (!wait)
142080219Swpaul		return (0);
142180219Swpaul
142280219Swpaul	for (i = 0; i < 10000; i++) {
142380219Swpaul		idx = hv->hv_resp_read_idx;
142480219Swpaul		if (idx != hv->hv_resp_write_idx) {
142580219Swpaul			*rspp = NULL;
142680219Swpaul			if (txp_response(sc, idx, id, seq, rspp))
142780219Swpaul				return (-1);
142880219Swpaul			if (*rspp != NULL)
142980219Swpaul				break;
143080219Swpaul		}
143180219Swpaul		DELAY(50);
143280219Swpaul	}
143380219Swpaul	if (i == 1000 || (*rspp) == NULL) {
143480219Swpaul		device_printf(sc->sc_dev, "0x%x command failed\n", id);
143580219Swpaul		return (-1);
143680219Swpaul	}
143780219Swpaul
143880219Swpaul	return (0);
143980219Swpaul}
144080219Swpaul
144180219Swpaulstatic int
1442189004Srdivackytxp_response(struct txp_softc *sc, u_int32_t ridx, u_int16_t id, u_int16_t seq,
1443189004Srdivacky    struct txp_rsp_desc **rspp)
144480219Swpaul{
144580219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
144680219Swpaul	struct txp_rsp_desc *rsp;
144780219Swpaul
144880219Swpaul	while (ridx != hv->hv_resp_write_idx) {
144980219Swpaul		rsp = (struct txp_rsp_desc *)(((u_int8_t *)sc->sc_rspring.base) + ridx);
145080219Swpaul
145180219Swpaul		if (id == rsp->rsp_id && rsp->rsp_seq == seq) {
145280219Swpaul			*rspp = (struct txp_rsp_desc *)malloc(
145380219Swpaul			    sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
145480219Swpaul			    M_DEVBUF, M_NOWAIT);
145580219Swpaul			if ((*rspp) == NULL)
145680219Swpaul				return (-1);
145780219Swpaul			txp_rsp_fixup(sc, rsp, *rspp);
145880219Swpaul			return (0);
145980219Swpaul		}
146080219Swpaul
146180219Swpaul		if (rsp->rsp_flags & RSP_FLAGS_ERROR) {
146280219Swpaul			device_printf(sc->sc_dev, "response error!\n");
146380219Swpaul			txp_rsp_fixup(sc, rsp, NULL);
146480219Swpaul			ridx = hv->hv_resp_read_idx;
146580219Swpaul			continue;
146680219Swpaul		}
146780219Swpaul
146880219Swpaul		switch (rsp->rsp_id) {
146980219Swpaul		case TXP_CMD_CYCLE_STATISTICS:
147080219Swpaul		case TXP_CMD_MEDIA_STATUS_READ:
147180219Swpaul			break;
147280219Swpaul		case TXP_CMD_HELLO_RESPONSE:
147380219Swpaul			device_printf(sc->sc_dev, "hello\n");
147480219Swpaul			break;
147580219Swpaul		default:
147680219Swpaul			device_printf(sc->sc_dev, "unknown id(0x%x)\n",
147780219Swpaul			    rsp->rsp_id);
147880219Swpaul		}
147980219Swpaul
148080219Swpaul		txp_rsp_fixup(sc, rsp, NULL);
148180219Swpaul		ridx = hv->hv_resp_read_idx;
148280219Swpaul		hv->hv_resp_read_idx = ridx;
148380219Swpaul	}
148480219Swpaul
148580219Swpaul	return (0);
148680219Swpaul}
148780219Swpaul
148880219Swpaulstatic void
1489189685Syongaritxp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp,
1490189685Syongari    struct txp_rsp_desc *dst)
149180219Swpaul{
149280219Swpaul	struct txp_rsp_desc *src = rsp;
149380219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
149480219Swpaul	u_int32_t i, ridx;
149580219Swpaul
149680219Swpaul	ridx = hv->hv_resp_read_idx;
149780219Swpaul
149880219Swpaul	for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
149980219Swpaul		if (dst != NULL)
150080219Swpaul			bcopy(src, dst++, sizeof(struct txp_rsp_desc));
150180219Swpaul		ridx += sizeof(struct txp_rsp_desc);
150280219Swpaul		if (ridx == sc->sc_rspring.size) {
150380219Swpaul			src = sc->sc_rspring.base;
150480219Swpaul			ridx = 0;
150580219Swpaul		} else
150680219Swpaul			src++;
150780219Swpaul		sc->sc_rspring.lastwrite = hv->hv_resp_read_idx = ridx;
150880219Swpaul	}
1509189686Syongari
151080219Swpaul	hv->hv_resp_read_idx = ridx;
151180219Swpaul}
151280219Swpaul
151380219Swpaulstatic int
1514189685Syongaritxp_cmd_desc_numfree(struct txp_softc *sc)
151580219Swpaul{
151680219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
151780219Swpaul	struct txp_boot_record *br = sc->sc_boot;
151880219Swpaul	u_int32_t widx, ridx, nfree;
151980219Swpaul
152080219Swpaul	widx = sc->sc_cmdring.lastwrite;
152180219Swpaul	ridx = hv->hv_cmd_read_idx;
152280219Swpaul
152380219Swpaul	if (widx == ridx) {
152480219Swpaul		/* Ring is completely free */
152580219Swpaul		nfree = br->br_cmd_siz - sizeof(struct txp_cmd_desc);
152680219Swpaul	} else {
152780219Swpaul		if (widx > ridx)
152880219Swpaul			nfree = br->br_cmd_siz -
152980219Swpaul			    (widx - ridx + sizeof(struct txp_cmd_desc));
153080219Swpaul		else
153180219Swpaul			nfree = ridx - widx - sizeof(struct txp_cmd_desc);
153280219Swpaul	}
153380219Swpaul
153480219Swpaul	return (nfree / sizeof(struct txp_cmd_desc));
153580219Swpaul}
153680219Swpaul
153780219Swpaulstatic void
1538189685Syongaritxp_stop(struct txp_softc *sc)
153980219Swpaul{
154080219Swpaul	struct ifnet *ifp;
154180219Swpaul
1542151772Sjhb	TXP_LOCK_ASSERT(sc);
1543147256Sbrooks	ifp = sc->sc_ifp;
154480219Swpaul
1545148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
154680219Swpaul
1547151772Sjhb	callout_stop(&sc->sc_tick);
154880219Swpaul
154980219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
155080219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
155180219Swpaul
155280219Swpaul	txp_rxring_empty(sc);
155380219Swpaul}
155480219Swpaul
155580219Swpaulstatic void
1556189685Syongaritxp_watchdog(struct ifnet *ifp)
155780219Swpaul{
1558189687Syongari
155980219Swpaul}
156080219Swpaul
156180219Swpaulstatic int
1562189685Syongaritxp_ifmedia_upd(struct ifnet *ifp)
156380219Swpaul{
156480219Swpaul	struct txp_softc *sc = ifp->if_softc;
156580219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
156680219Swpaul	u_int16_t new_xcvr;
156780219Swpaul
1568151772Sjhb	TXP_LOCK(sc);
1569151772Sjhb	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) {
1570151772Sjhb		TXP_UNLOCK(sc);
157180219Swpaul		return (EINVAL);
1572151772Sjhb	}
157380219Swpaul
157480219Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
157580219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
157680219Swpaul			new_xcvr = TXP_XCVR_10_FDX;
157780219Swpaul		else
157880219Swpaul			new_xcvr = TXP_XCVR_10_HDX;
157980219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
158080219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
158180219Swpaul			new_xcvr = TXP_XCVR_100_FDX;
158280219Swpaul		else
158380219Swpaul			new_xcvr = TXP_XCVR_100_HDX;
158480219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
158580219Swpaul		new_xcvr = TXP_XCVR_AUTO;
1586151772Sjhb	} else {
1587151772Sjhb		TXP_UNLOCK(sc);
158880219Swpaul		return (EINVAL);
1589151772Sjhb	}
159080219Swpaul
159180219Swpaul	/* nothing to do */
1592151772Sjhb	if (sc->sc_xcvr == new_xcvr) {
1593151772Sjhb		TXP_UNLOCK(sc);
159480219Swpaul		return (0);
1595151772Sjhb	}
159680219Swpaul
159780219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
159880219Swpaul	    NULL, NULL, NULL, 0);
159980219Swpaul	sc->sc_xcvr = new_xcvr;
1600151772Sjhb	TXP_UNLOCK(sc);
160180219Swpaul
160280219Swpaul	return (0);
160380219Swpaul}
160480219Swpaul
160580219Swpaulstatic void
1606189685Syongaritxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
160780219Swpaul{
160880219Swpaul	struct txp_softc *sc = ifp->if_softc;
160980219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
1610173666Syongari	u_int16_t bmsr, bmcr, anar, anlpar;
161180219Swpaul
161280219Swpaul	ifmr->ifm_status = IFM_AVALID;
161380219Swpaul	ifmr->ifm_active = IFM_ETHER;
161480219Swpaul
1615151772Sjhb	TXP_LOCK(sc);
161680219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
161780219Swpaul	    &bmsr, NULL, NULL, 1))
161880219Swpaul		goto bail;
161980219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
162080219Swpaul	    &bmsr, NULL, NULL, 1))
162180219Swpaul		goto bail;
162280219Swpaul
162380219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
162480219Swpaul	    &bmcr, NULL, NULL, 1))
162580219Swpaul		goto bail;
162680219Swpaul
162780219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
162880219Swpaul	    &anlpar, NULL, NULL, 1))
162980219Swpaul		goto bail;
1630173666Syongari
1631173666Syongari	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANAR, 0,
1632173666Syongari	    &anar, NULL, NULL, 1))
1633173666Syongari		goto bail;
1634151772Sjhb	TXP_UNLOCK(sc);
163580219Swpaul
163680219Swpaul	if (bmsr & BMSR_LINK)
163780219Swpaul		ifmr->ifm_status |= IFM_ACTIVE;
163880219Swpaul
163980219Swpaul	if (bmcr & BMCR_ISO) {
164080219Swpaul		ifmr->ifm_active |= IFM_NONE;
164180219Swpaul		ifmr->ifm_status = 0;
164280219Swpaul		return;
164380219Swpaul	}
164480219Swpaul
164580219Swpaul	if (bmcr & BMCR_LOOP)
164680219Swpaul		ifmr->ifm_active |= IFM_LOOP;
164780219Swpaul
164880219Swpaul	if (bmcr & BMCR_AUTOEN) {
164980219Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
165080219Swpaul			ifmr->ifm_active |= IFM_NONE;
165180219Swpaul			return;
165280219Swpaul		}
165380219Swpaul
1654173666Syongari		anlpar &= anar;
1655173665Syongari		if (anlpar & ANLPAR_TX_FD)
1656173665Syongari			ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
1657173665Syongari		else if (anlpar & ANLPAR_T4)
165880219Swpaul			ifmr->ifm_active |= IFM_100_T4;
165980219Swpaul		else if (anlpar & ANLPAR_TX)
166080219Swpaul			ifmr->ifm_active |= IFM_100_TX;
166180219Swpaul		else if (anlpar & ANLPAR_10_FD)
166280219Swpaul			ifmr->ifm_active |= IFM_10_T|IFM_FDX;
166380219Swpaul		else if (anlpar & ANLPAR_10)
166480219Swpaul			ifmr->ifm_active |= IFM_10_T;
166580219Swpaul		else
166680219Swpaul			ifmr->ifm_active |= IFM_NONE;
166780219Swpaul	} else
166880219Swpaul		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
166980219Swpaul	return;
167080219Swpaul
167180219Swpaulbail:
1672151772Sjhb	TXP_UNLOCK(sc);
167380219Swpaul	ifmr->ifm_active |= IFM_NONE;
167480219Swpaul	ifmr->ifm_status &= ~IFM_AVALID;
167580219Swpaul}
167680219Swpaul
167780219Swpaul#ifdef TXP_DEBUG
167880219Swpaulstatic void
1679189685Syongaritxp_show_descriptor(void *d)
168080219Swpaul{
168180219Swpaul	struct txp_cmd_desc *cmd = d;
168280219Swpaul	struct txp_rsp_desc *rsp = d;
168380219Swpaul	struct txp_tx_desc *txd = d;
168480219Swpaul	struct txp_frag_desc *frgd = d;
168580219Swpaul
168680219Swpaul	switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
168780219Swpaul	case CMD_FLAGS_TYPE_CMD:
168880219Swpaul		/* command descriptor */
168980219Swpaul		printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
169080219Swpaul		    cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq,
169180219Swpaul		    cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3);
169280219Swpaul		break;
169380219Swpaul	case CMD_FLAGS_TYPE_RESP:
169480219Swpaul		/* response descriptor */
169580219Swpaul		printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
169680219Swpaul		    rsp->rsp_flags, rsp->rsp_numdesc, rsp->rsp_id, rsp->rsp_seq,
169780219Swpaul		    rsp->rsp_par1, rsp->rsp_par2, rsp->rsp_par3);
169880219Swpaul		break;
169980219Swpaul	case CMD_FLAGS_TYPE_DATA:
170080219Swpaul		/* data header (assuming tx for now) */
170180219Swpaul		printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
170280219Swpaul		    txd->tx_flags, txd->tx_numdesc, txd->tx_totlen,
170380219Swpaul		    txd->tx_addrlo, txd->tx_addrhi, txd->tx_pflags);
170480219Swpaul		break;
170580219Swpaul	case CMD_FLAGS_TYPE_FRAG:
170680219Swpaul		/* fragment descriptor */
170780219Swpaul		printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
170880219Swpaul		    frgd->frag_flags, frgd->frag_rsvd1, frgd->frag_len,
170980219Swpaul		    frgd->frag_addrlo, frgd->frag_addrhi, frgd->frag_rsvd2);
171080219Swpaul		break;
171180219Swpaul	default:
171280219Swpaul		printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
171380219Swpaul		    cmd->cmd_flags & CMD_FLAGS_TYPE_M,
171480219Swpaul		    cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq,
171580219Swpaul		    cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3);
171680219Swpaul		break;
171780219Swpaul	}
171880219Swpaul}
171980219Swpaul#endif
172080219Swpaul
172180219Swpaulstatic void
1722189685Syongaritxp_set_filter(struct txp_softc *sc)
172380219Swpaul{
1724147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
172580219Swpaul	u_int32_t crc, carry, hashbit, hash[2];
172680219Swpaul	u_int16_t filter;
172780219Swpaul	u_int8_t octet;
172880219Swpaul	int i, j, mcnt = 0;
172980219Swpaul	struct ifmultiaddr *ifma;
173080219Swpaul	char *enm;
173180219Swpaul
173280219Swpaul	if (ifp->if_flags & IFF_PROMISC) {
173380219Swpaul		filter = TXP_RXFILT_PROMISC;
173480219Swpaul		goto setit;
173580219Swpaul	}
173680219Swpaul
173780219Swpaul	filter = TXP_RXFILT_DIRECT;
173880219Swpaul
173980219Swpaul	if (ifp->if_flags & IFF_BROADCAST)
174080219Swpaul		filter |= TXP_RXFILT_BROADCAST;
174180219Swpaul
174280219Swpaul	if (ifp->if_flags & IFF_ALLMULTI)
174380219Swpaul		filter |= TXP_RXFILT_ALLMULTI;
174480219Swpaul	else {
174580219Swpaul		hash[0] = hash[1] = 0;
174680219Swpaul
1747148654Srwatson		IF_ADDR_LOCK(ifp);
174880219Swpaul		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
174980219Swpaul			if (ifma->ifma_addr->sa_family != AF_LINK)
175080219Swpaul				continue;
175180219Swpaul
175280219Swpaul			enm = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
175380219Swpaul			mcnt++;
175480219Swpaul			crc = 0xffffffff;
175580219Swpaul
175680219Swpaul			for (i = 0; i < ETHER_ADDR_LEN; i++) {
175780219Swpaul				octet = enm[i];
175880219Swpaul				for (j = 0; j < 8; j++) {
175980219Swpaul					carry = ((crc & 0x80000000) ? 1 : 0) ^
176080219Swpaul					    (octet & 1);
176180219Swpaul					crc <<= 1;
176280219Swpaul					octet >>= 1;
176380219Swpaul					if (carry)
176480219Swpaul						crc = (crc ^ TXP_POLYNOMIAL) |
176580219Swpaul						    carry;
176680219Swpaul				}
176780219Swpaul			}
176880219Swpaul			hashbit = (u_int16_t)(crc & (64 - 1));
176980219Swpaul			hash[hashbit / 32] |= (1 << hashbit % 32);
177080219Swpaul		}
1771148654Srwatson		IF_ADDR_UNLOCK(ifp);
177280219Swpaul
177380219Swpaul		if (mcnt > 0) {
177480219Swpaul			filter |= TXP_RXFILT_HASHMULTI;
177580219Swpaul			txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE,
177680219Swpaul			    2, hash[0], hash[1], NULL, NULL, NULL, 0);
177780219Swpaul		}
177880219Swpaul	}
177980219Swpaul
178080219Swpaulsetit:
178180219Swpaul
178280219Swpaul	txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
178380219Swpaul	    NULL, NULL, NULL, 1);
178480219Swpaul}
178580219Swpaul
178680219Swpaulstatic void
1787189685Syongaritxp_capabilities(struct txp_softc *sc)
178880219Swpaul{
1789147256Sbrooks	struct ifnet *ifp = sc->sc_ifp;
179080219Swpaul	struct txp_rsp_desc *rsp = NULL;
179180219Swpaul	struct txp_ext_desc *ext;
179280219Swpaul
179380219Swpaul	if (txp_command2(sc, TXP_CMD_OFFLOAD_READ, 0, 0, 0, NULL, 0, &rsp, 1))
179480219Swpaul		goto out;
179580219Swpaul
179680219Swpaul	if (rsp->rsp_numdesc != 1)
179780219Swpaul		goto out;
179880219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
179980219Swpaul
180080219Swpaul	sc->sc_tx_capability = ext->ext_1 & OFFLOAD_MASK;
180180219Swpaul	sc->sc_rx_capability = ext->ext_2 & OFFLOAD_MASK;
180283631Sjlemon	ifp->if_capabilities = 0;
180380219Swpaul
180480219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_VLAN) {
180580219Swpaul		sc->sc_tx_capability |= OFFLOAD_VLAN;
180680219Swpaul		sc->sc_rx_capability |= OFFLOAD_VLAN;
1807106937Ssam		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
180880219Swpaul	}
180980219Swpaul
181080219Swpaul#if 0
181180219Swpaul	/* not ready yet */
181280219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPSEC) {
181380219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPSEC;
181480219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPSEC;
181580219Swpaul		ifp->if_capabilities |= IFCAP_IPSEC;
181680219Swpaul	}
181780219Swpaul#endif
181880219Swpaul
181980219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPCKSUM) {
182080219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPCKSUM;
182180219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPCKSUM;
182283631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
182380219Swpaul		ifp->if_hwassist |= CSUM_IP;
182480219Swpaul	}
182580219Swpaul
182680219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_TCPCKSUM) {
182780219Swpaul#if 0
182880219Swpaul		sc->sc_tx_capability |= OFFLOAD_TCPCKSUM;
182980219Swpaul#endif
183080219Swpaul		sc->sc_rx_capability |= OFFLOAD_TCPCKSUM;
183183631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
183280219Swpaul	}
183380219Swpaul
183480219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_UDPCKSUM) {
183580219Swpaul#if 0
183680219Swpaul		sc->sc_tx_capability |= OFFLOAD_UDPCKSUM;
183780219Swpaul#endif
183880219Swpaul		sc->sc_rx_capability |= OFFLOAD_UDPCKSUM;
183983631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
184080219Swpaul	}
184183631Sjlemon	ifp->if_capenable = ifp->if_capabilities;
184280219Swpaul
184380219Swpaul	if (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0,
184480219Swpaul	    sc->sc_tx_capability, sc->sc_rx_capability, NULL, NULL, NULL, 1))
184580219Swpaul		goto out;
184680219Swpaul
184780219Swpaulout:
184880219Swpaul	if (rsp != NULL)
184980219Swpaul		free(rsp, M_DEVBUF);
185080219Swpaul}
1851