if_txp.c revision 142880
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 142880 2005-03-01 07:50:12Z imp $");
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 142880 2005-03-01 07:50:12Z imp $");
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_pio.h>
7880219Swpaul#include <machine/bus_memio.h>
7980219Swpaul#include <machine/bus.h>
8080219Swpaul#include <machine/resource.h>
8180219Swpaul#include <sys/bus.h>
8280219Swpaul#include <sys/rman.h>
8380219Swpaul
8480219Swpaul#include <dev/mii/mii.h>
8580219Swpaul#include <dev/mii/miivar.h>
8680219Swpaul#include <dev/pci/pcireg.h>
8780219Swpaul#include <dev/pci/pcivar.h>
8880219Swpaul
8980219Swpaul#define TXP_USEIOSPACE
9080229Swpaul#define __STRICT_ALIGNMENT
9180219Swpaul
9280219Swpaul#include <dev/txp/if_txpreg.h>
9380219Swpaul#include <dev/txp/3c990img.h>
9480219Swpaul
9580219Swpaul#ifndef lint
9680219Swpaulstatic const char rcsid[] =
9780219Swpaul  "$FreeBSD: head/sys/dev/txp/if_txp.c 142880 2005-03-01 07:50:12Z imp $";
9880219Swpaul#endif
9980219Swpaul
10080219Swpaul/*
10180219Swpaul * Various supported device vendors/types and their names.
10280219Swpaul */
10380219Swpaulstatic struct txp_type txp_devs[] = {
10480219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95,
10580219Swpaul	    "3Com 3cR990-TX-95 Etherlink with 3XP Processor" },
10680219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97,
10780219Swpaul	    "3Com 3cR990-TX-97 Etherlink with 3XP Processor" },
10880219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM,
10980219Swpaul	    "3Com 3cR990B-TXM Etherlink with 3XP Processor" },
11080219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95,
11180219Swpaul	    "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" },
11280219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97,
11380219Swpaul	    "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" },
11480219Swpaul	{ TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV,
11580219Swpaul	    "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" },
11680219Swpaul	{ 0, 0, NULL }
11780219Swpaul};
11880219Swpaul
11992739Salfredstatic int txp_probe	(device_t);
12092739Salfredstatic int txp_attach	(device_t);
12192739Salfredstatic int txp_detach	(device_t);
12292739Salfredstatic void txp_intr	(void *);
12392739Salfredstatic void txp_tick	(void *);
12492739Salfredstatic int txp_shutdown	(device_t);
12592739Salfredstatic int txp_ioctl	(struct ifnet *, u_long, caddr_t);
12692739Salfredstatic void txp_start	(struct ifnet *);
12792739Salfredstatic void txp_stop	(struct txp_softc *);
12892739Salfredstatic void txp_init	(void *);
12992739Salfredstatic 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 *);
13692739Salfredstatic 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 *);
14492739Salfredstatic 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);
14692739Salfredstatic 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);
14992739Salfredstatic int txp_response (struct txp_softc *, u_int32_t, u_int16_t, u_int16_t,
15092739Salfred    struct txp_rsp_desc **);
15192739Salfredstatic 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;
22180219Swpaul	int unit, error = 0, rid;
22280219Swpaul
22380219Swpaul	sc = device_get_softc(dev);
22480219Swpaul	unit = device_get_unit(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,
22993818Sjhb	    MTX_DEF | MTX_RECURSE);
23080219Swpaul	/*
23180219Swpaul	 * Map control/status registers.
23280219Swpaul	 */
23380219Swpaul	pci_enable_busmaster(dev);
23480219Swpaul
23580219Swpaul	rid = TXP_RID;
236127135Snjl	sc->sc_res = bus_alloc_resource_any(dev, TXP_RES, &rid,
237127135Snjl	    RF_ACTIVE);
23880219Swpaul
23980219Swpaul	if (sc->sc_res == NULL) {
24080219Swpaul		device_printf(dev, "couldn't map ports/memory\n");
24180219Swpaul		error = ENXIO;
24280219Swpaul		goto fail;
24380219Swpaul	}
24480219Swpaul
24580219Swpaul	sc->sc_bt = rman_get_bustag(sc->sc_res);
24680219Swpaul	sc->sc_bh = rman_get_bushandle(sc->sc_res);
24780219Swpaul
24880219Swpaul	/* Allocate interrupt */
24980219Swpaul	rid = 0;
250127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
25180219Swpaul	    RF_SHAREABLE | RF_ACTIVE);
25280219Swpaul
25380219Swpaul	if (sc->sc_irq == NULL) {
25480219Swpaul		device_printf(dev, "couldn't map interrupt\n");
25580219Swpaul		txp_release_resources(sc);
25680219Swpaul		error = ENXIO;
25780219Swpaul		goto fail;
25880219Swpaul	}
25980219Swpaul
26080219Swpaul	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET,
26180219Swpaul	    txp_intr, sc, &sc->sc_intrhand);
26280219Swpaul
26380219Swpaul	if (error) {
26480219Swpaul		txp_release_resources(sc);
26580219Swpaul		device_printf(dev, "couldn't set up irq\n");
26680219Swpaul		goto fail;
26780219Swpaul	}
26880219Swpaul
26980219Swpaul	if (txp_chip_init(sc)) {
27080219Swpaul		txp_release_resources(sc);
27180219Swpaul		goto fail;
27280219Swpaul	}
27380219Swpaul
27480219Swpaul	sc->sc_fwbuf = contigmalloc(32768, M_DEVBUF,
27580219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
27680219Swpaul	error = txp_download_fw(sc);
27780219Swpaul	contigfree(sc->sc_fwbuf, 32768, M_DEVBUF);
27880219Swpaul	sc->sc_fwbuf = NULL;
27980219Swpaul
28080219Swpaul	if (error) {
28180219Swpaul		txp_release_resources(sc);
28280219Swpaul		goto fail;
28380219Swpaul	}
28480219Swpaul
28580219Swpaul	sc->sc_ldata = contigmalloc(sizeof(struct txp_ldata), M_DEVBUF,
28680219Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
28780457Swpaul	bzero(sc->sc_ldata, sizeof(struct txp_ldata));
28880219Swpaul
28980219Swpaul	if (txp_alloc_rings(sc)) {
29080219Swpaul		txp_release_resources(sc);
29180219Swpaul		goto fail;
29280219Swpaul	}
29380219Swpaul
29480219Swpaul	if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
29580219Swpaul	    NULL, NULL, NULL, 1)) {
29680219Swpaul		txp_release_resources(sc);
29780219Swpaul		goto fail;
29880219Swpaul	}
29980219Swpaul
30080219Swpaul	if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
30180219Swpaul	    &p1, &p2, NULL, 1)) {
30280219Swpaul		txp_release_resources(sc);
30380219Swpaul		goto fail;
30480219Swpaul	}
30580219Swpaul
30680219Swpaul	txp_set_filter(sc);
30780219Swpaul
30880219Swpaul	sc->sc_arpcom.ac_enaddr[0] = ((u_int8_t *)&p1)[1];
30980219Swpaul	sc->sc_arpcom.ac_enaddr[1] = ((u_int8_t *)&p1)[0];
31080219Swpaul	sc->sc_arpcom.ac_enaddr[2] = ((u_int8_t *)&p2)[3];
31180219Swpaul	sc->sc_arpcom.ac_enaddr[3] = ((u_int8_t *)&p2)[2];
31280219Swpaul	sc->sc_arpcom.ac_enaddr[4] = ((u_int8_t *)&p2)[1];
31380219Swpaul	sc->sc_arpcom.ac_enaddr[5] = ((u_int8_t *)&p2)[0];
31480219Swpaul
31580219Swpaul	sc->sc_cold = 0;
31680219Swpaul
31780219Swpaul	ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
31880219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
31980219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
32080219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
32180219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
32280219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);
32380219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
32480219Swpaul	ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
32580219Swpaul
32680219Swpaul	sc->sc_xcvr = TXP_XCVR_AUTO;
32780219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
32880219Swpaul	    NULL, NULL, NULL, 0);
32980219Swpaul	ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO);
33080219Swpaul
33180219Swpaul	ifp = &sc->sc_arpcom.ac_if;
33280219Swpaul	ifp->if_softc = sc;
333121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
33480219Swpaul	ifp->if_mtu = ETHERMTU;
335133701Srwatson	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
336133701Srwatson	    IFF_NEEDSGIANT;
33780219Swpaul	ifp->if_ioctl = txp_ioctl;
33880219Swpaul	ifp->if_start = txp_start;
33980219Swpaul	ifp->if_watchdog = txp_watchdog;
34080219Swpaul	ifp->if_init = txp_init;
34180219Swpaul	ifp->if_baudrate = 100000000;
34280219Swpaul	ifp->if_snd.ifq_maxlen = TX_ENTRIES;
34380219Swpaul	ifp->if_hwassist = 0;
34480219Swpaul	txp_capabilities(sc);
34580219Swpaul
34680219Swpaul	/*
34780219Swpaul	 * Attach us everywhere
34880219Swpaul	 */
349106937Ssam	ether_ifattach(ifp, sc->sc_arpcom.ac_enaddr);
35080219Swpaul	callout_handle_init(&sc->sc_tick);
35180219Swpaul	return(0);
35280219Swpaul
35380219Swpaulfail:
35480219Swpaul	txp_release_resources(sc);
35580219Swpaul	mtx_destroy(&sc->sc_mtx);
35680219Swpaul	return(error);
35780219Swpaul}
35880219Swpaul
35980219Swpaulstatic int
36080219Swpaultxp_detach(dev)
36180219Swpaul	device_t dev;
36280219Swpaul{
36380219Swpaul	struct txp_softc *sc;
36480219Swpaul	struct ifnet *ifp;
36580219Swpaul	int i;
36680219Swpaul
36780219Swpaul	sc = device_get_softc(dev);
36880219Swpaul	ifp = &sc->sc_arpcom.ac_if;
36980219Swpaul
37080219Swpaul	txp_stop(sc);
37180219Swpaul	txp_shutdown(dev);
37280219Swpaul
37380219Swpaul	ifmedia_removeall(&sc->sc_ifmedia);
374106937Ssam	ether_ifdetach(ifp);
37580219Swpaul
37680219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++)
37780219Swpaul		free(sc->sc_rxbufs[i].rb_sd, M_DEVBUF);
37880219Swpaul
37980219Swpaul	txp_release_resources(sc);
38080219Swpaul
38180219Swpaul	mtx_destroy(&sc->sc_mtx);
38280219Swpaul	return(0);
38380219Swpaul}
38480219Swpaul
38580219Swpaulstatic void
38680219Swpaultxp_release_resources(sc)
38780219Swpaul	struct txp_softc *sc;
38880219Swpaul{
38980219Swpaul	device_t dev;
39080219Swpaul
39180219Swpaul	dev = sc->sc_dev;
39280219Swpaul
39380219Swpaul	if (sc->sc_intrhand != NULL)
39480219Swpaul		bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
39580219Swpaul
39680219Swpaul	if (sc->sc_irq != NULL)
39780219Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
39880219Swpaul
39980219Swpaul	if (sc->sc_res != NULL)
40080219Swpaul		bus_release_resource(dev, TXP_RES, TXP_RID, sc->sc_res);
40180219Swpaul
40280219Swpaul	if (sc->sc_ldata != NULL)
40380219Swpaul		contigfree(sc->sc_ldata, sizeof(struct txp_ldata), M_DEVBUF);
40480219Swpaul
40580219Swpaul	return;
40680219Swpaul}
40780219Swpaul
40880219Swpaulstatic int
40980219Swpaultxp_chip_init(sc)
41080219Swpaul	struct txp_softc *sc;
41180219Swpaul{
41280219Swpaul	/* disable interrupts */
41380219Swpaul	WRITE_REG(sc, TXP_IER, 0);
41480219Swpaul	WRITE_REG(sc, TXP_IMR,
41580219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
41680219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
41780219Swpaul	    TXP_INT_LATCH);
41880219Swpaul
41980219Swpaul	/* ack all interrupts */
42080219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
42180219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
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_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
42580219Swpaul
42680219Swpaul	if (txp_reset_adapter(sc))
42780219Swpaul		return (-1);
42880219Swpaul
42980219Swpaul	/* disable interrupts */
43080219Swpaul	WRITE_REG(sc, TXP_IER, 0);
43180219Swpaul	WRITE_REG(sc, TXP_IMR,
43280219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
43380219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
43480219Swpaul	    TXP_INT_LATCH);
43580219Swpaul
43680219Swpaul	/* ack all interrupts */
43780219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
43880219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
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_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
44280219Swpaul
44380219Swpaul	return (0);
44480219Swpaul}
44580219Swpaul
44680219Swpaulstatic int
44780219Swpaultxp_reset_adapter(sc)
44880219Swpaul	struct txp_softc *sc;
44980219Swpaul{
45080219Swpaul	u_int32_t r;
45180219Swpaul	int i;
45280219Swpaul
45392643Sjeff	r = 0;
45480219Swpaul	WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
45580219Swpaul	DELAY(1000);
45680219Swpaul	WRITE_REG(sc, TXP_SRR, 0);
45780219Swpaul
45880219Swpaul	/* Should wait max 6 seconds */
45980219Swpaul	for (i = 0; i < 6000; i++) {
46080219Swpaul		r = READ_REG(sc, TXP_A2H_0);
46180219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
46280219Swpaul			break;
46380219Swpaul		DELAY(1000);
46480219Swpaul	}
46580219Swpaul
46680219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
46780219Swpaul		device_printf(sc->sc_dev, "reset hung\n");
46880219Swpaul		return (-1);
46980219Swpaul	}
47080219Swpaul
47180219Swpaul	return (0);
47280219Swpaul}
47380219Swpaul
47480219Swpaulstatic int
47580219Swpaultxp_download_fw(sc)
47680219Swpaul	struct txp_softc *sc;
47780219Swpaul{
47880219Swpaul	struct txp_fw_file_header *fileheader;
47980219Swpaul	struct txp_fw_section_header *secthead;
48080219Swpaul	int sect;
48180219Swpaul	u_int32_t r, i, ier, imr;
48280219Swpaul
48392643Sjeff	r = 0;
48480219Swpaul	ier = READ_REG(sc, TXP_IER);
48580219Swpaul	WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
48680219Swpaul
48780219Swpaul	imr = READ_REG(sc, TXP_IMR);
48880219Swpaul	WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
48980219Swpaul
49080219Swpaul	for (i = 0; i < 10000; i++) {
49180219Swpaul		r = READ_REG(sc, TXP_A2H_0);
49280219Swpaul		if (r == STAT_WAITING_FOR_HOST_REQUEST)
49380219Swpaul			break;
49480219Swpaul		DELAY(50);
49580219Swpaul	}
49680219Swpaul	if (r != STAT_WAITING_FOR_HOST_REQUEST) {
49780219Swpaul		device_printf(sc->sc_dev, "not waiting for host request\n");
49880219Swpaul		return (-1);
49980219Swpaul	}
50080219Swpaul
50180219Swpaul	/* Ack the status */
50280219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
50380219Swpaul
50480219Swpaul	fileheader = (struct txp_fw_file_header *)tc990image;
50580219Swpaul	if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
50680219Swpaul		device_printf(sc->sc_dev, "fw invalid magic\n");
50780219Swpaul		return (-1);
50880219Swpaul	}
50980219Swpaul
51080219Swpaul	/* Tell boot firmware to get ready for image */
51180219Swpaul	WRITE_REG(sc, TXP_H2A_1, fileheader->addr);
51280219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
51380219Swpaul
51480219Swpaul	if (txp_download_fw_wait(sc)) {
51580219Swpaul		device_printf(sc->sc_dev, "fw wait failed, initial\n");
51680219Swpaul		return (-1);
51780219Swpaul	}
51880219Swpaul
51980219Swpaul	secthead = (struct txp_fw_section_header *)(((u_int8_t *)tc990image) +
52080219Swpaul	    sizeof(struct txp_fw_file_header));
52180219Swpaul
52280219Swpaul	for (sect = 0; sect < fileheader->nsections; sect++) {
52380219Swpaul		if (txp_download_fw_section(sc, secthead, sect))
52480219Swpaul			return (-1);
52580219Swpaul		secthead = (struct txp_fw_section_header *)
52680219Swpaul		    (((u_int8_t *)secthead) + secthead->nbytes +
52780219Swpaul		    sizeof(*secthead));
52880219Swpaul	}
52980219Swpaul
53080219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
53180219Swpaul
53280219Swpaul	for (i = 0; i < 10000; i++) {
53380219Swpaul		r = READ_REG(sc, TXP_A2H_0);
53480219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
53580219Swpaul			break;
53680219Swpaul		DELAY(50);
53780219Swpaul	}
53880219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
53980219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
54080219Swpaul		return (-1);
54180219Swpaul	}
54280219Swpaul
54380219Swpaul	WRITE_REG(sc, TXP_IER, ier);
54480219Swpaul	WRITE_REG(sc, TXP_IMR, imr);
54580219Swpaul
54680219Swpaul	return (0);
54780219Swpaul}
54880219Swpaul
54980219Swpaulstatic int
55080219Swpaultxp_download_fw_wait(sc)
55180219Swpaul	struct txp_softc *sc;
55280219Swpaul{
55380219Swpaul	u_int32_t i, r;
55480219Swpaul
55592643Sjeff	r = 0;
55680219Swpaul	for (i = 0; i < 10000; i++) {
55780219Swpaul		r = READ_REG(sc, TXP_ISR);
55880219Swpaul		if (r & TXP_INT_A2H_0)
55980219Swpaul			break;
56080219Swpaul		DELAY(50);
56180219Swpaul	}
56280219Swpaul
56380219Swpaul	if (!(r & TXP_INT_A2H_0)) {
56480219Swpaul		device_printf(sc->sc_dev, "fw wait failed comm0\n");
56580219Swpaul		return (-1);
56680219Swpaul	}
56780219Swpaul
56880219Swpaul	WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
56980219Swpaul
57080219Swpaul	r = READ_REG(sc, TXP_A2H_0);
57180219Swpaul	if (r != STAT_WAITING_FOR_SEGMENT) {
57280219Swpaul		device_printf(sc->sc_dev, "fw not waiting for segment\n");
57380219Swpaul		return (-1);
57480219Swpaul	}
57580219Swpaul	return (0);
57680219Swpaul}
57780219Swpaul
57880219Swpaulstatic int
57980219Swpaultxp_download_fw_section(sc, sect, sectnum)
58080219Swpaul	struct txp_softc *sc;
58180219Swpaul	struct txp_fw_section_header *sect;
58280219Swpaul	int sectnum;
58380219Swpaul{
58480219Swpaul	vm_offset_t dma;
58580219Swpaul	int rseg, err = 0;
58680219Swpaul	struct mbuf m;
58780219Swpaul	u_int16_t csum;
58880219Swpaul
58980219Swpaul	/* Skip zero length sections */
59080219Swpaul	if (sect->nbytes == 0)
59180219Swpaul		return (0);
59280219Swpaul
59380219Swpaul	/* Make sure we aren't past the end of the image */
59480219Swpaul	rseg = ((u_int8_t *)sect) - ((u_int8_t *)tc990image);
59580219Swpaul	if (rseg >= sizeof(tc990image)) {
59680219Swpaul		device_printf(sc->sc_dev, "fw invalid section address, "
59780219Swpaul		    "section %d\n", sectnum);
59880219Swpaul		return (-1);
59980219Swpaul	}
60080219Swpaul
60180219Swpaul	/* Make sure this section doesn't go past the end */
60280219Swpaul	rseg += sect->nbytes;
60380219Swpaul	if (rseg >= sizeof(tc990image)) {
60480219Swpaul		device_printf(sc->sc_dev, "fw truncated section %d\n",
60580219Swpaul		    sectnum);
60680219Swpaul		return (-1);
60780219Swpaul	}
60880219Swpaul
60980219Swpaul	bcopy(((u_int8_t *)sect) + sizeof(*sect), sc->sc_fwbuf, sect->nbytes);
61080219Swpaul	dma = vtophys(sc->sc_fwbuf);
61180219Swpaul
61280219Swpaul	/*
61380219Swpaul	 * dummy up mbuf and verify section checksum
61480219Swpaul	 */
61580219Swpaul	m.m_type = MT_DATA;
61680219Swpaul	m.m_next = m.m_nextpkt = NULL;
61780219Swpaul	m.m_len = sect->nbytes;
61880219Swpaul	m.m_data = sc->sc_fwbuf;
61980219Swpaul	m.m_flags = 0;
62080219Swpaul	csum = in_cksum(&m, sect->nbytes);
62180219Swpaul	if (csum != sect->cksum) {
62280219Swpaul		device_printf(sc->sc_dev, "fw section %d, bad "
62380219Swpaul		    "cksum (expected 0x%x got 0x%x)\n",
62480219Swpaul		    sectnum, sect->cksum, csum);
62580219Swpaul		err = -1;
62680219Swpaul		goto bail;
62780219Swpaul	}
62880219Swpaul
62980219Swpaul	WRITE_REG(sc, TXP_H2A_1, sect->nbytes);
63080219Swpaul	WRITE_REG(sc, TXP_H2A_2, sect->cksum);
63180219Swpaul	WRITE_REG(sc, TXP_H2A_3, sect->addr);
63280219Swpaul	WRITE_REG(sc, TXP_H2A_4, 0);
63380219Swpaul	WRITE_REG(sc, TXP_H2A_5, dma & 0xffffffff);
63480219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
63580219Swpaul
63680219Swpaul	if (txp_download_fw_wait(sc)) {
63780219Swpaul		device_printf(sc->sc_dev, "fw wait failed, "
63880219Swpaul		    "section %d\n", sectnum);
63980219Swpaul		err = -1;
64080219Swpaul	}
64180219Swpaul
64280219Swpaulbail:
64380219Swpaul	return (err);
64480219Swpaul}
64580219Swpaul
64680219Swpaulstatic void
64780219Swpaultxp_intr(vsc)
64880219Swpaul	void *vsc;
64980219Swpaul{
65080219Swpaul	struct txp_softc *sc = vsc;
65180219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
65280219Swpaul	u_int32_t isr;
65380219Swpaul
65480219Swpaul	/* mask all interrupts */
65580219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_RESERVED | TXP_INT_SELF |
65680219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
65780219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
65880219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
65980219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
66080219Swpaul
66180219Swpaul	isr = READ_REG(sc, TXP_ISR);
66280219Swpaul	while (isr) {
66380219Swpaul		WRITE_REG(sc, TXP_ISR, isr);
66480219Swpaul
66580219Swpaul		if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
66680219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxhir);
66780219Swpaul		if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
66880219Swpaul			txp_rx_reclaim(sc, &sc->sc_rxlor);
66980219Swpaul
67080219Swpaul		if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
67180219Swpaul			txp_rxbuf_reclaim(sc);
67280219Swpaul
67380219Swpaul		if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
67480219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txhir.r_off))))
67580219Swpaul			txp_tx_reclaim(sc, &sc->sc_txhir);
67680219Swpaul
67780219Swpaul		if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
67880219Swpaul		    TXP_OFFSET2IDX(*(sc->sc_txlor.r_off))))
67980219Swpaul			txp_tx_reclaim(sc, &sc->sc_txlor);
68080219Swpaul
68180219Swpaul		isr = READ_REG(sc, TXP_ISR);
68280219Swpaul	}
68380219Swpaul
68480219Swpaul	/* unmask all interrupts */
68580219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
68680219Swpaul
68780219Swpaul	txp_start(&sc->sc_arpcom.ac_if);
68880219Swpaul
68980219Swpaul	return;
69080219Swpaul}
69180219Swpaul
69280219Swpaulstatic void
69380219Swpaultxp_rx_reclaim(sc, r)
69480219Swpaul	struct txp_softc *sc;
69580219Swpaul	struct txp_rx_ring *r;
69680219Swpaul{
69780219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
69880219Swpaul	struct txp_rx_desc *rxd;
69980219Swpaul	struct mbuf *m;
70080219Swpaul	struct txp_swdesc *sd = NULL;
70180219Swpaul	u_int32_t roff, woff;
70280219Swpaul
70380219Swpaul	roff = *r->r_roff;
70480219Swpaul	woff = *r->r_woff;
70580219Swpaul	rxd = r->r_desc + (roff / sizeof(struct txp_rx_desc));
70680219Swpaul
70780219Swpaul	while (roff != woff) {
70880219Swpaul
70980219Swpaul		if (rxd->rx_flags & RX_FLAGS_ERROR) {
71080219Swpaul			device_printf(sc->sc_dev, "error 0x%x\n",
71180219Swpaul			    rxd->rx_stat);
71280219Swpaul			ifp->if_ierrors++;
71380219Swpaul			goto next;
71480219Swpaul		}
71580219Swpaul
71680219Swpaul		/* retrieve stashed pointer */
71780219Swpaul		sd = rxd->rx_sd;
71880219Swpaul
71980219Swpaul		m = sd->sd_mbuf;
72080219Swpaul		sd->sd_mbuf = NULL;
72180219Swpaul
72280219Swpaul		m->m_pkthdr.len = m->m_len = rxd->rx_len;
72380219Swpaul
72480219Swpaul#ifdef __STRICT_ALIGNMENT
72580219Swpaul		{
72680219Swpaul			/*
72780219Swpaul			 * XXX Nice chip, except it won't accept "off by 2"
72880219Swpaul			 * buffers, so we're force to copy.  Supposedly
72980219Swpaul			 * this will be fixed in a newer firmware rev
73080219Swpaul			 * and this will be temporary.
73180219Swpaul			 */
73280219Swpaul			struct mbuf *mnew;
73380219Swpaul
734111119Simp			MGETHDR(mnew, M_DONTWAIT, MT_DATA);
73580219Swpaul			if (mnew == NULL) {
73680219Swpaul				m_freem(m);
73780219Swpaul				goto next;
73880219Swpaul			}
73980219Swpaul			if (m->m_len > (MHLEN - 2)) {
740111119Simp				MCLGET(mnew, M_DONTWAIT);
74180219Swpaul				if (!(mnew->m_flags & M_EXT)) {
74280219Swpaul					m_freem(mnew);
74380219Swpaul					m_freem(m);
74480219Swpaul					goto next;
74580219Swpaul				}
74680219Swpaul			}
74780219Swpaul			mnew->m_pkthdr.rcvif = ifp;
74880219Swpaul			m_adj(mnew, 2);
74980219Swpaul			mnew->m_pkthdr.len = mnew->m_len = m->m_len;
75080219Swpaul			m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, caddr_t));
75180219Swpaul			m_freem(m);
75280219Swpaul			m = mnew;
75380219Swpaul		}
75480219Swpaul#endif
75580219Swpaul
75680219Swpaul		if (rxd->rx_stat & RX_STAT_IPCKSUMBAD)
75780219Swpaul			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
75880219Swpaul		else if (rxd->rx_stat & RX_STAT_IPCKSUMGOOD)
75980219Swpaul		 	m->m_pkthdr.csum_flags |=
76080219Swpaul			    CSUM_IP_CHECKED|CSUM_IP_VALID;
76180219Swpaul
76280219Swpaul		if ((rxd->rx_stat & RX_STAT_TCPCKSUMGOOD) ||
76380219Swpaul		    (rxd->rx_stat & RX_STAT_UDPCKSUMGOOD)) {
76480219Swpaul			m->m_pkthdr.csum_flags |=
76580219Swpaul			    CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
76680219Swpaul			m->m_pkthdr.csum_data = 0xffff;
76780219Swpaul		}
76880219Swpaul
76980219Swpaul		if (rxd->rx_stat & RX_STAT_VLAN) {
770106937Ssam			VLAN_INPUT_TAG(ifp,
771106937Ssam				m, htons(rxd->rx_vlan >> 16), goto next);
77280219Swpaul		}
77383115Sbrooks
774106937Ssam		(*ifp->if_input)(ifp, m);
77580219Swpaul
77680219Swpaulnext:
77780219Swpaul
77880219Swpaul		roff += sizeof(struct txp_rx_desc);
77980219Swpaul		if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
78080219Swpaul			roff = 0;
78180219Swpaul			rxd = r->r_desc;
78280219Swpaul		} else
78380219Swpaul			rxd++;
78480219Swpaul		woff = *r->r_woff;
78580219Swpaul	}
78680219Swpaul
78780219Swpaul	*r->r_roff = woff;
78880219Swpaul
78980219Swpaul	return;
79080219Swpaul}
79180219Swpaul
79280219Swpaulstatic void
79380219Swpaultxp_rxbuf_reclaim(sc)
79480219Swpaul	struct txp_softc *sc;
79580219Swpaul{
79680219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
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
80280219Swpaul	if (!(ifp->if_flags & IFF_RUNNING))
80380219Swpaul		return;
80480219Swpaul
80580219Swpaul	i = sc->sc_rxbufprod;
80680219Swpaul	rbd = sc->sc_rxbufs + i;
80780219Swpaul
80880219Swpaul	while (1) {
80980219Swpaul		sd = rbd->rb_sd;
81080219Swpaul		if (sd->sd_mbuf != NULL)
81180219Swpaul			break;
81280219Swpaul
813111119Simp		MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA);
81480219Swpaul		if (sd->sd_mbuf == NULL)
81580219Swpaul			goto err_sd;
81680219Swpaul
817111119Simp		MCLGET(sd->sd_mbuf, M_DONTWAIT);
81880219Swpaul		if ((sd->sd_mbuf->m_flags & M_EXT) == 0)
81980219Swpaul			goto err_mbuf;
82080219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
82180219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
82280219Swpaul
82380219Swpaul		rbd->rb_paddrlo = vtophys(mtod(sd->sd_mbuf, vm_offset_t))
82480219Swpaul		    & 0xffffffff;
82580219Swpaul		rbd->rb_paddrhi = 0;
82680219Swpaul
82780219Swpaul		hv->hv_rx_buf_write_idx = TXP_IDX2OFFSET(i);
82880219Swpaul
82980219Swpaul		if (++i == RXBUF_ENTRIES) {
83080219Swpaul			i = 0;
83180219Swpaul			rbd = sc->sc_rxbufs;
83280219Swpaul		} else
83380219Swpaul			rbd++;
83480219Swpaul	}
83580219Swpaul
83680219Swpaul	sc->sc_rxbufprod = i;
83780219Swpaul
83880219Swpaul	return;
83980219Swpaul
84080219Swpaulerr_mbuf:
84180219Swpaul	m_freem(sd->sd_mbuf);
84280219Swpaulerr_sd:
84380219Swpaul	free(sd, M_DEVBUF);
84480219Swpaul}
84580219Swpaul
84680219Swpaul/*
84780219Swpaul * Reclaim mbufs and entries from a transmit ring.
84880219Swpaul */
84980219Swpaulstatic void
85080219Swpaultxp_tx_reclaim(sc, r)
85180219Swpaul	struct txp_softc *sc;
85280219Swpaul	struct txp_tx_ring *r;
85380219Swpaul{
85480219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
85580219Swpaul	u_int32_t idx = TXP_OFFSET2IDX(*(r->r_off));
85680219Swpaul	u_int32_t cons = r->r_cons, cnt = r->r_cnt;
85780219Swpaul	struct txp_tx_desc *txd = r->r_desc + cons;
85880219Swpaul	struct txp_swdesc *sd = sc->sc_txd + cons;
85980219Swpaul	struct mbuf *m;
86080219Swpaul
86180219Swpaul	while (cons != idx) {
86280219Swpaul		if (cnt == 0)
86380219Swpaul			break;
86480219Swpaul
86580219Swpaul		if ((txd->tx_flags & TX_FLAGS_TYPE_M) ==
86680219Swpaul		    TX_FLAGS_TYPE_DATA) {
86780219Swpaul			m = sd->sd_mbuf;
86880219Swpaul			if (m != NULL) {
86980219Swpaul				m_freem(m);
87080219Swpaul				txd->tx_addrlo = 0;
87180219Swpaul				txd->tx_addrhi = 0;
87280219Swpaul				ifp->if_opackets++;
87380219Swpaul			}
87480219Swpaul		}
87580219Swpaul		ifp->if_flags &= ~IFF_OACTIVE;
87680219Swpaul
87780219Swpaul		if (++cons == TX_ENTRIES) {
87880219Swpaul			txd = r->r_desc;
87980219Swpaul			cons = 0;
88080219Swpaul			sd = sc->sc_txd;
88180219Swpaul		} else {
88280219Swpaul			txd++;
88380219Swpaul			sd++;
88480219Swpaul		}
88580219Swpaul
88680219Swpaul		cnt--;
88780219Swpaul	}
88880219Swpaul
88980219Swpaul	r->r_cons = cons;
89080219Swpaul	r->r_cnt = cnt;
89180219Swpaul	if (cnt == 0)
89280219Swpaul		ifp->if_timer = 0;
89380219Swpaul}
89480219Swpaul
89580219Swpaulstatic int
89680219Swpaultxp_shutdown(dev)
89780219Swpaul	device_t dev;
89880219Swpaul{
89980219Swpaul	struct txp_softc *sc;
90080219Swpaul
90180219Swpaul	sc = device_get_softc(dev);
90280219Swpaul
90380219Swpaul	/* mask all interrupts */
90480219Swpaul	WRITE_REG(sc, TXP_IMR,
90580219Swpaul	    TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
90680219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
90780219Swpaul	    TXP_INT_LATCH);
90880219Swpaul
90980219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
91080219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
91180219Swpaul	txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 0);
91280219Swpaul
91380219Swpaul	return(0);
91480219Swpaul}
91580219Swpaul
91680219Swpaulstatic int
91780219Swpaultxp_alloc_rings(sc)
91880219Swpaul	struct txp_softc *sc;
91980219Swpaul{
92080219Swpaul	struct txp_boot_record *boot;
92180219Swpaul	struct txp_ldata *ld;
92280219Swpaul	u_int32_t r;
92380219Swpaul	int i;
92480219Swpaul
92592643Sjeff	r = 0;
92680219Swpaul	ld = sc->sc_ldata;
92780219Swpaul	boot = &ld->txp_boot;
92880219Swpaul
92980219Swpaul	/* boot record */
93080219Swpaul	sc->sc_boot = boot;
93180219Swpaul
93280219Swpaul	/* host variables */
93380219Swpaul	bzero(&ld->txp_hostvar, sizeof(struct txp_hostvar));
93480219Swpaul	boot->br_hostvar_lo = vtophys(&ld->txp_hostvar);
93580219Swpaul	boot->br_hostvar_hi = 0;
93680219Swpaul	sc->sc_hostvar = (struct txp_hostvar *)&ld->txp_hostvar;
93780219Swpaul
93880219Swpaul	/* hi priority tx ring */
93980219Swpaul	boot->br_txhipri_lo = vtophys(&ld->txp_txhiring);;
94080219Swpaul	boot->br_txhipri_hi = 0;
94180219Swpaul	boot->br_txhipri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
94280219Swpaul	sc->sc_txhir.r_reg = TXP_H2A_1;
94380219Swpaul	sc->sc_txhir.r_desc = (struct txp_tx_desc *)&ld->txp_txhiring;
94480219Swpaul	sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
94580219Swpaul	sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
94680219Swpaul
94780219Swpaul	/* lo priority tx ring */
94880219Swpaul	boot->br_txlopri_lo = vtophys(&ld->txp_txloring);
94980219Swpaul	boot->br_txlopri_hi = 0;
95080219Swpaul	boot->br_txlopri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc);
95180219Swpaul	sc->sc_txlor.r_reg = TXP_H2A_3;
95280219Swpaul	sc->sc_txlor.r_desc = (struct txp_tx_desc *)&ld->txp_txloring;
95380219Swpaul	sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
95480219Swpaul	sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
95580219Swpaul
95680219Swpaul	/* high priority rx ring */
95780219Swpaul	boot->br_rxhipri_lo = vtophys(&ld->txp_rxhiring);
95880219Swpaul	boot->br_rxhipri_hi = 0;
95980219Swpaul	boot->br_rxhipri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
96080219Swpaul	sc->sc_rxhir.r_desc = (struct txp_rx_desc *)&ld->txp_rxhiring;
96180219Swpaul	sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
96280219Swpaul	sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
96380219Swpaul
96480219Swpaul	/* low priority rx ring */
96580219Swpaul	boot->br_rxlopri_lo = vtophys(&ld->txp_rxloring);
96680219Swpaul	boot->br_rxlopri_hi = 0;
96780219Swpaul	boot->br_rxlopri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc);
96880219Swpaul	sc->sc_rxlor.r_desc = (struct txp_rx_desc *)&ld->txp_rxloring;
96980219Swpaul	sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
97080219Swpaul	sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
97180219Swpaul
97280219Swpaul	/* command ring */
97380219Swpaul	bzero(&ld->txp_cmdring, sizeof(struct txp_cmd_desc) * CMD_ENTRIES);
97480219Swpaul	boot->br_cmd_lo = vtophys(&ld->txp_cmdring);
97580219Swpaul	boot->br_cmd_hi = 0;
97680219Swpaul	boot->br_cmd_siz = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
97780219Swpaul	sc->sc_cmdring.base = (struct txp_cmd_desc *)&ld->txp_cmdring;
97880219Swpaul	sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
97980219Swpaul	sc->sc_cmdring.lastwrite = 0;
98080219Swpaul
98180219Swpaul	/* response ring */
98280219Swpaul	bzero(&ld->txp_rspring, sizeof(struct txp_rsp_desc) * RSP_ENTRIES);
98380219Swpaul	boot->br_resp_lo = vtophys(&ld->txp_rspring);
98480219Swpaul	boot->br_resp_hi = 0;
98580219Swpaul	boot->br_resp_siz = CMD_ENTRIES * sizeof(struct txp_rsp_desc);
98680219Swpaul	sc->sc_rspring.base = (struct txp_rsp_desc *)&ld->txp_rspring;
98780219Swpaul	sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
98880219Swpaul	sc->sc_rspring.lastwrite = 0;
98980219Swpaul
99080219Swpaul	/* receive buffer ring */
99180219Swpaul	boot->br_rxbuf_lo = vtophys(&ld->txp_rxbufs);
99280219Swpaul	boot->br_rxbuf_hi = 0;
99380219Swpaul	boot->br_rxbuf_siz = RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc);
99480219Swpaul	sc->sc_rxbufs = (struct txp_rxbuf_desc *)&ld->txp_rxbufs;
99580219Swpaul
99680219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
99780457Swpaul		struct txp_swdesc *sd;
99880219Swpaul		if (sc->sc_rxbufs[i].rb_sd != NULL)
99980219Swpaul			continue;
100080219Swpaul		sc->sc_rxbufs[i].rb_sd = malloc(sizeof(struct txp_swdesc),
100180219Swpaul		    M_DEVBUF, M_NOWAIT);
100280219Swpaul		if (sc->sc_rxbufs[i].rb_sd == NULL)
100380219Swpaul			return(ENOBUFS);
100480457Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
100580457Swpaul		sd->sd_mbuf = NULL;
100680219Swpaul	}
100780219Swpaul	sc->sc_rxbufprod = 0;
100880219Swpaul
100980219Swpaul	/* zero dma */
101080219Swpaul	bzero(&ld->txp_zero, sizeof(u_int32_t));
101180219Swpaul	boot->br_zero_lo = vtophys(&ld->txp_zero);
101280219Swpaul	boot->br_zero_hi = 0;
101380219Swpaul
101480219Swpaul	/* See if it's waiting for boot, and try to boot it */
101580219Swpaul	for (i = 0; i < 10000; i++) {
101680219Swpaul		r = READ_REG(sc, TXP_A2H_0);
101780219Swpaul		if (r == STAT_WAITING_FOR_BOOT)
101880219Swpaul			break;
101980219Swpaul		DELAY(50);
102080219Swpaul	}
102180219Swpaul
102280219Swpaul	if (r != STAT_WAITING_FOR_BOOT) {
102380219Swpaul		device_printf(sc->sc_dev, "not waiting for boot\n");
102480455Swpaul		return(ENXIO);
102580219Swpaul	}
102680219Swpaul
102780219Swpaul	WRITE_REG(sc, TXP_H2A_2, 0);
102880219Swpaul	WRITE_REG(sc, TXP_H2A_1, vtophys(sc->sc_boot));
102980219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
103080219Swpaul
103180219Swpaul	/* See if it booted */
103280219Swpaul	for (i = 0; i < 10000; i++) {
103380219Swpaul		r = READ_REG(sc, TXP_A2H_0);
103480219Swpaul		if (r == STAT_RUNNING)
103580219Swpaul			break;
103680219Swpaul		DELAY(50);
103780219Swpaul	}
103880219Swpaul	if (r != STAT_RUNNING) {
103980219Swpaul		device_printf(sc->sc_dev, "fw not running\n");
104080219Swpaul		return(ENXIO);
104180219Swpaul	}
104280219Swpaul
104380219Swpaul	/* Clear TX and CMD ring write registers */
104480219Swpaul	WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
104580219Swpaul	WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
104680219Swpaul	WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
104780219Swpaul	WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
104880219Swpaul
104980219Swpaul	return (0);
105080219Swpaul}
105180219Swpaul
105280219Swpaulstatic int
105380219Swpaultxp_ioctl(ifp, command, data)
105480219Swpaul	struct ifnet *ifp;
105580219Swpaul	u_long command;
105680219Swpaul	caddr_t data;
105780219Swpaul{
105880219Swpaul	struct txp_softc *sc = ifp->if_softc;
105980219Swpaul	struct ifreq *ifr = (struct ifreq *)data;
106080219Swpaul	int s, error = 0;
106180219Swpaul
106280219Swpaul	s = splnet();
106380219Swpaul
106480219Swpaul	switch(command) {
106580219Swpaul	case SIOCSIFFLAGS:
106680219Swpaul		if (ifp->if_flags & IFF_UP) {
106780219Swpaul			txp_init(sc);
106880219Swpaul		} else {
106980219Swpaul			if (ifp->if_flags & IFF_RUNNING)
107080219Swpaul				txp_stop(sc);
107180219Swpaul		}
107280219Swpaul		break;
107380219Swpaul	case SIOCADDMULTI:
107480219Swpaul	case SIOCDELMULTI:
107580219Swpaul		/*
107680219Swpaul		 * Multicast list has changed; set the hardware
107780219Swpaul		 * filter accordingly.
107880219Swpaul		 */
107980219Swpaul		txp_set_filter(sc);
108080219Swpaul		error = 0;
108180219Swpaul		break;
108280219Swpaul	case SIOCGIFMEDIA:
108380219Swpaul	case SIOCSIFMEDIA:
108480219Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
108580219Swpaul		break;
108680219Swpaul	default:
1087106937Ssam		error = ether_ioctl(ifp, command, data);
108880219Swpaul		break;
108980219Swpaul	}
109080219Swpaul
109180219Swpaul	(void)splx(s);
109280219Swpaul
109380219Swpaul	return(error);
109480219Swpaul}
109580219Swpaul
109680219Swpaulstatic int
109780219Swpaultxp_rxring_fill(sc)
109880219Swpaul	struct txp_softc *sc;
109980219Swpaul{
110080219Swpaul	int i;
110180219Swpaul	struct ifnet *ifp;
110280219Swpaul	struct txp_swdesc *sd;
110380219Swpaul
110480219Swpaul	ifp = &sc->sc_arpcom.ac_if;
110580219Swpaul
110680219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
110780219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
1108111119Simp		MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA);
110980219Swpaul		if (sd->sd_mbuf == NULL)
111080219Swpaul			return(ENOBUFS);
111180219Swpaul
1112111119Simp		MCLGET(sd->sd_mbuf, M_DONTWAIT);
111380219Swpaul		if ((sd->sd_mbuf->m_flags & M_EXT) == 0) {
111480219Swpaul			m_freem(sd->sd_mbuf);
111580219Swpaul			return(ENOBUFS);
111680219Swpaul		}
111780219Swpaul		sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
111880219Swpaul		sd->sd_mbuf->m_pkthdr.rcvif = ifp;
111980219Swpaul
112080219Swpaul		sc->sc_rxbufs[i].rb_paddrlo =
112180219Swpaul		    vtophys(mtod(sd->sd_mbuf, vm_offset_t));
112280219Swpaul		sc->sc_rxbufs[i].rb_paddrhi = 0;
112380219Swpaul	}
112480219Swpaul
112580219Swpaul	sc->sc_hostvar->hv_rx_buf_write_idx = (RXBUF_ENTRIES - 1) *
112680219Swpaul	    sizeof(struct txp_rxbuf_desc);
112780219Swpaul
112880219Swpaul	return(0);
112980219Swpaul}
113080219Swpaul
113180219Swpaulstatic void
113280219Swpaultxp_rxring_empty(sc)
113380219Swpaul	struct txp_softc *sc;
113480219Swpaul{
113580219Swpaul	int i;
113680219Swpaul	struct txp_swdesc *sd;
113780219Swpaul
113880219Swpaul	if (sc->sc_rxbufs == NULL)
113980219Swpaul		return;
114080219Swpaul
114180219Swpaul	for (i = 0; i < RXBUF_ENTRIES; i++) {
114280219Swpaul		if (&sc->sc_rxbufs[i] == NULL)
114380219Swpaul			continue;
114480219Swpaul		sd = sc->sc_rxbufs[i].rb_sd;
114580219Swpaul		if (sd == NULL)
114680219Swpaul			continue;
114780219Swpaul		if (sd->sd_mbuf != NULL) {
114880219Swpaul			m_freem(sd->sd_mbuf);
114980219Swpaul			sd->sd_mbuf = NULL;
115080219Swpaul		}
115180219Swpaul	}
115280219Swpaul
115380219Swpaul	return;
115480219Swpaul}
115580219Swpaul
115680219Swpaulstatic void
115780219Swpaultxp_init(xsc)
115880219Swpaul	void *xsc;
115980219Swpaul{
116080219Swpaul	struct txp_softc *sc;
116180219Swpaul	struct ifnet *ifp;
116280219Swpaul	u_int16_t p1;
116380219Swpaul	u_int32_t p2;
116480219Swpaul	int s;
116580219Swpaul
116680219Swpaul	sc = xsc;
116780219Swpaul	ifp = &sc->sc_arpcom.ac_if;
116880219Swpaul
116980219Swpaul	if (ifp->if_flags & IFF_RUNNING)
117080219Swpaul		return;
117180219Swpaul
117280219Swpaul	txp_stop(sc);
117380219Swpaul
117480219Swpaul	s = splnet();
117580219Swpaul
117680219Swpaul	txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
117780219Swpaul	    NULL, NULL, NULL, 1);
117880219Swpaul
117980219Swpaul	/* Set station address. */
118080219Swpaul	((u_int8_t *)&p1)[1] = sc->sc_arpcom.ac_enaddr[0];
118180219Swpaul	((u_int8_t *)&p1)[0] = sc->sc_arpcom.ac_enaddr[1];
118280219Swpaul	((u_int8_t *)&p2)[3] = sc->sc_arpcom.ac_enaddr[2];
118380219Swpaul	((u_int8_t *)&p2)[2] = sc->sc_arpcom.ac_enaddr[3];
118480219Swpaul	((u_int8_t *)&p2)[1] = sc->sc_arpcom.ac_enaddr[4];
118580219Swpaul	((u_int8_t *)&p2)[0] = sc->sc_arpcom.ac_enaddr[5];
118680219Swpaul	txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0,
118780219Swpaul	    NULL, NULL, NULL, 1);
118880219Swpaul
118980219Swpaul	txp_set_filter(sc);
119080219Swpaul
119180219Swpaul	txp_rxring_fill(sc);
119280219Swpaul
119380219Swpaul	txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
119480219Swpaul	txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
119580219Swpaul
119680219Swpaul	WRITE_REG(sc, TXP_IER, TXP_INT_RESERVED | TXP_INT_SELF |
119780219Swpaul	    TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
119880219Swpaul	    TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
119980219Swpaul	    TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
120080219Swpaul	    TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |  TXP_INT_LATCH);
120180219Swpaul	WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
120280219Swpaul
120380219Swpaul	ifp->if_flags |= IFF_RUNNING;
120480219Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
120580219Swpaul	ifp->if_timer = 0;
120680219Swpaul
120780219Swpaul	sc->sc_tick = timeout(txp_tick, sc, hz);
120880219Swpaul
120980219Swpaul	splx(s);
121080219Swpaul}
121180219Swpaul
121280219Swpaulstatic void
121380219Swpaultxp_tick(vsc)
121480219Swpaul	void *vsc;
121580219Swpaul{
121680219Swpaul	struct txp_softc *sc = vsc;
121780219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
121880219Swpaul	struct txp_rsp_desc *rsp = NULL;
121980219Swpaul	struct txp_ext_desc *ext;
122080219Swpaul	int s;
122180219Swpaul
122280219Swpaul	s = splnet();
122380219Swpaul	txp_rxbuf_reclaim(sc);
122480219Swpaul
122580219Swpaul	if (txp_command2(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
122680219Swpaul	    &rsp, 1))
122780219Swpaul		goto out;
122880219Swpaul	if (rsp->rsp_numdesc != 6)
122980219Swpaul		goto out;
123080219Swpaul	if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
123180219Swpaul	    NULL, NULL, NULL, 1))
123280219Swpaul		goto out;
123380219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
123480219Swpaul
123580219Swpaul	ifp->if_ierrors += ext[3].ext_2 + ext[3].ext_3 + ext[3].ext_4 +
123680219Swpaul	    ext[4].ext_1 + ext[4].ext_4;
123780219Swpaul	ifp->if_oerrors += ext[0].ext_1 + ext[1].ext_1 + ext[1].ext_4 +
123880219Swpaul	    ext[2].ext_1;
123980219Swpaul	ifp->if_collisions += ext[0].ext_2 + ext[0].ext_3 + ext[1].ext_2 +
124080219Swpaul	    ext[1].ext_3;
124180219Swpaul	ifp->if_opackets += rsp->rsp_par2;
124280219Swpaul	ifp->if_ipackets += ext[2].ext_3;
124380219Swpaul
124480219Swpaulout:
124580219Swpaul	if (rsp != NULL)
124680219Swpaul		free(rsp, M_DEVBUF);
124780219Swpaul
124880219Swpaul	splx(s);
124980219Swpaul	sc->sc_tick = timeout(txp_tick, sc, hz);
125080219Swpaul
125180219Swpaul	return;
125280219Swpaul}
125380219Swpaul
125480219Swpaulstatic void
125580219Swpaultxp_start(ifp)
125680219Swpaul	struct ifnet *ifp;
125780219Swpaul{
125880219Swpaul	struct txp_softc *sc = ifp->if_softc;
125980219Swpaul	struct txp_tx_ring *r = &sc->sc_txhir;
126080219Swpaul	struct txp_tx_desc *txd;
126180219Swpaul	struct txp_frag_desc *fxd;
126280219Swpaul	struct mbuf *m, *m0;
126380219Swpaul	struct txp_swdesc *sd;
126480219Swpaul	u_int32_t firstprod, firstcnt, prod, cnt;
1265106937Ssam	struct m_tag *mtag;
126680219Swpaul
126780219Swpaul	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
126880219Swpaul		return;
126980219Swpaul
127080219Swpaul	prod = r->r_prod;
127180219Swpaul	cnt = r->r_cnt;
127280219Swpaul
127380219Swpaul	while (1) {
127480219Swpaul		IF_DEQUEUE(&ifp->if_snd, m);
127580219Swpaul		if (m == NULL)
127680219Swpaul			break;
127780219Swpaul
127880219Swpaul		firstprod = prod;
127980219Swpaul		firstcnt = cnt;
128080219Swpaul
128180219Swpaul		sd = sc->sc_txd + prod;
128280219Swpaul		sd->sd_mbuf = m;
128380219Swpaul
128480219Swpaul		if ((TX_ENTRIES - cnt) < 4)
128580219Swpaul			goto oactive;
128680219Swpaul
128780219Swpaul		txd = r->r_desc + prod;
128880219Swpaul
128980219Swpaul		txd->tx_flags = TX_FLAGS_TYPE_DATA;
129080219Swpaul		txd->tx_numdesc = 0;
129180219Swpaul		txd->tx_addrlo = 0;
129280219Swpaul		txd->tx_addrhi = 0;
129380219Swpaul		txd->tx_totlen = 0;
129480219Swpaul		txd->tx_pflags = 0;
129580219Swpaul
129680219Swpaul		if (++prod == TX_ENTRIES)
129780219Swpaul			prod = 0;
129880219Swpaul
129980219Swpaul		if (++cnt >= (TX_ENTRIES - 4))
130080219Swpaul			goto oactive;
130180219Swpaul
1302106937Ssam		mtag = VLAN_OUTPUT_TAG(ifp, m);
1303106937Ssam		if (mtag != NULL) {
130480219Swpaul			txd->tx_pflags = TX_PFLAGS_VLAN |
1305106937Ssam			    (htons(VLAN_TAG_VALUE(mtag)) << TX_PFLAGS_VLANTAG_S);
130680219Swpaul		}
130783115Sbrooks
130880219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_IP)
130980219Swpaul			txd->tx_pflags |= TX_PFLAGS_IPCKSUM;
131080219Swpaul
131180219Swpaul#if 0
131280219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_TCP)
131380219Swpaul			txd->tx_pflags |= TX_PFLAGS_TCPCKSUM;
131480219Swpaul		if (m->m_pkthdr.csum_flags & CSUM_UDP)
131580219Swpaul			txd->tx_pflags |= TX_PFLAGS_UDPCKSUM;
131680219Swpaul#endif
131780219Swpaul
131880219Swpaul		fxd = (struct txp_frag_desc *)(r->r_desc + prod);
131980219Swpaul		for (m0 = m; m0 != NULL; m0 = m0->m_next) {
132080219Swpaul			if (m0->m_len == 0)
132180219Swpaul				continue;
132280219Swpaul			if (++cnt >= (TX_ENTRIES - 4))
132380219Swpaul				goto oactive;
132480219Swpaul
132580219Swpaul			txd->tx_numdesc++;
132680219Swpaul
132780219Swpaul			fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG;
132880219Swpaul			fxd->frag_rsvd1 = 0;
132980219Swpaul			fxd->frag_len = m0->m_len;
133080219Swpaul			fxd->frag_addrlo = vtophys(mtod(m0, vm_offset_t));
133180219Swpaul			fxd->frag_addrhi = 0;
133280219Swpaul			fxd->frag_rsvd2 = 0;
133380219Swpaul
133480219Swpaul			if (++prod == TX_ENTRIES) {
133580219Swpaul				fxd = (struct txp_frag_desc *)r->r_desc;
133680219Swpaul				prod = 0;
133780219Swpaul			} else
133880219Swpaul				fxd++;
133980219Swpaul
134080219Swpaul		}
134180219Swpaul
134280219Swpaul		ifp->if_timer = 5;
134380219Swpaul
1344106937Ssam		BPF_MTAP(ifp, m);
134580219Swpaul		WRITE_REG(sc, r->r_reg, TXP_IDX2OFFSET(prod));
134680219Swpaul	}
134780219Swpaul
134880219Swpaul	r->r_prod = prod;
134980219Swpaul	r->r_cnt = cnt;
135080219Swpaul	return;
135180219Swpaul
135280219Swpauloactive:
135380219Swpaul	ifp->if_flags |= IFF_OACTIVE;
135480219Swpaul	r->r_prod = firstprod;
135580219Swpaul	r->r_cnt = firstcnt;
135680219Swpaul	IF_PREPEND(&ifp->if_snd, m);
135780219Swpaul	return;
135880219Swpaul}
135980219Swpaul
136080219Swpaul/*
136180219Swpaul * Handle simple commands sent to the typhoon
136280219Swpaul */
136380219Swpaulstatic int
136480219Swpaultxp_command(sc, id, in1, in2, in3, out1, out2, out3, wait)
136580219Swpaul	struct txp_softc *sc;
136680219Swpaul	u_int16_t id, in1, *out1;
136780219Swpaul	u_int32_t in2, in3, *out2, *out3;
136880219Swpaul	int wait;
136980219Swpaul{
137080219Swpaul	struct txp_rsp_desc *rsp = NULL;
137180219Swpaul
137280219Swpaul	if (txp_command2(sc, id, in1, in2, in3, NULL, 0, &rsp, wait))
137380219Swpaul		return (-1);
137480219Swpaul
137580219Swpaul	if (!wait)
137680219Swpaul		return (0);
137780219Swpaul
137880219Swpaul	if (out1 != NULL)
137980219Swpaul		*out1 = rsp->rsp_par1;
138080219Swpaul	if (out2 != NULL)
138180219Swpaul		*out2 = rsp->rsp_par2;
138280219Swpaul	if (out3 != NULL)
138380219Swpaul		*out3 = rsp->rsp_par3;
138480219Swpaul	free(rsp, M_DEVBUF);
138580219Swpaul	return (0);
138680219Swpaul}
138780219Swpaul
138880219Swpaulstatic int
138980219Swpaultxp_command2(sc, id, in1, in2, in3, in_extp, in_extn, rspp, wait)
139080219Swpaul	struct txp_softc *sc;
139180219Swpaul	u_int16_t id, in1;
139280219Swpaul	u_int32_t in2, in3;
139380219Swpaul	struct txp_ext_desc *in_extp;
139480219Swpaul	u_int8_t in_extn;
139580219Swpaul	struct txp_rsp_desc **rspp;
139680219Swpaul	int wait;
139780219Swpaul{
139880219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
139980219Swpaul	struct txp_cmd_desc *cmd;
140080219Swpaul	struct txp_ext_desc *ext;
140180219Swpaul	u_int32_t idx, i;
140280219Swpaul	u_int16_t seq;
140380219Swpaul
140480219Swpaul	if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
140580219Swpaul		device_printf(sc->sc_dev, "no free cmd descriptors\n");
140680219Swpaul		return (-1);
140780219Swpaul	}
140880219Swpaul
140980219Swpaul	idx = sc->sc_cmdring.lastwrite;
141080219Swpaul	cmd = (struct txp_cmd_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
141180219Swpaul	bzero(cmd, sizeof(*cmd));
141280219Swpaul
141380219Swpaul	cmd->cmd_numdesc = in_extn;
141480219Swpaul	cmd->cmd_seq = seq = sc->sc_seq++;
141580219Swpaul	cmd->cmd_id = id;
141680219Swpaul	cmd->cmd_par1 = in1;
141780219Swpaul	cmd->cmd_par2 = in2;
141880219Swpaul	cmd->cmd_par3 = in3;
141980219Swpaul	cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
142080219Swpaul	    (wait ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
142180219Swpaul
142280219Swpaul	idx += sizeof(struct txp_cmd_desc);
142380219Swpaul	if (idx == sc->sc_cmdring.size)
142480219Swpaul		idx = 0;
142580219Swpaul
142680219Swpaul	for (i = 0; i < in_extn; i++) {
142780219Swpaul		ext = (struct txp_ext_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
142880219Swpaul		bcopy(in_extp, ext, sizeof(struct txp_ext_desc));
142980219Swpaul		in_extp++;
143080219Swpaul		idx += sizeof(struct txp_cmd_desc);
143180219Swpaul		if (idx == sc->sc_cmdring.size)
143280219Swpaul			idx = 0;
143380219Swpaul	}
143480219Swpaul
143580219Swpaul	sc->sc_cmdring.lastwrite = idx;
143680219Swpaul
143780219Swpaul	WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
143880219Swpaul
143980219Swpaul	if (!wait)
144080219Swpaul		return (0);
144180219Swpaul
144280219Swpaul	for (i = 0; i < 10000; i++) {
144380219Swpaul		idx = hv->hv_resp_read_idx;
144480219Swpaul		if (idx != hv->hv_resp_write_idx) {
144580219Swpaul			*rspp = NULL;
144680219Swpaul			if (txp_response(sc, idx, id, seq, rspp))
144780219Swpaul				return (-1);
144880219Swpaul			if (*rspp != NULL)
144980219Swpaul				break;
145080219Swpaul		}
145180219Swpaul		DELAY(50);
145280219Swpaul	}
145380219Swpaul	if (i == 1000 || (*rspp) == NULL) {
145480219Swpaul		device_printf(sc->sc_dev, "0x%x command failed\n", id);
145580219Swpaul		return (-1);
145680219Swpaul	}
145780219Swpaul
145880219Swpaul	return (0);
145980219Swpaul}
146080219Swpaul
146180219Swpaulstatic int
146280219Swpaultxp_response(sc, ridx, id, seq, rspp)
146380219Swpaul	struct txp_softc *sc;
146480219Swpaul	u_int32_t ridx;
146580219Swpaul	u_int16_t id;
146680219Swpaul	u_int16_t seq;
146780219Swpaul	struct txp_rsp_desc **rspp;
146880219Swpaul{
146980219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
147080219Swpaul	struct txp_rsp_desc *rsp;
147180219Swpaul
147280219Swpaul	while (ridx != hv->hv_resp_write_idx) {
147380219Swpaul		rsp = (struct txp_rsp_desc *)(((u_int8_t *)sc->sc_rspring.base) + ridx);
147480219Swpaul
147580219Swpaul		if (id == rsp->rsp_id && rsp->rsp_seq == seq) {
147680219Swpaul			*rspp = (struct txp_rsp_desc *)malloc(
147780219Swpaul			    sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
147880219Swpaul			    M_DEVBUF, M_NOWAIT);
147980219Swpaul			if ((*rspp) == NULL)
148080219Swpaul				return (-1);
148180219Swpaul			txp_rsp_fixup(sc, rsp, *rspp);
148280219Swpaul			return (0);
148380219Swpaul		}
148480219Swpaul
148580219Swpaul		if (rsp->rsp_flags & RSP_FLAGS_ERROR) {
148680219Swpaul			device_printf(sc->sc_dev, "response error!\n");
148780219Swpaul			txp_rsp_fixup(sc, rsp, NULL);
148880219Swpaul			ridx = hv->hv_resp_read_idx;
148980219Swpaul			continue;
149080219Swpaul		}
149180219Swpaul
149280219Swpaul		switch (rsp->rsp_id) {
149380219Swpaul		case TXP_CMD_CYCLE_STATISTICS:
149480219Swpaul		case TXP_CMD_MEDIA_STATUS_READ:
149580219Swpaul			break;
149680219Swpaul		case TXP_CMD_HELLO_RESPONSE:
149780219Swpaul			device_printf(sc->sc_dev, "hello\n");
149880219Swpaul			break;
149980219Swpaul		default:
150080219Swpaul			device_printf(sc->sc_dev, "unknown id(0x%x)\n",
150180219Swpaul			    rsp->rsp_id);
150280219Swpaul		}
150380219Swpaul
150480219Swpaul		txp_rsp_fixup(sc, rsp, NULL);
150580219Swpaul		ridx = hv->hv_resp_read_idx;
150680219Swpaul		hv->hv_resp_read_idx = ridx;
150780219Swpaul	}
150880219Swpaul
150980219Swpaul	return (0);
151080219Swpaul}
151180219Swpaul
151280219Swpaulstatic void
151380219Swpaultxp_rsp_fixup(sc, rsp, dst)
151480219Swpaul	struct txp_softc *sc;
151580219Swpaul	struct txp_rsp_desc *rsp, *dst;
151680219Swpaul{
151780219Swpaul	struct txp_rsp_desc *src = rsp;
151880219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
151980219Swpaul	u_int32_t i, ridx;
152080219Swpaul
152180219Swpaul	ridx = hv->hv_resp_read_idx;
152280219Swpaul
152380219Swpaul	for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
152480219Swpaul		if (dst != NULL)
152580219Swpaul			bcopy(src, dst++, sizeof(struct txp_rsp_desc));
152680219Swpaul		ridx += sizeof(struct txp_rsp_desc);
152780219Swpaul		if (ridx == sc->sc_rspring.size) {
152880219Swpaul			src = sc->sc_rspring.base;
152980219Swpaul			ridx = 0;
153080219Swpaul		} else
153180219Swpaul			src++;
153280219Swpaul		sc->sc_rspring.lastwrite = hv->hv_resp_read_idx = ridx;
153380219Swpaul	}
153480219Swpaul
153580219Swpaul	hv->hv_resp_read_idx = ridx;
153680219Swpaul}
153780219Swpaul
153880219Swpaulstatic int
153980219Swpaultxp_cmd_desc_numfree(sc)
154080219Swpaul	struct txp_softc *sc;
154180219Swpaul{
154280219Swpaul	struct txp_hostvar *hv = sc->sc_hostvar;
154380219Swpaul	struct txp_boot_record *br = sc->sc_boot;
154480219Swpaul	u_int32_t widx, ridx, nfree;
154580219Swpaul
154680219Swpaul	widx = sc->sc_cmdring.lastwrite;
154780219Swpaul	ridx = hv->hv_cmd_read_idx;
154880219Swpaul
154980219Swpaul	if (widx == ridx) {
155080219Swpaul		/* Ring is completely free */
155180219Swpaul		nfree = br->br_cmd_siz - sizeof(struct txp_cmd_desc);
155280219Swpaul	} else {
155380219Swpaul		if (widx > ridx)
155480219Swpaul			nfree = br->br_cmd_siz -
155580219Swpaul			    (widx - ridx + sizeof(struct txp_cmd_desc));
155680219Swpaul		else
155780219Swpaul			nfree = ridx - widx - sizeof(struct txp_cmd_desc);
155880219Swpaul	}
155980219Swpaul
156080219Swpaul	return (nfree / sizeof(struct txp_cmd_desc));
156180219Swpaul}
156280219Swpaul
156380219Swpaulstatic void
156480219Swpaultxp_stop(sc)
156580219Swpaul	struct txp_softc *sc;
156680219Swpaul{
156780219Swpaul	struct ifnet *ifp;
156880219Swpaul
156980219Swpaul	ifp = &sc->sc_arpcom.ac_if;
157080219Swpaul
157180219Swpaul	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
157280219Swpaul
157380219Swpaul	untimeout(txp_tick, sc, sc->sc_tick);
157480219Swpaul
157580219Swpaul	txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
157680219Swpaul	txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
157780219Swpaul
157880219Swpaul	txp_rxring_empty(sc);
157980219Swpaul
158080219Swpaul	return;
158180219Swpaul}
158280219Swpaul
158380219Swpaulstatic void
158480219Swpaultxp_watchdog(ifp)
158580219Swpaul	struct ifnet *ifp;
158680219Swpaul{
158780219Swpaul	return;
158880219Swpaul}
158980219Swpaul
159080219Swpaulstatic int
159180219Swpaultxp_ifmedia_upd(ifp)
159280219Swpaul	struct ifnet *ifp;
159380219Swpaul{
159480219Swpaul	struct txp_softc *sc = ifp->if_softc;
159580219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
159680219Swpaul	u_int16_t new_xcvr;
159780219Swpaul
159880219Swpaul	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
159980219Swpaul		return (EINVAL);
160080219Swpaul
160180219Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
160280219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
160380219Swpaul			new_xcvr = TXP_XCVR_10_FDX;
160480219Swpaul		else
160580219Swpaul			new_xcvr = TXP_XCVR_10_HDX;
160680219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
160780219Swpaul		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
160880219Swpaul			new_xcvr = TXP_XCVR_100_FDX;
160980219Swpaul		else
161080219Swpaul			new_xcvr = TXP_XCVR_100_HDX;
161180219Swpaul	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
161280219Swpaul		new_xcvr = TXP_XCVR_AUTO;
161380219Swpaul	} else
161480219Swpaul		return (EINVAL);
161580219Swpaul
161680219Swpaul	/* nothing to do */
161780219Swpaul	if (sc->sc_xcvr == new_xcvr)
161880219Swpaul		return (0);
161980219Swpaul
162080219Swpaul	txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
162180219Swpaul	    NULL, NULL, NULL, 0);
162280219Swpaul	sc->sc_xcvr = new_xcvr;
162380219Swpaul
162480219Swpaul	return (0);
162580219Swpaul}
162680219Swpaul
162780219Swpaulstatic void
162880219Swpaultxp_ifmedia_sts(ifp, ifmr)
162980219Swpaul	struct ifnet *ifp;
163080219Swpaul	struct ifmediareq *ifmr;
163180219Swpaul{
163280219Swpaul	struct txp_softc *sc = ifp->if_softc;
163380219Swpaul	struct ifmedia *ifm = &sc->sc_ifmedia;
163480219Swpaul	u_int16_t bmsr, bmcr, anlpar;
163580219Swpaul
163680219Swpaul	ifmr->ifm_status = IFM_AVALID;
163780219Swpaul	ifmr->ifm_active = IFM_ETHER;
163880219Swpaul
163980219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
164080219Swpaul	    &bmsr, NULL, NULL, 1))
164180219Swpaul		goto bail;
164280219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
164380219Swpaul	    &bmsr, NULL, NULL, 1))
164480219Swpaul		goto bail;
164580219Swpaul
164680219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
164780219Swpaul	    &bmcr, NULL, NULL, 1))
164880219Swpaul		goto bail;
164980219Swpaul
165080219Swpaul	if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
165180219Swpaul	    &anlpar, NULL, NULL, 1))
165280219Swpaul		goto bail;
165380219Swpaul
165480219Swpaul	if (bmsr & BMSR_LINK)
165580219Swpaul		ifmr->ifm_status |= IFM_ACTIVE;
165680219Swpaul
165780219Swpaul	if (bmcr & BMCR_ISO) {
165880219Swpaul		ifmr->ifm_active |= IFM_NONE;
165980219Swpaul		ifmr->ifm_status = 0;
166080219Swpaul		return;
166180219Swpaul	}
166280219Swpaul
166380219Swpaul	if (bmcr & BMCR_LOOP)
166480219Swpaul		ifmr->ifm_active |= IFM_LOOP;
166580219Swpaul
166680219Swpaul	if (bmcr & BMCR_AUTOEN) {
166780219Swpaul		if ((bmsr & BMSR_ACOMP) == 0) {
166880219Swpaul			ifmr->ifm_active |= IFM_NONE;
166980219Swpaul			return;
167080219Swpaul		}
167180219Swpaul
167280219Swpaul		if (anlpar & ANLPAR_T4)
167380219Swpaul			ifmr->ifm_active |= IFM_100_T4;
167480219Swpaul		else if (anlpar & ANLPAR_TX_FD)
167580219Swpaul			ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
167680219Swpaul		else if (anlpar & ANLPAR_TX)
167780219Swpaul			ifmr->ifm_active |= IFM_100_TX;
167880219Swpaul		else if (anlpar & ANLPAR_10_FD)
167980219Swpaul			ifmr->ifm_active |= IFM_10_T|IFM_FDX;
168080219Swpaul		else if (anlpar & ANLPAR_10)
168180219Swpaul			ifmr->ifm_active |= IFM_10_T;
168280219Swpaul		else
168380219Swpaul			ifmr->ifm_active |= IFM_NONE;
168480219Swpaul	} else
168580219Swpaul		ifmr->ifm_active = ifm->ifm_cur->ifm_media;
168680219Swpaul	return;
168780219Swpaul
168880219Swpaulbail:
168980219Swpaul	ifmr->ifm_active |= IFM_NONE;
169080219Swpaul	ifmr->ifm_status &= ~IFM_AVALID;
169180219Swpaul}
169280219Swpaul
169380219Swpaul#ifdef TXP_DEBUG
169480219Swpaulstatic void
169580219Swpaultxp_show_descriptor(d)
169680219Swpaul	void *d;
169780219Swpaul{
169880219Swpaul	struct txp_cmd_desc *cmd = d;
169980219Swpaul	struct txp_rsp_desc *rsp = d;
170080219Swpaul	struct txp_tx_desc *txd = d;
170180219Swpaul	struct txp_frag_desc *frgd = d;
170280219Swpaul
170380219Swpaul	switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
170480219Swpaul	case CMD_FLAGS_TYPE_CMD:
170580219Swpaul		/* command descriptor */
170680219Swpaul		printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
170780219Swpaul		    cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq,
170880219Swpaul		    cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3);
170980219Swpaul		break;
171080219Swpaul	case CMD_FLAGS_TYPE_RESP:
171180219Swpaul		/* response descriptor */
171280219Swpaul		printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
171380219Swpaul		    rsp->rsp_flags, rsp->rsp_numdesc, rsp->rsp_id, rsp->rsp_seq,
171480219Swpaul		    rsp->rsp_par1, rsp->rsp_par2, rsp->rsp_par3);
171580219Swpaul		break;
171680219Swpaul	case CMD_FLAGS_TYPE_DATA:
171780219Swpaul		/* data header (assuming tx for now) */
171880219Swpaul		printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
171980219Swpaul		    txd->tx_flags, txd->tx_numdesc, txd->tx_totlen,
172080219Swpaul		    txd->tx_addrlo, txd->tx_addrhi, txd->tx_pflags);
172180219Swpaul		break;
172280219Swpaul	case CMD_FLAGS_TYPE_FRAG:
172380219Swpaul		/* fragment descriptor */
172480219Swpaul		printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
172580219Swpaul		    frgd->frag_flags, frgd->frag_rsvd1, frgd->frag_len,
172680219Swpaul		    frgd->frag_addrlo, frgd->frag_addrhi, frgd->frag_rsvd2);
172780219Swpaul		break;
172880219Swpaul	default:
172980219Swpaul		printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
173080219Swpaul		    cmd->cmd_flags & CMD_FLAGS_TYPE_M,
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	}
173580219Swpaul}
173680219Swpaul#endif
173780219Swpaul
173880219Swpaulstatic void
173980219Swpaultxp_set_filter(sc)
174080219Swpaul	struct txp_softc *sc;
174180219Swpaul{
174280219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
174380219Swpaul	u_int32_t crc, carry, hashbit, hash[2];
174480219Swpaul	u_int16_t filter;
174580219Swpaul	u_int8_t octet;
174680219Swpaul	int i, j, mcnt = 0;
174780219Swpaul	struct ifmultiaddr *ifma;
174880219Swpaul	char *enm;
174980219Swpaul
175080219Swpaul	if (ifp->if_flags & IFF_PROMISC) {
175180219Swpaul		filter = TXP_RXFILT_PROMISC;
175280219Swpaul		goto setit;
175380219Swpaul	}
175480219Swpaul
175580219Swpaul	filter = TXP_RXFILT_DIRECT;
175680219Swpaul
175780219Swpaul	if (ifp->if_flags & IFF_BROADCAST)
175880219Swpaul		filter |= TXP_RXFILT_BROADCAST;
175980219Swpaul
176080219Swpaul	if (ifp->if_flags & IFF_ALLMULTI)
176180219Swpaul		filter |= TXP_RXFILT_ALLMULTI;
176280219Swpaul	else {
176380219Swpaul		hash[0] = hash[1] = 0;
176480219Swpaul
176580219Swpaul		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
176680219Swpaul			if (ifma->ifma_addr->sa_family != AF_LINK)
176780219Swpaul				continue;
176880219Swpaul
176980219Swpaul			enm = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
177080219Swpaul			mcnt++;
177180219Swpaul			crc = 0xffffffff;
177280219Swpaul
177380219Swpaul			for (i = 0; i < ETHER_ADDR_LEN; i++) {
177480219Swpaul				octet = enm[i];
177580219Swpaul				for (j = 0; j < 8; j++) {
177680219Swpaul					carry = ((crc & 0x80000000) ? 1 : 0) ^
177780219Swpaul					    (octet & 1);
177880219Swpaul					crc <<= 1;
177980219Swpaul					octet >>= 1;
178080219Swpaul					if (carry)
178180219Swpaul						crc = (crc ^ TXP_POLYNOMIAL) |
178280219Swpaul						    carry;
178380219Swpaul				}
178480219Swpaul			}
178580219Swpaul			hashbit = (u_int16_t)(crc & (64 - 1));
178680219Swpaul			hash[hashbit / 32] |= (1 << hashbit % 32);
178780219Swpaul		}
178880219Swpaul
178980219Swpaul		if (mcnt > 0) {
179080219Swpaul			filter |= TXP_RXFILT_HASHMULTI;
179180219Swpaul			txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE,
179280219Swpaul			    2, hash[0], hash[1], NULL, NULL, NULL, 0);
179380219Swpaul		}
179480219Swpaul	}
179580219Swpaul
179680219Swpaulsetit:
179780219Swpaul
179880219Swpaul	txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
179980219Swpaul	    NULL, NULL, NULL, 1);
180080219Swpaul
180180219Swpaul	return;
180280219Swpaul}
180380219Swpaul
180480219Swpaulstatic void
180580219Swpaultxp_capabilities(sc)
180680219Swpaul	struct txp_softc *sc;
180780219Swpaul{
180880219Swpaul	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
180980219Swpaul	struct txp_rsp_desc *rsp = NULL;
181080219Swpaul	struct txp_ext_desc *ext;
181180219Swpaul
181280219Swpaul	if (txp_command2(sc, TXP_CMD_OFFLOAD_READ, 0, 0, 0, NULL, 0, &rsp, 1))
181380219Swpaul		goto out;
181480219Swpaul
181580219Swpaul	if (rsp->rsp_numdesc != 1)
181680219Swpaul		goto out;
181780219Swpaul	ext = (struct txp_ext_desc *)(rsp + 1);
181880219Swpaul
181980219Swpaul	sc->sc_tx_capability = ext->ext_1 & OFFLOAD_MASK;
182080219Swpaul	sc->sc_rx_capability = ext->ext_2 & OFFLOAD_MASK;
182183631Sjlemon	ifp->if_capabilities = 0;
182280219Swpaul
182380219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_VLAN) {
182480219Swpaul		sc->sc_tx_capability |= OFFLOAD_VLAN;
182580219Swpaul		sc->sc_rx_capability |= OFFLOAD_VLAN;
1826106937Ssam		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
182780219Swpaul	}
182880219Swpaul
182980219Swpaul#if 0
183080219Swpaul	/* not ready yet */
183180219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPSEC) {
183280219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPSEC;
183380219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPSEC;
183480219Swpaul		ifp->if_capabilities |= IFCAP_IPSEC;
183580219Swpaul	}
183680219Swpaul#endif
183780219Swpaul
183880219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPCKSUM) {
183980219Swpaul		sc->sc_tx_capability |= OFFLOAD_IPCKSUM;
184080219Swpaul		sc->sc_rx_capability |= OFFLOAD_IPCKSUM;
184183631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
184280219Swpaul		ifp->if_hwassist |= CSUM_IP;
184380219Swpaul	}
184480219Swpaul
184580219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_TCPCKSUM) {
184680219Swpaul#if 0
184780219Swpaul		sc->sc_tx_capability |= OFFLOAD_TCPCKSUM;
184880219Swpaul#endif
184980219Swpaul		sc->sc_rx_capability |= OFFLOAD_TCPCKSUM;
185083631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
185180219Swpaul	}
185280219Swpaul
185380219Swpaul	if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_UDPCKSUM) {
185480219Swpaul#if 0
185580219Swpaul		sc->sc_tx_capability |= OFFLOAD_UDPCKSUM;
185680219Swpaul#endif
185780219Swpaul		sc->sc_rx_capability |= OFFLOAD_UDPCKSUM;
185883631Sjlemon		ifp->if_capabilities |= IFCAP_HWCSUM;
185980219Swpaul	}
186083631Sjlemon	ifp->if_capenable = ifp->if_capabilities;
186180219Swpaul
186280219Swpaul	if (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0,
186380219Swpaul	    sc->sc_tx_capability, sc->sc_rx_capability, NULL, NULL, NULL, 1))
186480219Swpaul		goto out;
186580219Swpaul
186680219Swpaulout:
186780219Swpaul	if (rsp != NULL)
186880219Swpaul		free(rsp, M_DEVBUF);
186980219Swpaul
187080219Swpaul	return;
187180219Swpaul}
1872