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