From 08feeb2436d8817c951b188d7f86bd0dec292b03 Mon Sep 17 00:00:00 2001 From: wpaul Date: Mon, 23 Jul 2001 20:44:54 +0000 Subject: [PATCH] You were knocked senseless by the Boomerang, spun around by the Cyclone, blown over by the Hurricane and had a house dropped on you by the Tornado. Now it's time to have your parade rained on by... the Typhoon! This commit adds driver support for 3Com 3cR990 10/100 ethernet adapters based on the Typhoon I and Typhoon II chipsets. This is actually a port of the OpenBSD driver with many hacks by me. No Virginia, there isn't any support for the hardware crypto yet. However there is support for TCP/IP checksum offload and VLANs. Special thanks go to Jason Wright, Aaron Campbell and Theo de Raadt for squeezing enough info out of 3Com to get this written, and for doing most of the hard work. Manual page is included. Compiled as a module and included in GENERIC. --- share/man/man4/Makefile | 1 + share/man/man4/txp.4 | 142 +++ sys/alpha/conf/GENERIC | 1 + sys/alpha/conf/NOTES | 1 + sys/amd64/conf/GENERIC | 1 + sys/conf/NOTES | 1 + sys/conf/files | 1 + sys/dev/txp/if_txp.c | 1930 +++++++++++++++++++++++++++++++++ sys/dev/txp/if_txpreg.h | 663 +++++++++++ sys/i386/conf/GENERIC | 1 + sys/i386/conf/NOTES | 1 + sys/modules/Makefile | 1 + sys/modules/txp/Makefile | 12 + usr.sbin/sade/devices.c | 1 + usr.sbin/sysinstall/devices.c | 1 + 15 files changed, 2758 insertions(+) create mode 100644 share/man/man4/txp.4 create mode 100644 sys/dev/txp/if_txp.c create mode 100644 sys/dev/txp/if_txpreg.h create mode 100644 sys/modules/txp/Makefile diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index d42b6f14ed3d..6b3d441091b6 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -160,6 +160,7 @@ MAN= aac.4 \ tty.4 \ tun.4 \ twe.4 \ + txp.4 \ udbp.4 \ udp.4 \ uhci.4 \ diff --git a/share/man/man4/txp.4 b/share/man/man4/txp.4 new file mode 100644 index 000000000000..fc5497b0a5ad --- /dev/null +++ b/share/man/man4/txp.4 @@ -0,0 +1,142 @@ +.\" $OpenBSD: txp.4,v 1.8 2001/06/26 02:09:11 pjanzen Exp $ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2001 Jason L. Wright (jason@thought.net) +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Jason L. Wright +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd April 15, 2001 +.Dt TXP 4 +.Os +.Sh NAME +.Nm txp +.Nd 3Com 3XP Typhoon/Sidewinder (3CR990) Ethernet interface +.Sh SYNOPSIS +.Cd "device txp" +.Sh DESCRIPTION +The +.Nm +interface provides access to the 10Mb/s and 100Mb/s Ethernet networks via the +.Tn 3Com +.Tn Typhoon/Sidewinder +chipset. +This driver supports the following cards: +.Pp +.Bl -bullet -offset indent -compact +.It +3Com 3CR990-TX-95 +.It +3Com 3CR990-TX-97 +.It +3Com 3cR990B-TXM +.It +3Com 3CR990SVR95 +.It +3Com 3CR990SVR97 +.It +3Com 3cR990B-SRV +.El +.Pp +Basic Ethernet functions are provided as well as support for +.Xr vlan 4 +tag removal and insertion assistance, receive +.Xr ip 4 , +.Xr tcp 4 , +and +.Xr udp 4 +checksum offloading, +and +transmit +.Xr ip 4 +checksum offloading. +There is currently no support for +transmit +.Xr tcp 4 +or +.Xr udp 4 +checksum offloading, +.Xr tcp 4 +segmentation, nor +.Xr ipsec 4 +acceleration. +Note that hardware checksumming is only used when the interface is not +in +.Xr bridge 4 +mode. +.Pp +Each of the host's network addresses +is specified at boot time with an +.Dv SIOCSIFADDR +.Xr ioctl 2 . +The +.Nm +interface employs the address resolution protocol described in +.Xr arp 4 +to dynamically map between Internet and Ethernet addresses on the local +network. +.Pp +When a +.Nm +interface is brought up, by default, it will attempt to auto-negotiate the +link speed and duplex mode. The speeds, in order of attempt, are: +100Mb/s Full Duplex, 100Mb/s Half Duplex, 10 Mb/s Full Duplex, and +10 Mb/s Half Duplex. +.Pp +The +.Nm +supports several media types, which are selected via the +.Xr ifconfig 8 +command. +The supported media types are: +.Bl -tag -width xxxxxxxxxxxxxx -offset indent +.It media autoselect +Attempt to autoselect the media type (default) +.It media 100baseTX mediaopt full-duplex +Use 100baseTX, full duplex +.It media 100baseTX Op mediaopt half-duplex +Use 100baseTX, half duplex +.It media 10baseT mediaopt full-duplex +Use 10baseT, full duplex +.It media 10baseT Op mediaopt half-duplex +Use 10baseT, half duplex +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr ifmedia 4 , +.Xr inet 4 , +.Xr intro 4 , +.Xr ip 4 , +.Xr tcp 4 , +.Xr udp 4 , +.Xr vlan 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 2.9 . diff --git a/sys/alpha/conf/GENERIC b/sys/alpha/conf/GENERIC index 984709077472..cff98a2a10ae 100644 --- a/sys/alpha/conf/GENERIC +++ b/sys/alpha/conf/GENERIC @@ -140,6 +140,7 @@ device ppi # Parallel port interface device device de # DEC/Intel DC21x4x (``Tulip'') device le # Lance device vx # 3Com 3c590, 3c595 (``Vortex'') +device txp # 3Com 3cR990 (``Typhoon'') # PCI Ethernet NICs that use the common MII bus controller code. device miibus # MII bus support diff --git a/sys/alpha/conf/NOTES b/sys/alpha/conf/NOTES index 984709077472..cff98a2a10ae 100644 --- a/sys/alpha/conf/NOTES +++ b/sys/alpha/conf/NOTES @@ -140,6 +140,7 @@ device ppi # Parallel port interface device device de # DEC/Intel DC21x4x (``Tulip'') device le # Lance device vx # 3Com 3c590, 3c595 (``Vortex'') +device txp # 3Com 3cR990 (``Typhoon'') # PCI Ethernet NICs that use the common MII bus controller code. device miibus # MII bus support diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 36efa13928c6..74af39beda2b 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -162,6 +162,7 @@ device ppi # Parallel port interface device # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') +device txp # 3Com 3cR990 (``Typhoon'') # PCI Ethernet NICs that use the common MII bus controller code. # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 52de4c85bde2..9fb2a554cbcd 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1710,6 +1710,7 @@ device miibus # in several Compaq Prosignia, Proliant and Deskpro systems. It also # supports several Olicom 10Mbps and 10/100 boards. # tx: SMC 9432 TX, BTX and FTX cards. (SMC EtherPower II serie) +# txp: Support for 3Com 3cR990 cards with the "Typhoon" chipset # vr: Support for various fast ethernet adapters based on the VIA # Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips, # including the D-Link DFE530TX (see 'rl' for DFE530TX+), the Hawking diff --git a/sys/conf/files b/sys/conf/files index 73f2e0bdc2cc..a40327f3c20c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -543,6 +543,7 @@ dev/sym/sym_hipd.c optional sym \ dev/tdfx/tdfx_pci.c optional tdfx pci dev/twe/twe.c optional twe dev/twe/twe_freebsd.c optional twe +dev/txp/if_txp.c optional txp # # USB support dev/usb/usb_if.m optional usb diff --git a/sys/dev/txp/if_txp.c b/sys/dev/txp/if_txp.c new file mode 100644 index 000000000000..74bb5b6bd6a2 --- /dev/null +++ b/sys/dev/txp/if_txp.c @@ -0,0 +1,1930 @@ +/* $OpenBSD: if_txp.c,v 1.48 2001/06/27 06:34:50 kjc Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2001 + * Jason L. Wright , Theo de Raadt, and + * Aaron Campbell . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright, + * Theo de Raadt and Aaron Campbell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Driver for 3c990 (Typhoon) Ethernet ASIC + */ + +#include "vlan.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#if NVLAN > 0 +#include +#endif + +#include /* for vtophys */ +#include /* for vtophys */ +#include /* for DELAY */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TXP_USEIOSPACE +/*#define __STRICT_ALIGNMENT*/ + +#include +#include + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct txp_type txp_devs[] = { + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95, + "3Com 3cR990-TX-95 Etherlink with 3XP Processor" }, + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97, + "3Com 3cR990-TX-97 Etherlink with 3XP Processor" }, + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM, + "3Com 3cR990B-TXM Etherlink with 3XP Processor" }, + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95, + "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" }, + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97, + "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" }, + { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV, + "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" }, + { 0, 0, NULL } +}; + +static int txp_probe __P((device_t)); +static int txp_attach __P((device_t)); +static int txp_detach __P((device_t)); +static void txp_intr __P((void *)); +static void txp_tick __P((void *)); +static int txp_shutdown __P((device_t)); +static int txp_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void txp_start __P((struct ifnet *)); +static void txp_stop __P((struct txp_softc *)); +static void txp_init __P((void *)); +static void txp_watchdog __P((struct ifnet *)); + +static void txp_release_resources __P((struct txp_softc *)); +static int txp_chip_init __P((struct txp_softc *)); +static int txp_reset_adapter __P((struct txp_softc *)); +static int txp_download_fw __P((struct txp_softc *)); +static int txp_download_fw_wait __P((struct txp_softc *)); +static int txp_download_fw_section __P((struct txp_softc *, + struct txp_fw_section_header *, int)); +static int txp_alloc_rings __P((struct txp_softc *)); +static int txp_rxring_fill __P((struct txp_softc *)); +static void txp_rxring_empty __P((struct txp_softc *)); +static void txp_set_filter __P((struct txp_softc *)); + +static int txp_cmd_desc_numfree __P((struct txp_softc *)); +static int txp_command __P((struct txp_softc *, u_int16_t, u_int16_t, u_int32_t, + u_int32_t, u_int16_t *, u_int32_t *, u_int32_t *, int)); +static int txp_command2 __P((struct txp_softc *, u_int16_t, u_int16_t, + u_int32_t, u_int32_t, struct txp_ext_desc *, u_int8_t, + struct txp_rsp_desc **, int)); +static int txp_response __P((struct txp_softc *, u_int32_t, u_int16_t, u_int16_t, + struct txp_rsp_desc **)); +static void txp_rsp_fixup __P((struct txp_softc *, struct txp_rsp_desc *, + struct txp_rsp_desc *)); +static void txp_capabilities __P((struct txp_softc *)); + +static void txp_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); +static int txp_ifmedia_upd __P((struct ifnet *)); +#ifdef TXP_DEBUG +static void txp_show_descriptor __P((void *)); +#endif +static void txp_tx_reclaim __P((struct txp_softc *, struct txp_tx_ring *)); +static void txp_rxbuf_reclaim __P((struct txp_softc *)); +static void txp_rx_reclaim __P((struct txp_softc *, struct txp_rx_ring *)); + +#ifdef TXP_USEIOSPACE +#define TXP_RES SYS_RES_IOPORT +#define TXP_RID TXP_PCI_LOIO +#else +#define TXP_RES SYS_RES_MEMORY +#define TXP_RID TXP_PCI_LOMEM +#endif + +static device_method_t txp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, txp_probe), + DEVMETHOD(device_attach, txp_attach), + DEVMETHOD(device_detach, txp_detach), + DEVMETHOD(device_shutdown, txp_shutdown), + { 0, 0 } +}; + +static driver_t txp_driver = { + "txp", + txp_methods, + sizeof(struct txp_softc) +}; + +static devclass_t txp_devclass; + +DRIVER_MODULE(if_txp, pci, txp_driver, txp_devclass, 0, 0); + +static int +txp_probe(dev) + device_t dev; +{ + struct txp_type *t; + + t = txp_devs; + + while(t->txp_name != NULL) { + if ((pci_get_vendor(dev) == t->txp_vid) && + (pci_get_device(dev) == t->txp_did)) { + device_set_desc(dev, t->txp_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +static int +txp_attach(dev) + device_t dev; +{ + struct txp_softc *sc; + struct ifnet *ifp; + u_int32_t command; + u_int16_t p1; + u_int32_t p2; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + sc->sc_dev = dev; + sc->sc_cold = 1; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_DEF|MTX_RECURSE); + + /* + * Handle power management nonsense. + */ + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, TXP_PCI_LOIO, 4); + membase = pci_read_config(dev, TXP_PCI_LOMEM, 4); + irq = pci_read_config(dev, TXP_PCI_INTLINE, 4); + + /* Reset the power state. */ + device_printf(dev, "chip is in D%d power mode " + "-- setting to D0\n", pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, TXP_PCI_LOIO, iobase, 4); + pci_write_config(dev, TXP_PCI_LOMEM, membase, 4); + pci_write_config(dev, TXP_PCI_INTLINE, irq, 4); + } + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + pci_enable_io(dev, SYS_RES_IOPORT); + pci_enable_io(dev, SYS_RES_MEMORY); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef TXP_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + device_printf(dev, "failed to enable I/O ports!\n"); + error = ENXIO;; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + device_printf(dev, "failed to enable memory mapping!\n"); + error = ENXIO;; + goto fail; + } +#endif + + rid = TXP_RID; + sc->sc_res = bus_alloc_resource(dev, TXP_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->sc_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + + sc->sc_bt = rman_get_bustag(sc->sc_res); + sc->sc_bh = rman_get_bushandle(sc->sc_res); + + /* Allocate interrupt */ + rid = 0; + sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sc_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + txp_release_resources(sc); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET, + txp_intr, sc, &sc->sc_intrhand); + + if (error) { + txp_release_resources(sc); + device_printf(dev, "couldn't set up irq\n"); + goto fail; + } + + if (txp_chip_init(sc)) { + txp_release_resources(sc); + goto fail; + } + + sc->sc_fwbuf = contigmalloc(32768, M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + error = txp_download_fw(sc); + contigfree(sc->sc_fwbuf, 32768, M_DEVBUF); + sc->sc_fwbuf = NULL; + + if (error) { + txp_release_resources(sc); + goto fail; + } + + sc->sc_ldata = contigmalloc(sizeof(struct txp_ldata), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (txp_alloc_rings(sc)) { + txp_release_resources(sc); + goto fail; + } + + if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0, + NULL, NULL, NULL, 1)) { + txp_release_resources(sc); + goto fail; + } + + if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0, + &p1, &p2, NULL, 1)) { + txp_release_resources(sc); + goto fail; + } + + txp_set_filter(sc); + + sc->sc_arpcom.ac_enaddr[0] = ((u_int8_t *)&p1)[1]; + sc->sc_arpcom.ac_enaddr[1] = ((u_int8_t *)&p1)[0]; + sc->sc_arpcom.ac_enaddr[2] = ((u_int8_t *)&p2)[3]; + sc->sc_arpcom.ac_enaddr[3] = ((u_int8_t *)&p2)[2]; + sc->sc_arpcom.ac_enaddr[4] = ((u_int8_t *)&p2)[1]; + sc->sc_arpcom.ac_enaddr[5] = ((u_int8_t *)&p2)[0]; + + printf("txp%d: Ethernet address %6D\n", unit, + sc->sc_arpcom.ac_enaddr, ":"); + + sc->sc_cold = 0; + + ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); + + sc->sc_xcvr = TXP_XCVR_AUTO; + txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0, + NULL, NULL, NULL, 0); + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO); + + ifp = &sc->sc_arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "txp"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = txp_ioctl; + ifp->if_output = ether_output; + ifp->if_start = txp_start; + ifp->if_watchdog = txp_watchdog; + ifp->if_init = txp_init; + ifp->if_baudrate = 100000000; + ifp->if_snd.ifq_maxlen = TX_ENTRIES; + ifp->if_hwassist = 0; + txp_capabilities(sc); + + /* + * Attach us everywhere + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + callout_handle_init(&sc->sc_tick); + return(0); + +fail: + txp_release_resources(sc); + mtx_destroy(&sc->sc_mtx); + return(error); +} + +static int +txp_detach(dev) + device_t dev; +{ + struct txp_softc *sc; + struct ifnet *ifp; + int i; + + sc = device_get_softc(dev); + ifp = &sc->sc_arpcom.ac_if; + + txp_stop(sc); + txp_shutdown(dev); + + ifmedia_removeall(&sc->sc_ifmedia); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + for (i = 0; i < RXBUF_ENTRIES; i++) + free(sc->sc_rxbufs[i].rb_sd, M_DEVBUF); + + txp_release_resources(sc); + + mtx_destroy(&sc->sc_mtx); + return(0); +} + +static void +txp_release_resources(sc) + struct txp_softc *sc; +{ + device_t dev; + + dev = sc->sc_dev; + + if (sc->sc_intrhand != NULL) + bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand); + + if (sc->sc_irq != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq); + + if (sc->sc_res != NULL) + bus_release_resource(dev, TXP_RES, TXP_RID, sc->sc_res); + + if (sc->sc_ldata != NULL) + contigfree(sc->sc_ldata, sizeof(struct txp_ldata), M_DEVBUF); + + return; +} + +static int +txp_chip_init(sc) + struct txp_softc *sc; +{ + /* disable interrupts */ + WRITE_REG(sc, TXP_IER, 0); + WRITE_REG(sc, TXP_IMR, + TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_LATCH); + + /* ack all interrupts */ + WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH | + TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 | + TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0); + + if (txp_reset_adapter(sc)) + return (-1); + + /* disable interrupts */ + WRITE_REG(sc, TXP_IER, 0); + WRITE_REG(sc, TXP_IMR, + TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_LATCH); + + /* ack all interrupts */ + WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH | + TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 | + TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0); + + return (0); +} + +static int +txp_reset_adapter(sc) + struct txp_softc *sc; +{ + u_int32_t r; + int i; + + WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL); + DELAY(1000); + WRITE_REG(sc, TXP_SRR, 0); + + /* Should wait max 6 seconds */ + for (i = 0; i < 6000; i++) { + r = READ_REG(sc, TXP_A2H_0); + if (r == STAT_WAITING_FOR_HOST_REQUEST) + break; + DELAY(1000); + } + + if (r != STAT_WAITING_FOR_HOST_REQUEST) { + device_printf(sc->sc_dev, "reset hung\n"); + return (-1); + } + + return (0); +} + +static int +txp_download_fw(sc) + struct txp_softc *sc; +{ + struct txp_fw_file_header *fileheader; + struct txp_fw_section_header *secthead; + int sect; + u_int32_t r, i, ier, imr; + + ier = READ_REG(sc, TXP_IER); + WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0); + + imr = READ_REG(sc, TXP_IMR); + WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0); + + for (i = 0; i < 10000; i++) { + r = READ_REG(sc, TXP_A2H_0); + if (r == STAT_WAITING_FOR_HOST_REQUEST) + break; + DELAY(50); + } + if (r != STAT_WAITING_FOR_HOST_REQUEST) { + device_printf(sc->sc_dev, "not waiting for host request\n"); + return (-1); + } + + /* Ack the status */ + WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); + + fileheader = (struct txp_fw_file_header *)tc990image; + if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) { + device_printf(sc->sc_dev, "fw invalid magic\n"); + return (-1); + } + + /* Tell boot firmware to get ready for image */ + WRITE_REG(sc, TXP_H2A_1, fileheader->addr); + WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE); + + if (txp_download_fw_wait(sc)) { + device_printf(sc->sc_dev, "fw wait failed, initial\n"); + return (-1); + } + + secthead = (struct txp_fw_section_header *)(((u_int8_t *)tc990image) + + sizeof(struct txp_fw_file_header)); + + for (sect = 0; sect < fileheader->nsections; sect++) { + if (txp_download_fw_section(sc, secthead, sect)) + return (-1); + secthead = (struct txp_fw_section_header *) + (((u_int8_t *)secthead) + secthead->nbytes + + sizeof(*secthead)); + } + + WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE); + + for (i = 0; i < 10000; i++) { + r = READ_REG(sc, TXP_A2H_0); + if (r == STAT_WAITING_FOR_BOOT) + break; + DELAY(50); + } + if (r != STAT_WAITING_FOR_BOOT) { + device_printf(sc->sc_dev, "not waiting for boot\n"); + return (-1); + } + + WRITE_REG(sc, TXP_IER, ier); + WRITE_REG(sc, TXP_IMR, imr); + + return (0); +} + +static int +txp_download_fw_wait(sc) + struct txp_softc *sc; +{ + u_int32_t i, r; + + for (i = 0; i < 10000; i++) { + r = READ_REG(sc, TXP_ISR); + if (r & TXP_INT_A2H_0) + break; + DELAY(50); + } + + if (!(r & TXP_INT_A2H_0)) { + device_printf(sc->sc_dev, "fw wait failed comm0\n"); + return (-1); + } + + WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); + + r = READ_REG(sc, TXP_A2H_0); + if (r != STAT_WAITING_FOR_SEGMENT) { + device_printf(sc->sc_dev, "fw not waiting for segment\n"); + return (-1); + } + return (0); +} + +static int +txp_download_fw_section(sc, sect, sectnum) + struct txp_softc *sc; + struct txp_fw_section_header *sect; + int sectnum; +{ + vm_offset_t dma; + int rseg, err = 0; + struct mbuf m; + u_int16_t csum; + + /* Skip zero length sections */ + if (sect->nbytes == 0) + return (0); + + /* Make sure we aren't past the end of the image */ + rseg = ((u_int8_t *)sect) - ((u_int8_t *)tc990image); + if (rseg >= sizeof(tc990image)) { + device_printf(sc->sc_dev, "fw invalid section address, " + "section %d\n", sectnum); + return (-1); + } + + /* Make sure this section doesn't go past the end */ + rseg += sect->nbytes; + if (rseg >= sizeof(tc990image)) { + device_printf(sc->sc_dev, "fw truncated section %d\n", + sectnum); + return (-1); + } + + bcopy(((u_int8_t *)sect) + sizeof(*sect), sc->sc_fwbuf, sect->nbytes); + dma = vtophys(sc->sc_fwbuf); + + /* + * dummy up mbuf and verify section checksum + */ + m.m_type = MT_DATA; + m.m_next = m.m_nextpkt = NULL; + m.m_len = sect->nbytes; + m.m_data = sc->sc_fwbuf; + m.m_flags = 0; + csum = in_cksum(&m, sect->nbytes); + if (csum != sect->cksum) { + device_printf(sc->sc_dev, "fw section %d, bad " + "cksum (expected 0x%x got 0x%x)\n", + sectnum, sect->cksum, csum); + err = -1; + goto bail; + } + + WRITE_REG(sc, TXP_H2A_1, sect->nbytes); + WRITE_REG(sc, TXP_H2A_2, sect->cksum); + WRITE_REG(sc, TXP_H2A_3, sect->addr); + WRITE_REG(sc, TXP_H2A_4, 0); + WRITE_REG(sc, TXP_H2A_5, dma & 0xffffffff); + WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE); + + if (txp_download_fw_wait(sc)) { + device_printf(sc->sc_dev, "fw wait failed, " + "section %d\n", sectnum); + err = -1; + } + +bail: + return (err); +} + +static void +txp_intr(vsc) + void *vsc; +{ + struct txp_softc *sc = vsc; + struct txp_hostvar *hv = sc->sc_hostvar; + u_int32_t isr; + + /* mask all interrupts */ + WRITE_REG(sc, TXP_IMR, TXP_INT_RESERVED | TXP_INT_SELF | + TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 | + TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | TXP_INT_LATCH); + + isr = READ_REG(sc, TXP_ISR); + while (isr) { + WRITE_REG(sc, TXP_ISR, isr); + + if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff)) + txp_rx_reclaim(sc, &sc->sc_rxhir); + if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff)) + txp_rx_reclaim(sc, &sc->sc_rxlor); + + if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx) + txp_rxbuf_reclaim(sc); + + if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons != + TXP_OFFSET2IDX(*(sc->sc_txhir.r_off)))) + txp_tx_reclaim(sc, &sc->sc_txhir); + + if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons != + TXP_OFFSET2IDX(*(sc->sc_txlor.r_off)))) + txp_tx_reclaim(sc, &sc->sc_txlor); + + isr = READ_REG(sc, TXP_ISR); + } + + /* unmask all interrupts */ + WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3); + + txp_start(&sc->sc_arpcom.ac_if); + + return; +} + +static void +txp_rx_reclaim(sc, r) + struct txp_softc *sc; + struct txp_rx_ring *r; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct txp_rx_desc *rxd; + struct mbuf *m; + struct txp_swdesc *sd = NULL; + u_int32_t roff, woff; + struct ether_header *eh = NULL; + + roff = *r->r_roff; + woff = *r->r_woff; + rxd = r->r_desc + (roff / sizeof(struct txp_rx_desc)); + + while (roff != woff) { + + if (rxd->rx_flags & RX_FLAGS_ERROR) { + device_printf(sc->sc_dev, "error 0x%x\n", + rxd->rx_stat); + ifp->if_ierrors++; + goto next; + } + + /* retrieve stashed pointer */ + sd = rxd->rx_sd; + + m = sd->sd_mbuf; + sd->sd_mbuf = NULL; + + m->m_pkthdr.len = m->m_len = rxd->rx_len; + +#ifdef __STRICT_ALIGNMENT + { + /* + * XXX Nice chip, except it won't accept "off by 2" + * buffers, so we're force to copy. Supposedly + * this will be fixed in a newer firmware rev + * and this will be temporary. + */ + struct mbuf *mnew; + + MGETHDR(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) { + m_freem(m); + goto next; + } + if (m->m_len > (MHLEN - 2)) { + MCLGET(mnew, M_DONTWAIT); + if (!(mnew->m_flags & M_EXT)) { + m_freem(mnew); + m_freem(m); + goto next; + } + } + mnew->m_pkthdr.rcvif = ifp; + m_adj(mnew, 2); + mnew->m_pkthdr.len = mnew->m_len = m->m_len; + m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, caddr_t)); + m_freem(m); + m = mnew; + } +#endif + + if (rxd->rx_stat & RX_STAT_IPCKSUMBAD) + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + else if (rxd->rx_stat & RX_STAT_IPCKSUMGOOD) + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED|CSUM_IP_VALID; + + if ((rxd->rx_stat & RX_STAT_TCPCKSUMGOOD) || + (rxd->rx_stat & RX_STAT_UDPCKSUMGOOD)) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + +#if NVLAN > 0 + if (rxd->rx_stat & RX_STAT_VLAN) { + if (vlan_input_tag(eh, m, + htons(rxd->rx_vlan >> 16)) < 0) + ifp->if_noproto++; + goto next; + } +#endif + + eh = mtod(m, struct ether_header *); + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + + ether_input(ifp, eh, m); + +next: + + roff += sizeof(struct txp_rx_desc); + if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) { + roff = 0; + rxd = r->r_desc; + } else + rxd++; + woff = *r->r_woff; + } + + *r->r_roff = woff; + + return; +} + +static void +txp_rxbuf_reclaim(sc) + struct txp_softc *sc; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct txp_hostvar *hv = sc->sc_hostvar; + struct txp_rxbuf_desc *rbd; + struct txp_swdesc *sd; + u_int32_t i; + + if (!(ifp->if_flags & IFF_RUNNING)) + return; + + i = sc->sc_rxbufprod; + rbd = sc->sc_rxbufs + i; + + while (1) { + sd = rbd->rb_sd; + if (sd->sd_mbuf != NULL) + break; + + MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA); + if (sd->sd_mbuf == NULL) + goto err_sd; + + MCLGET(sd->sd_mbuf, M_DONTWAIT); + if ((sd->sd_mbuf->m_flags & M_EXT) == 0) + goto err_mbuf; + sd->sd_mbuf->m_pkthdr.rcvif = ifp; + sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; + + rbd->rb_paddrlo = vtophys(mtod(sd->sd_mbuf, vm_offset_t)) + & 0xffffffff; + rbd->rb_paddrhi = 0; + + hv->hv_rx_buf_write_idx = TXP_IDX2OFFSET(i); + + if (++i == RXBUF_ENTRIES) { + i = 0; + rbd = sc->sc_rxbufs; + } else + rbd++; + } + + sc->sc_rxbufprod = i; + + return; + +err_mbuf: + m_freem(sd->sd_mbuf); +err_sd: + free(sd, M_DEVBUF); +} + +/* + * Reclaim mbufs and entries from a transmit ring. + */ +static void +txp_tx_reclaim(sc, r) + struct txp_softc *sc; + struct txp_tx_ring *r; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + u_int32_t idx = TXP_OFFSET2IDX(*(r->r_off)); + u_int32_t cons = r->r_cons, cnt = r->r_cnt; + struct txp_tx_desc *txd = r->r_desc + cons; + struct txp_swdesc *sd = sc->sc_txd + cons; + struct mbuf *m; + + while (cons != idx) { + if (cnt == 0) + break; + + if ((txd->tx_flags & TX_FLAGS_TYPE_M) == + TX_FLAGS_TYPE_DATA) { + m = sd->sd_mbuf; + if (m != NULL) { + m_freem(m); + txd->tx_addrlo = 0; + txd->tx_addrhi = 0; + ifp->if_opackets++; + } + } + ifp->if_flags &= ~IFF_OACTIVE; + + if (++cons == TX_ENTRIES) { + txd = r->r_desc; + cons = 0; + sd = sc->sc_txd; + } else { + txd++; + sd++; + } + + cnt--; + } + + r->r_cons = cons; + r->r_cnt = cnt; + if (cnt == 0) + ifp->if_timer = 0; +} + +static int +txp_shutdown(dev) + device_t dev; +{ + struct txp_softc *sc; + + sc = device_get_softc(dev); + + /* mask all interrupts */ + WRITE_REG(sc, TXP_IMR, + TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_LATCH); + + txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0); + txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0); + txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 0); + + return(0); +} + +static int +txp_alloc_rings(sc) + struct txp_softc *sc; +{ + struct txp_boot_record *boot; + struct txp_ldata *ld; + u_int32_t r; + int i; + + ld = sc->sc_ldata; + boot = &ld->txp_boot; + + /* boot record */ + sc->sc_boot = boot; + + /* host variables */ + bzero(&ld->txp_hostvar, sizeof(struct txp_hostvar)); + boot->br_hostvar_lo = vtophys(&ld->txp_hostvar); + boot->br_hostvar_hi = 0; + sc->sc_hostvar = (struct txp_hostvar *)&ld->txp_hostvar; + + /* hi priority tx ring */ + boot->br_txhipri_lo = vtophys(&ld->txp_txhiring);; + boot->br_txhipri_hi = 0; + boot->br_txhipri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc); + sc->sc_txhir.r_reg = TXP_H2A_1; + sc->sc_txhir.r_desc = (struct txp_tx_desc *)&ld->txp_txhiring; + sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0; + sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx; + + /* lo priority tx ring */ + boot->br_txlopri_lo = vtophys(&ld->txp_txloring); + boot->br_txlopri_hi = 0; + boot->br_txlopri_siz = TX_ENTRIES * sizeof(struct txp_tx_desc); + sc->sc_txlor.r_reg = TXP_H2A_3; + sc->sc_txlor.r_desc = (struct txp_tx_desc *)&ld->txp_txloring; + sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0; + sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx; + + /* high priority rx ring */ + boot->br_rxhipri_lo = vtophys(&ld->txp_rxhiring); + boot->br_rxhipri_hi = 0; + boot->br_rxhipri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc); + sc->sc_rxhir.r_desc = (struct txp_rx_desc *)&ld->txp_rxhiring; + sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx; + sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx; + + /* low priority rx ring */ + boot->br_rxlopri_lo = vtophys(&ld->txp_rxloring); + boot->br_rxlopri_hi = 0; + boot->br_rxlopri_siz = RX_ENTRIES * sizeof(struct txp_rx_desc); + sc->sc_rxlor.r_desc = (struct txp_rx_desc *)&ld->txp_rxloring; + sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx; + sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx; + + /* command ring */ + bzero(&ld->txp_cmdring, sizeof(struct txp_cmd_desc) * CMD_ENTRIES); + boot->br_cmd_lo = vtophys(&ld->txp_cmdring); + boot->br_cmd_hi = 0; + boot->br_cmd_siz = CMD_ENTRIES * sizeof(struct txp_cmd_desc); + sc->sc_cmdring.base = (struct txp_cmd_desc *)&ld->txp_cmdring; + sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc); + sc->sc_cmdring.lastwrite = 0; + + /* response ring */ + bzero(&ld->txp_rspring, sizeof(struct txp_rsp_desc) * RSP_ENTRIES); + boot->br_resp_lo = vtophys(&ld->txp_rspring); + boot->br_resp_hi = 0; + boot->br_resp_siz = CMD_ENTRIES * sizeof(struct txp_rsp_desc); + sc->sc_rspring.base = (struct txp_rsp_desc *)&ld->txp_rspring; + sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc); + sc->sc_rspring.lastwrite = 0; + + /* receive buffer ring */ + boot->br_rxbuf_lo = vtophys(&ld->txp_rxbufs); + boot->br_rxbuf_hi = 0; + boot->br_rxbuf_siz = RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc); + sc->sc_rxbufs = (struct txp_rxbuf_desc *)&ld->txp_rxbufs; + + for (i = 0; i < RXBUF_ENTRIES; i++) { + if (sc->sc_rxbufs[i].rb_sd != NULL) + continue; + sc->sc_rxbufs[i].rb_sd = malloc(sizeof(struct txp_swdesc), + M_DEVBUF, M_NOWAIT); + if (sc->sc_rxbufs[i].rb_sd == NULL) + return(ENOBUFS); + } + sc->sc_rxbufprod = 0; + + /* zero dma */ + bzero(&ld->txp_zero, sizeof(u_int32_t)); + boot->br_zero_lo = vtophys(&ld->txp_zero); + boot->br_zero_hi = 0; + + /* See if it's waiting for boot, and try to boot it */ + for (i = 0; i < 10000; i++) { + r = READ_REG(sc, TXP_A2H_0); + if (r == STAT_WAITING_FOR_BOOT) + break; + DELAY(50); + } + + if (r != STAT_WAITING_FOR_BOOT) { + device_printf(sc->sc_dev, "not waiting for boot\n"); + /*return(ENXIO);*/ + } + + WRITE_REG(sc, TXP_H2A_2, 0); + WRITE_REG(sc, TXP_H2A_1, vtophys(sc->sc_boot)); + WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD); + + /* See if it booted */ + for (i = 0; i < 10000; i++) { + r = READ_REG(sc, TXP_A2H_0); + if (r == STAT_RUNNING) + break; + DELAY(50); + } + if (r != STAT_RUNNING) { + device_printf(sc->sc_dev, "fw not running\n"); + return(ENXIO); + } + + /* Clear TX and CMD ring write registers */ + WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL); + WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL); + WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL); + WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL); + + return (0); +} + +static int +txp_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct txp_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + if ((error = ether_ioctl(ifp, command, data)) > 0) { + splx(s); + return error; + } + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + txp_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + txp_stop(sc); + } + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Multicast list has changed; set the hardware + * filter accordingly. + */ + txp_set_filter(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command); + break; + default: + error = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +static int +txp_rxring_fill(sc) + struct txp_softc *sc; +{ + int i; + struct ifnet *ifp; + struct txp_swdesc *sd; + + ifp = &sc->sc_arpcom.ac_if; + + for (i = 0; i < RXBUF_ENTRIES; i++) { + sd = sc->sc_rxbufs[i].rb_sd; + MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA); + if (sd->sd_mbuf == NULL) + return(ENOBUFS); + + MCLGET(sd->sd_mbuf, M_DONTWAIT); + if ((sd->sd_mbuf->m_flags & M_EXT) == 0) { + m_freem(sd->sd_mbuf); + return(ENOBUFS); + } + sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; + sd->sd_mbuf->m_pkthdr.rcvif = ifp; + + sc->sc_rxbufs[i].rb_paddrlo = + vtophys(mtod(sd->sd_mbuf, vm_offset_t)); + sc->sc_rxbufs[i].rb_paddrhi = 0; + } + + sc->sc_hostvar->hv_rx_buf_write_idx = (RXBUF_ENTRIES - 1) * + sizeof(struct txp_rxbuf_desc); + + return(0); +} + +static void +txp_rxring_empty(sc) + struct txp_softc *sc; +{ + int i; + struct txp_swdesc *sd; + + if (sc->sc_rxbufs == NULL) + return; + + for (i = 0; i < RXBUF_ENTRIES; i++) { + if (&sc->sc_rxbufs[i] == NULL) + continue; + sd = sc->sc_rxbufs[i].rb_sd; + if (sd == NULL) + continue; + if (sd->sd_mbuf != NULL) { + m_freem(sd->sd_mbuf); + sd->sd_mbuf = NULL; + } + } + + return; +} + +static void +txp_init(xsc) + void *xsc; +{ + struct txp_softc *sc; + struct ifnet *ifp; + u_int16_t p1; + u_int32_t p2; + int s; + + sc = xsc; + ifp = &sc->sc_arpcom.ac_if; + + if (ifp->if_flags & IFF_RUNNING) + return; + + txp_stop(sc); + + s = splnet(); + + txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0, + NULL, NULL, NULL, 1); + + /* Set station address. */ + ((u_int8_t *)&p1)[1] = sc->sc_arpcom.ac_enaddr[0]; + ((u_int8_t *)&p1)[0] = sc->sc_arpcom.ac_enaddr[1]; + ((u_int8_t *)&p2)[3] = sc->sc_arpcom.ac_enaddr[2]; + ((u_int8_t *)&p2)[2] = sc->sc_arpcom.ac_enaddr[3]; + ((u_int8_t *)&p2)[1] = sc->sc_arpcom.ac_enaddr[4]; + ((u_int8_t *)&p2)[0] = sc->sc_arpcom.ac_enaddr[5]; + txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, + NULL, NULL, NULL, 1); + + txp_set_filter(sc); + + txp_rxring_fill(sc); + + txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1); + txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1); + + WRITE_REG(sc, TXP_IER, TXP_INT_RESERVED | TXP_INT_SELF | + TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 | + TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 | + TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 | + TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | TXP_INT_LATCH); + WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + + sc->sc_tick = timeout(txp_tick, sc, hz); + + splx(s); +} + +static void +txp_tick(vsc) + void *vsc; +{ + struct txp_softc *sc = vsc; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct txp_rsp_desc *rsp = NULL; + struct txp_ext_desc *ext; + int s; + + s = splnet(); + txp_rxbuf_reclaim(sc); + + if (txp_command2(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0, + &rsp, 1)) + goto out; + if (rsp->rsp_numdesc != 6) + goto out; + if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0, + NULL, NULL, NULL, 1)) + goto out; + ext = (struct txp_ext_desc *)(rsp + 1); + + ifp->if_ierrors += ext[3].ext_2 + ext[3].ext_3 + ext[3].ext_4 + + ext[4].ext_1 + ext[4].ext_4; + ifp->if_oerrors += ext[0].ext_1 + ext[1].ext_1 + ext[1].ext_4 + + ext[2].ext_1; + ifp->if_collisions += ext[0].ext_2 + ext[0].ext_3 + ext[1].ext_2 + + ext[1].ext_3; + ifp->if_opackets += rsp->rsp_par2; + ifp->if_ipackets += ext[2].ext_3; + +out: + if (rsp != NULL) + free(rsp, M_DEVBUF); + + splx(s); + sc->sc_tick = timeout(txp_tick, sc, hz); + + return; +} + +static void +txp_start(ifp) + struct ifnet *ifp; +{ + struct txp_softc *sc = ifp->if_softc; + struct txp_tx_ring *r = &sc->sc_txhir; + struct txp_tx_desc *txd; + struct txp_frag_desc *fxd; + struct mbuf *m, *m0; + struct txp_swdesc *sd; + u_int32_t firstprod, firstcnt, prod, cnt; +#if NVLAN > 0 + struct ifvlan *ifv; +#endif + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + prod = r->r_prod; + cnt = r->r_cnt; + + while (1) { + IF_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + firstprod = prod; + firstcnt = cnt; + + sd = sc->sc_txd + prod; + sd->sd_mbuf = m; + + if ((TX_ENTRIES - cnt) < 4) + goto oactive; + + txd = r->r_desc + prod; + + txd->tx_flags = TX_FLAGS_TYPE_DATA; + txd->tx_numdesc = 0; + txd->tx_addrlo = 0; + txd->tx_addrhi = 0; + txd->tx_totlen = 0; + txd->tx_pflags = 0; + + if (++prod == TX_ENTRIES) + prod = 0; + + if (++cnt >= (TX_ENTRIES - 4)) + goto oactive; + +#if NVLAN > 0 + if ((m->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && + m->m_pkthdr.rcvif != NULL) { + ifv = m->m_pkthdr.rcvif->if_softc; + txd->tx_pflags = TX_PFLAGS_VLAN | + (htons(ifv->ifv_tag) << TX_PFLAGS_VLANTAG_S); + } +#endif + if (m->m_pkthdr.csum_flags & CSUM_IP) + txd->tx_pflags |= TX_PFLAGS_IPCKSUM; + +#if 0 + if (m->m_pkthdr.csum_flags & CSUM_TCP) + txd->tx_pflags |= TX_PFLAGS_TCPCKSUM; + if (m->m_pkthdr.csum_flags & CSUM_UDP) + txd->tx_pflags |= TX_PFLAGS_UDPCKSUM; +#endif + + fxd = (struct txp_frag_desc *)(r->r_desc + prod); + for (m0 = m; m0 != NULL; m0 = m0->m_next) { + if (m0->m_len == 0) + continue; + if (++cnt >= (TX_ENTRIES - 4)) + goto oactive; + + txd->tx_numdesc++; + + fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG; + fxd->frag_rsvd1 = 0; + fxd->frag_len = m0->m_len; + fxd->frag_addrlo = vtophys(mtod(m0, vm_offset_t)); + fxd->frag_addrhi = 0; + fxd->frag_rsvd2 = 0; + + if (++prod == TX_ENTRIES) { + fxd = (struct txp_frag_desc *)r->r_desc; + prod = 0; + } else + fxd++; + + } + + ifp->if_timer = 5; + + if (ifp->if_bpf) + bpf_mtap(ifp, m); + WRITE_REG(sc, r->r_reg, TXP_IDX2OFFSET(prod)); + } + + r->r_prod = prod; + r->r_cnt = cnt; + return; + +oactive: + ifp->if_flags |= IFF_OACTIVE; + r->r_prod = firstprod; + r->r_cnt = firstcnt; + IF_PREPEND(&ifp->if_snd, m); + return; +} + +/* + * Handle simple commands sent to the typhoon + */ +static int +txp_command(sc, id, in1, in2, in3, out1, out2, out3, wait) + struct txp_softc *sc; + u_int16_t id, in1, *out1; + u_int32_t in2, in3, *out2, *out3; + int wait; +{ + struct txp_rsp_desc *rsp = NULL; + + if (txp_command2(sc, id, in1, in2, in3, NULL, 0, &rsp, wait)) + return (-1); + + if (!wait) + return (0); + + if (out1 != NULL) + *out1 = rsp->rsp_par1; + if (out2 != NULL) + *out2 = rsp->rsp_par2; + if (out3 != NULL) + *out3 = rsp->rsp_par3; + free(rsp, M_DEVBUF); + return (0); +} + +static int +txp_command2(sc, id, in1, in2, in3, in_extp, in_extn, rspp, wait) + struct txp_softc *sc; + u_int16_t id, in1; + u_int32_t in2, in3; + struct txp_ext_desc *in_extp; + u_int8_t in_extn; + struct txp_rsp_desc **rspp; + int wait; +{ + struct txp_hostvar *hv = sc->sc_hostvar; + struct txp_cmd_desc *cmd; + struct txp_ext_desc *ext; + u_int32_t idx, i; + u_int16_t seq; + + if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) { + device_printf(sc->sc_dev, "no free cmd descriptors\n"); + return (-1); + } + + idx = sc->sc_cmdring.lastwrite; + cmd = (struct txp_cmd_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx); + bzero(cmd, sizeof(*cmd)); + + cmd->cmd_numdesc = in_extn; + cmd->cmd_seq = seq = sc->sc_seq++; + cmd->cmd_id = id; + cmd->cmd_par1 = in1; + cmd->cmd_par2 = in2; + cmd->cmd_par3 = in3; + cmd->cmd_flags = CMD_FLAGS_TYPE_CMD | + (wait ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID; + + idx += sizeof(struct txp_cmd_desc); + if (idx == sc->sc_cmdring.size) + idx = 0; + + for (i = 0; i < in_extn; i++) { + ext = (struct txp_ext_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx); + bcopy(in_extp, ext, sizeof(struct txp_ext_desc)); + in_extp++; + idx += sizeof(struct txp_cmd_desc); + if (idx == sc->sc_cmdring.size) + idx = 0; + } + + sc->sc_cmdring.lastwrite = idx; + + WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite); + + if (!wait) + return (0); + + for (i = 0; i < 10000; i++) { + idx = hv->hv_resp_read_idx; + if (idx != hv->hv_resp_write_idx) { + *rspp = NULL; + if (txp_response(sc, idx, id, seq, rspp)) + return (-1); + if (*rspp != NULL) + break; + } + DELAY(50); + } + if (i == 1000 || (*rspp) == NULL) { + device_printf(sc->sc_dev, "0x%x command failed\n", id); + return (-1); + } + + return (0); +} + +static int +txp_response(sc, ridx, id, seq, rspp) + struct txp_softc *sc; + u_int32_t ridx; + u_int16_t id; + u_int16_t seq; + struct txp_rsp_desc **rspp; +{ + struct txp_hostvar *hv = sc->sc_hostvar; + struct txp_rsp_desc *rsp; + + while (ridx != hv->hv_resp_write_idx) { + rsp = (struct txp_rsp_desc *)(((u_int8_t *)sc->sc_rspring.base) + ridx); + + if (id == rsp->rsp_id && rsp->rsp_seq == seq) { + *rspp = (struct txp_rsp_desc *)malloc( + sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1), + M_DEVBUF, M_NOWAIT); + if ((*rspp) == NULL) + return (-1); + txp_rsp_fixup(sc, rsp, *rspp); + return (0); + } + + if (rsp->rsp_flags & RSP_FLAGS_ERROR) { + device_printf(sc->sc_dev, "response error!\n"); + txp_rsp_fixup(sc, rsp, NULL); + ridx = hv->hv_resp_read_idx; + continue; + } + + switch (rsp->rsp_id) { + case TXP_CMD_CYCLE_STATISTICS: + case TXP_CMD_MEDIA_STATUS_READ: + break; + case TXP_CMD_HELLO_RESPONSE: + device_printf(sc->sc_dev, "hello\n"); + break; + default: + device_printf(sc->sc_dev, "unknown id(0x%x)\n", + rsp->rsp_id); + } + + txp_rsp_fixup(sc, rsp, NULL); + ridx = hv->hv_resp_read_idx; + hv->hv_resp_read_idx = ridx; + } + + return (0); +} + +static void +txp_rsp_fixup(sc, rsp, dst) + struct txp_softc *sc; + struct txp_rsp_desc *rsp, *dst; +{ + struct txp_rsp_desc *src = rsp; + struct txp_hostvar *hv = sc->sc_hostvar; + u_int32_t i, ridx; + + ridx = hv->hv_resp_read_idx; + + for (i = 0; i < rsp->rsp_numdesc + 1; i++) { + if (dst != NULL) + bcopy(src, dst++, sizeof(struct txp_rsp_desc)); + ridx += sizeof(struct txp_rsp_desc); + if (ridx == sc->sc_rspring.size) { + src = sc->sc_rspring.base; + ridx = 0; + } else + src++; + sc->sc_rspring.lastwrite = hv->hv_resp_read_idx = ridx; + } + + hv->hv_resp_read_idx = ridx; +} + +static int +txp_cmd_desc_numfree(sc) + struct txp_softc *sc; +{ + struct txp_hostvar *hv = sc->sc_hostvar; + struct txp_boot_record *br = sc->sc_boot; + u_int32_t widx, ridx, nfree; + + widx = sc->sc_cmdring.lastwrite; + ridx = hv->hv_cmd_read_idx; + + if (widx == ridx) { + /* Ring is completely free */ + nfree = br->br_cmd_siz - sizeof(struct txp_cmd_desc); + } else { + if (widx > ridx) + nfree = br->br_cmd_siz - + (widx - ridx + sizeof(struct txp_cmd_desc)); + else + nfree = ridx - widx - sizeof(struct txp_cmd_desc); + } + + return (nfree / sizeof(struct txp_cmd_desc)); +} + +static void +txp_stop(sc) + struct txp_softc *sc; +{ + struct ifnet *ifp; + + ifp = &sc->sc_arpcom.ac_if; + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + untimeout(txp_tick, sc, sc->sc_tick); + + txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1); + txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1); + + txp_rxring_empty(sc); + + return; +} + +static void +txp_watchdog(ifp) + struct ifnet *ifp; +{ + return; +} + +static int +txp_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct txp_softc *sc = ifp->if_softc; + struct ifmedia *ifm = &sc->sc_ifmedia; + u_int16_t new_xcvr; + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) { + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + new_xcvr = TXP_XCVR_10_FDX; + else + new_xcvr = TXP_XCVR_10_HDX; + } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) { + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + new_xcvr = TXP_XCVR_100_FDX; + else + new_xcvr = TXP_XCVR_100_HDX; + } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { + new_xcvr = TXP_XCVR_AUTO; + } else + return (EINVAL); + + /* nothing to do */ + if (sc->sc_xcvr == new_xcvr) + return (0); + + txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0, + NULL, NULL, NULL, 0); + sc->sc_xcvr = new_xcvr; + + return (0); +} + +static void +txp_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct txp_softc *sc = ifp->if_softc; + struct ifmedia *ifm = &sc->sc_ifmedia; + u_int16_t bmsr, bmcr, anlpar; + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, + &bmsr, NULL, NULL, 1)) + goto bail; + if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, + &bmsr, NULL, NULL, 1)) + goto bail; + + if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0, + &bmcr, NULL, NULL, 1)) + goto bail; + + if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0, + &anlpar, NULL, NULL, 1)) + goto bail; + + if (bmsr & BMSR_LINK) + ifmr->ifm_status |= IFM_ACTIVE; + + if (bmcr & BMCR_ISO) { + ifmr->ifm_active |= IFM_NONE; + ifmr->ifm_status = 0; + return; + } + + if (bmcr & BMCR_LOOP) + ifmr->ifm_active |= IFM_LOOP; + + if (bmcr & BMCR_AUTOEN) { + if ((bmsr & BMSR_ACOMP) == 0) { + ifmr->ifm_active |= IFM_NONE; + return; + } + + if (anlpar & ANLPAR_T4) + ifmr->ifm_active |= IFM_100_T4; + else if (anlpar & ANLPAR_TX_FD) + ifmr->ifm_active |= IFM_100_TX|IFM_FDX; + else if (anlpar & ANLPAR_TX) + ifmr->ifm_active |= IFM_100_TX; + else if (anlpar & ANLPAR_10_FD) + ifmr->ifm_active |= IFM_10_T|IFM_FDX; + else if (anlpar & ANLPAR_10) + ifmr->ifm_active |= IFM_10_T; + else + ifmr->ifm_active |= IFM_NONE; + } else + ifmr->ifm_active = ifm->ifm_cur->ifm_media; + return; + +bail: + ifmr->ifm_active |= IFM_NONE; + ifmr->ifm_status &= ~IFM_AVALID; +} + +#ifdef TXP_DEBUG +static void +txp_show_descriptor(d) + void *d; +{ + struct txp_cmd_desc *cmd = d; + struct txp_rsp_desc *rsp = d; + struct txp_tx_desc *txd = d; + struct txp_frag_desc *frgd = d; + + switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) { + case CMD_FLAGS_TYPE_CMD: + /* command descriptor */ + printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", + cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq, + cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3); + break; + case CMD_FLAGS_TYPE_RESP: + /* response descriptor */ + printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", + rsp->rsp_flags, rsp->rsp_numdesc, rsp->rsp_id, rsp->rsp_seq, + rsp->rsp_par1, rsp->rsp_par2, rsp->rsp_par3); + break; + case CMD_FLAGS_TYPE_DATA: + /* data header (assuming tx for now) */ + printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]", + txd->tx_flags, txd->tx_numdesc, txd->tx_totlen, + txd->tx_addrlo, txd->tx_addrhi, txd->tx_pflags); + break; + case CMD_FLAGS_TYPE_FRAG: + /* fragment descriptor */ + printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]", + frgd->frag_flags, frgd->frag_rsvd1, frgd->frag_len, + frgd->frag_addrlo, frgd->frag_addrhi, frgd->frag_rsvd2); + break; + default: + printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", + cmd->cmd_flags & CMD_FLAGS_TYPE_M, + cmd->cmd_flags, cmd->cmd_numdesc, cmd->cmd_id, cmd->cmd_seq, + cmd->cmd_par1, cmd->cmd_par2, cmd->cmd_par3); + break; + } +} +#endif + +static void +txp_set_filter(sc) + struct txp_softc *sc; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + u_int32_t crc, carry, hashbit, hash[2]; + u_int16_t filter; + u_int8_t octet; + int i, j, mcnt = 0; + struct ifmultiaddr *ifma; + char *enm; + + if (ifp->if_flags & IFF_PROMISC) { + filter = TXP_RXFILT_PROMISC; + goto setit; + } + + filter = TXP_RXFILT_DIRECT; + + if (ifp->if_flags & IFF_BROADCAST) + filter |= TXP_RXFILT_BROADCAST; + + if (ifp->if_flags & IFF_ALLMULTI) + filter |= TXP_RXFILT_ALLMULTI; + else { + hash[0] = hash[1] = 0; + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + enm = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); + mcnt++; + crc = 0xffffffff; + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + octet = enm[i]; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ + (octet & 1); + crc <<= 1; + octet >>= 1; + if (carry) + crc = (crc ^ TXP_POLYNOMIAL) | + carry; + } + } + hashbit = (u_int16_t)(crc & (64 - 1)); + hash[hashbit / 32] |= (1 << hashbit % 32); + } + + if (mcnt > 0) { + filter |= TXP_RXFILT_HASHMULTI; + txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE, + 2, hash[0], hash[1], NULL, NULL, NULL, 0); + } + } + +setit: + + txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0, + NULL, NULL, NULL, 1); + + return; +} + +static void +txp_capabilities(sc) + struct txp_softc *sc; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct txp_rsp_desc *rsp = NULL; + struct txp_ext_desc *ext; + + if (txp_command2(sc, TXP_CMD_OFFLOAD_READ, 0, 0, 0, NULL, 0, &rsp, 1)) + goto out; + + if (rsp->rsp_numdesc != 1) + goto out; + ext = (struct txp_ext_desc *)(rsp + 1); + + sc->sc_tx_capability = ext->ext_1 & OFFLOAD_MASK; + sc->sc_rx_capability = ext->ext_2 & OFFLOAD_MASK; + +#if NVLAN > 0 + if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_VLAN) { + sc->sc_tx_capability |= OFFLOAD_VLAN; + sc->sc_rx_capability |= OFFLOAD_VLAN; + } +#endif + +#if 0 + /* not ready yet */ + if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPSEC) { + sc->sc_tx_capability |= OFFLOAD_IPSEC; + sc->sc_rx_capability |= OFFLOAD_IPSEC; + ifp->if_capabilities |= IFCAP_IPSEC; + } +#endif + + if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPCKSUM) { + sc->sc_tx_capability |= OFFLOAD_IPCKSUM; + sc->sc_rx_capability |= OFFLOAD_IPCKSUM; + ifp->if_hwassist |= CSUM_IP; + } + + if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_TCPCKSUM) { +#if 0 + sc->sc_tx_capability |= OFFLOAD_TCPCKSUM; +#endif + sc->sc_rx_capability |= OFFLOAD_TCPCKSUM; +#if 0 + ifp->if_capabilities |= CSUM_TCP; +#endif + } + + if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_UDPCKSUM) { +#if 0 + sc->sc_tx_capability |= OFFLOAD_UDPCKSUM; +#endif + sc->sc_rx_capability |= OFFLOAD_UDPCKSUM; +#if 0 + ifp->if_capabilities |= CSUM_UDP; +#endif + } + + if (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0, + sc->sc_tx_capability, sc->sc_rx_capability, NULL, NULL, NULL, 1)) + goto out; + +out: + if (rsp != NULL) + free(rsp, M_DEVBUF); + + return; +} diff --git a/sys/dev/txp/if_txpreg.h b/sys/dev/txp/if_txpreg.h new file mode 100644 index 000000000000..8704a33a4b3e --- /dev/null +++ b/sys/dev/txp/if_txpreg.h @@ -0,0 +1,663 @@ +/* $OpenBSD: if_txpreg.h,v 1.30 2001/06/23 04:18:02 jason Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2001 Aaron Campbell . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Aaron Campbell. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define TXP_PCI_LOMEM 0x14 /* pci conf, memory map BAR */ +#define TXP_PCI_LOIO 0x10 /* pci conf, IO map BAR */ +#define TXP_PCI_INTLINE 0x3C + +/* + * Typhoon registers. + */ +#define TXP_SRR 0x00 /* soft reset register */ +#define TXP_ISR 0x04 /* interrupt status register */ +#define TXP_IER 0x08 /* interrupt enable register */ +#define TXP_IMR 0x0c /* interrupt mask register */ +#define TXP_SIR 0x10 /* self interrupt register */ +#define TXP_H2A_7 0x14 /* host->arm comm 7 */ +#define TXP_H2A_6 0x18 /* host->arm comm 6 */ +#define TXP_H2A_5 0x1c /* host->arm comm 5 */ +#define TXP_H2A_4 0x20 /* host->arm comm 4 */ +#define TXP_H2A_3 0x24 /* host->arm comm 3 */ +#define TXP_H2A_2 0x28 /* host->arm comm 2 */ +#define TXP_H2A_1 0x2c /* host->arm comm 1 */ +#define TXP_H2A_0 0x30 /* host->arm comm 0 */ +#define TXP_A2H_3 0x34 /* arm->host comm 3 */ +#define TXP_A2H_2 0x38 /* arm->host comm 2 */ +#define TXP_A2H_1 0x3c /* arm->host comm 1 */ +#define TXP_A2H_0 0x40 /* arm->host comm 0 */ + +/* + * interrupt bits (IMR, ISR, IER) + */ +#define TXP_INT_RESERVED 0xffff0000 +#define TXP_INT_A2H_7 0x00008000 /* arm->host comm 7 */ +#define TXP_INT_A2H_6 0x00004000 /* arm->host comm 6 */ +#define TXP_INT_A2H_5 0x00002000 /* arm->host comm 5 */ +#define TXP_INT_A2H_4 0x00001000 /* arm->host comm 4 */ +#define TXP_INT_SELF 0x00000800 /* self interrupt */ +#define TXP_INT_PCI_TABORT 0x00000400 /* pci target abort */ +#define TXP_INT_PCI_MABORT 0x00000200 /* pci master abort */ +#define TXP_INT_DMA3 0x00000100 /* dma3 done */ +#define TXP_INT_DMA2 0x00000080 /* dma2 done */ +#define TXP_INT_DMA1 0x00000040 /* dma1 done */ +#define TXP_INT_DMA0 0x00000020 /* dma0 done */ +#define TXP_INT_A2H_3 0x00000010 /* arm->host comm 3 */ +#define TXP_INT_A2H_2 0x00000008 /* arm->host comm 2 */ +#define TXP_INT_A2H_1 0x00000004 /* arm->host comm 1 */ +#define TXP_INT_A2H_0 0x00000002 /* arm->host comm 0 */ +#define TXP_INT_LATCH 0x00000001 /* interrupt latch */ + +/* + * soft reset register (SRR) + */ +#define TXP_SRR_ALL 0x0000007f /* full reset */ + +/* + * Typhoon boot commands. + */ +#define TXP_BOOTCMD_NULL 0x00 +#define TXP_BOOTCMD_DOWNLOAD_COMPLETE 0xfb +#define TXP_BOOTCMD_SEGMENT_AVAILABLE 0xfc +#define TXP_BOOTCMD_RUNTIME_IMAGE 0xfd +#define TXP_BOOTCMD_REGISTER_BOOT_RECORD 0xff + +/* + * Typhoon runtime commands. + */ +#define TXP_CMD_GLOBAL_RESET 0x00 +#define TXP_CMD_TX_ENABLE 0x01 +#define TXP_CMD_TX_DISABLE 0x02 +#define TXP_CMD_RX_ENABLE 0x03 +#define TXP_CMD_RX_DISABLE 0x04 +#define TXP_CMD_RX_FILTER_WRITE 0x05 +#define TXP_CMD_RX_FILTER_READ 0x06 +#define TXP_CMD_READ_STATISTICS 0x07 +#define TXP_CMD_CYCLE_STATISTICS 0x08 +#define TXP_CMD_CLEAR_STATISTICS 0x09 +#define TXP_CMD_MEMORY_READ 0x0a +#define TXP_CMD_MEMORY_WRITE_SINGLE 0x0b +#define TXP_CMD_VARIABLE_SECTION_READ 0x0c +#define TXP_CMD_VARIABLE_SECTION_WRITE 0x0d +#define TXP_CMD_STATIC_SECTION_READ 0x0e +#define TXP_CMD_STATIC_SECTION_WRITE 0x0f +#define TXP_CMD_IMAGE_SECTION_PROGRAM 0x10 +#define TXP_CMD_NVRAM_PAGE_READ 0x11 +#define TXP_CMD_NVRAM_PAGE_WRITE 0x12 +#define TXP_CMD_XCVR_SELECT 0x13 +#define TXP_CMD_TEST_MUX 0x14 +#define TXP_CMD_PHYLOOPBACK_ENABLE 0x15 +#define TXP_CMD_PHYLOOPBACK_DISABLE 0x16 +#define TXP_CMD_MAC_CONTROL_READ 0x17 +#define TXP_CMD_MAC_CONTROL_WRITE 0x18 +#define TXP_CMD_MAX_PKT_SIZE_READ 0x19 +#define TXP_CMD_MAX_PKT_SIZE_WRITE 0x1a +#define TXP_CMD_MEDIA_STATUS_READ 0x1b +#define TXP_CMD_MEDIA_STATUS_WRITE 0x1c +#define TXP_CMD_NETWORK_DIAGS_READ 0x1d +#define TXP_CMD_NETWORK_DIAGS_WRITE 0x1e +#define TXP_CMD_PHY_MGMT_READ 0x1f +#define TXP_CMD_PHY_MGMT_WRITE 0x20 +#define TXP_CMD_VARIABLE_PARAMETER_READ 0x21 +#define TXP_CMD_VARIABLE_PARAMETER_WRITE 0x22 +#define TXP_CMD_GOTO_SLEEP 0x23 +#define TXP_CMD_FIREWALL_CONTROL 0x24 +#define TXP_CMD_MCAST_HASH_MASK_WRITE 0x25 +#define TXP_CMD_STATION_ADDRESS_WRITE 0x26 +#define TXP_CMD_STATION_ADDRESS_READ 0x27 +#define TXP_CMD_STATION_MASK_WRITE 0x28 +#define TXP_CMD_STATION_MASK_READ 0x29 +#define TXP_CMD_VLAN_ETHER_TYPE_READ 0x2a +#define TXP_CMD_VLAN_ETHER_TYPE_WRITE 0x2b +#define TXP_CMD_VLAN_MASK_READ 0x2c +#define TXP_CMD_VLAN_MASK_WRITE 0x2d +#define TXP_CMD_BCAST_THROTTLE_WRITE 0x2e +#define TXP_CMD_BCAST_THROTTLE_READ 0x2f +#define TXP_CMD_DHCP_PREVENT_WRITE 0x30 +#define TXP_CMD_DHCP_PREVENT_READ 0x31 +#define TXP_CMD_RECV_BUFFER_CONTROL 0x32 +#define TXP_CMD_SOFTWARE_RESET 0x33 +#define TXP_CMD_CREATE_SA 0x34 +#define TXP_CMD_DELETE_SA 0x35 +#define TXP_CMD_ENABLE_RX_IP_OPTION 0x36 +#define TXP_CMD_RANDOM_NUMBER_CONTROL 0x37 +#define TXP_CMD_RANDOM_NUMBER_READ 0x38 +#define TXP_CMD_MATRIX_TABLE_MODE_WRITE 0x39 +#define TXP_CMD_MATRIX_DETAIL_READ 0x3a +#define TXP_CMD_FILTER_ARRAY_READ 0x3b +#define TXP_CMD_FILTER_DETAIL_READ 0x3c +#define TXP_CMD_FILTER_TABLE_MODE_WRITE 0x3d +#define TXP_CMD_FILTER_TCL_WRITE 0x3e +#define TXP_CMD_FILTER_TBL_READ 0x3f +#define TXP_CMD_FILTER_DEFINE 0x45 +#define TXP_CMD_ADD_WAKEUP_PKT 0x46 +#define TXP_CMD_ADD_SLEEP_PKT 0x47 +#define TXP_CMD_ENABLE_SLEEP_EVENTS 0x48 +#define TXP_CMD_ENABLE_WAKEUP_EVENTS 0x49 +#define TXP_CMD_GET_IP_ADDRESS 0x4a +#define TXP_CMD_READ_PCI_REG 0x4c +#define TXP_CMD_WRITE_PCI_REG 0x4d +#define TXP_CMD_OFFLOAD_READ 0x4e +#define TXP_CMD_OFFLOAD_WRITE 0x4f +#define TXP_CMD_HELLO_RESPONSE 0x57 +#define TXP_CMD_ENABLE_RX_FILTER 0x58 +#define TXP_CMD_RX_FILTER_CAPABILITY 0x59 +#define TXP_CMD_HALT 0x5d +#define TXP_CMD_READ_IPSEC_INFO 0x54 +#define TXP_CMD_GET_IPSEC_ENABLE 0x67 +#define TXP_CMD_INVALID 0xffff + +#define TXP_FRAGMENT 0x0000 +#define TXP_TXFRAME 0x0001 +#define TXP_COMMAND 0x0002 +#define TXP_OPTION 0x0003 +#define TXP_RECEIVE 0x0004 +#define TXP_RESPONSE 0x0005 + +#define TXP_TYPE_IPSEC 0x0000 +#define TXP_TYPE_TCPSEGMENT 0x0001 + +#define TXP_PFLAG_NOCRC 0x0000 +#define TXP_PFLAG_IPCKSUM 0x0001 +#define TXP_PFLAG_TCPCKSUM 0x0002 +#define TXP_PFLAG_TCPSEGMENT 0x0004 +#define TXP_PFLAG_INSERTVLAN 0x0008 +#define TXP_PFLAG_IPSEC 0x0010 +#define TXP_PFLAG_PRIORITY 0x0020 +#define TXP_PFLAG_UDPCKSUM 0x0040 +#define TXP_PFLAG_PADFRAME 0x0080 + +#define TXP_MISC_FIRSTDESC 0x0000 +#define TXP_MISC_LASTDESC 0x0001 + +#define TXP_ERR_INTERNAL 0x0000 +#define TXP_ERR_FIFOUNDERRUN 0x0001 +#define TXP_ERR_BADSSD 0x0002 +#define TXP_ERR_RUNT 0x0003 +#define TXP_ERR_CRC 0x0004 +#define TXP_ERR_OVERSIZE 0x0005 +#define TXP_ERR_ALIGNMENT 0x0006 +#define TXP_ERR_DRIBBLEBIT 0x0007 + +#define TXP_PROTO_UNKNOWN 0x0000 +#define TXP_PROTO_IP 0x0001 +#define TXP_PROTO_IPX 0x0002 +#define TXP_PROTO_RESERVED 0x0003 + +#define TXP_STAT_PROTO 0x0001 +#define TXP_STAT_VLAN 0x0002 +#define TXP_STAT_IPFRAGMENT 0x0004 +#define TXP_STAT_IPSEC 0x0008 +#define TXP_STAT_IPCKSUMBAD 0x0010 +#define TXP_STAT_TCPCKSUMBAD 0x0020 +#define TXP_STAT_UDPCKSUMBAD 0x0040 +#define TXP_STAT_IPCKSUMGOOD 0x0080 +#define TXP_STAT_TCPCKSUMGOOD 0x0100 +#define TXP_STAT_UDPCKSUMGOOD 0x0200 + +struct txp_tx_desc { + volatile u_int8_t tx_flags; /* type/descriptor flags */ + volatile u_int8_t tx_numdesc; /* number of descriptors */ + volatile u_int16_t tx_totlen; /* total packet length */ + volatile u_int32_t tx_addrlo; /* virt addr low word */ + volatile u_int32_t tx_addrhi; /* virt addr high word */ + volatile u_int32_t tx_pflags; /* processing flags */ +}; +#define TX_FLAGS_TYPE_M 0x07 /* type mask */ +#define TX_FLAGS_TYPE_FRAG 0x00 /* type: fragment */ +#define TX_FLAGS_TYPE_DATA 0x01 /* type: data frame */ +#define TX_FLAGS_TYPE_CMD 0x02 /* type: command frame */ +#define TX_FLAGS_TYPE_OPT 0x03 /* type: options */ +#define TX_FLAGS_TYPE_RX 0x04 /* type: command */ +#define TX_FLAGS_TYPE_RESP 0x05 /* type: response */ +#define TX_FLAGS_RESP 0x40 /* response requested */ +#define TX_FLAGS_VALID 0x80 /* valid descriptor */ + +#define TX_PFLAGS_DNAC 0x00000001 /* do not add crc */ +#define TX_PFLAGS_IPCKSUM 0x00000002 /* ip checksum */ +#define TX_PFLAGS_TCPCKSUM 0x00000004 /* tcp checksum */ +#define TX_PFLAGS_TCPSEG 0x00000008 /* tcp segmentation */ +#define TX_PFLAGS_VLAN 0x00000010 /* insert vlan */ +#define TX_PFLAGS_IPSEC 0x00000020 /* perform ipsec */ +#define TX_PFLAGS_PRIO 0x00000040 /* priority field valid */ +#define TX_PFLAGS_UDPCKSUM 0x00000080 /* udp checksum */ +#define TX_PFLAGS_PADFRAME 0x00000100 /* pad frame */ +#define TX_PFLAGS_VLANTAG_M 0x0ffff000 /* vlan tag mask */ +#define TX_PFLAGS_VLANPRI_M 0x00700000 /* vlan priority mask */ +#define TX_PFLAGS_VLANTAG_S 12 /* amount to shift tag */ + +struct txp_rx_desc { + volatile u_int8_t rx_flags; /* type/descriptor flags */ + volatile u_int8_t rx_numdesc; /* number of descriptors */ + volatile u_int16_t rx_len; /* frame length */ +#ifdef notdef + volatile u_int32_t rx_vaddrlo; /* virtual address, lo word */ + volatile u_int32_t rx_vaddrhi; /* virtual address, hi word */ +#endif + union { + struct txp_swdesc *rx_sd; + u_int64_t rx_dummy; + } txp_rx_u; + volatile u_int32_t rx_stat; /* status */ + volatile u_int16_t rx_filter; /* filter status */ + volatile u_int16_t rx_hash; /* hash status */ + volatile u_int32_t rx_vlan; /* vlan tag/priority */ +}; + +#define rx_sd txp_rx_u.rx_sd + +/* txp_rx_desc.rx_flags */ +#define RX_FLAGS_TYPE_M 0x07 /* type mask */ +#define RX_FLAGS_TYPE_FRAG 0x00 /* type: fragment */ +#define RX_FLAGS_TYPE_DATA 0x01 /* type: data frame */ +#define RX_FLAGS_TYPE_CMD 0x02 /* type: command frame */ +#define RX_FLAGS_TYPE_OPT 0x03 /* type: options */ +#define RX_FLAGS_TYPE_RX 0x04 /* type: command */ +#define RX_FLAGS_TYPE_RESP 0x05 /* type: response */ +#define RX_FLAGS_RCV_TYPE_M 0x18 /* rcvtype mask */ +#define RX_FLAGS_RCV_TYPE_RX 0x00 /* rcvtype: receive */ +#define RX_FLAGS_RCV_TYPE_RSP 0x08 /* rcvtype: response */ +#define RX_FLAGS_ERROR 0x40 /* error in packet */ + +/* txp_rx_desc.rx_stat (if rx_flags & RX_FLAGS_ERROR bit set) */ +#define RX_ERROR_ADAPTER 0x00000000 /* adapter internal error */ +#define RX_ERROR_FIFO 0x00000001 /* fifo underrun */ +#define RX_ERROR_BADSSD 0x00000002 /* bad ssd */ +#define RX_ERROR_RUNT 0x00000003 /* runt packet */ +#define RX_ERROR_CRC 0x00000004 /* bad crc */ +#define RX_ERROR_OVERSIZE 0x00000005 /* oversized packet */ +#define RX_ERROR_ALIGN 0x00000006 /* alignment error */ +#define RX_ERROR_DRIBBLE 0x00000007 /* dribble bit */ + +/* txp_rx_desc.rx_stat (if rx_flags & RX_FLAGS_ERROR not bit set) */ +#define RX_STAT_PROTO_M 0x00000003 /* protocol mask */ +#define RX_STAT_PROTO_UK 0x00000000 /* unknown protocol */ +#define RX_STAT_PROTO_IPX 0x00000001 /* IPX */ +#define RX_STAT_PROTO_IP 0x00000002 /* IP */ +#define RX_STAT_PROTO_RSV 0x00000003 /* reserved */ +#define RX_STAT_VLAN 0x00000004 /* vlan tag (in rxd) */ +#define RX_STAT_IPFRAG 0x00000008 /* fragment, ipsec not done */ +#define RX_STAT_IPSEC 0x00000010 /* ipsec decoded packet */ +#define RX_STAT_IPCKSUMBAD 0x00000020 /* ip checksum failed */ +#define RX_STAT_UDPCKSUMBAD 0x00000040 /* udp checksum failed */ +#define RX_STAT_TCPCKSUMBAD 0x00000080 /* tcp checksum failed */ +#define RX_STAT_IPCKSUMGOOD 0x00000100 /* ip checksum succeeded */ +#define RX_STAT_UDPCKSUMGOOD 0x00000200 /* udp checksum succeeded */ +#define RX_STAT_TCPCKSUMGOOD 0x00000400 /* tcp checksum succeeded */ + + +struct txp_rxbuf_desc { + volatile u_int32_t rb_paddrlo; + volatile u_int32_t rb_paddrhi; +#ifdef notdef + volatile u_int32_t rb_vaddrlo; + volatile u_int32_t rb_vaddrhi; +#endif + union { + struct txp_swdesc *rb_sd; + u_int64_t rb_dummy; + } txp_rb_u; +}; + +#define rb_sd txp_rb_u.rb_sd + +/* Extension descriptor */ +struct txp_ext_desc { + volatile u_int32_t ext_1; + volatile u_int32_t ext_2; + volatile u_int32_t ext_3; + volatile u_int32_t ext_4; +}; + +struct txp_cmd_desc { + volatile u_int8_t cmd_flags; + volatile u_int8_t cmd_numdesc; + volatile u_int16_t cmd_id; + volatile u_int16_t cmd_seq; + volatile u_int16_t cmd_par1; + volatile u_int32_t cmd_par2; + volatile u_int32_t cmd_par3; +}; +#define CMD_FLAGS_TYPE_M 0x07 /* type mask */ +#define CMD_FLAGS_TYPE_FRAG 0x00 /* type: fragment */ +#define CMD_FLAGS_TYPE_DATA 0x01 /* type: data frame */ +#define CMD_FLAGS_TYPE_CMD 0x02 /* type: command frame */ +#define CMD_FLAGS_TYPE_OPT 0x03 /* type: options */ +#define CMD_FLAGS_TYPE_RX 0x04 /* type: command */ +#define CMD_FLAGS_TYPE_RESP 0x05 /* type: response */ +#define CMD_FLAGS_RESP 0x40 /* response requested */ +#define CMD_FLAGS_VALID 0x80 /* valid descriptor */ + +struct txp_rsp_desc { + volatile u_int8_t rsp_flags; + volatile u_int8_t rsp_numdesc; + volatile u_int16_t rsp_id; + volatile u_int16_t rsp_seq; + volatile u_int16_t rsp_par1; + volatile u_int32_t rsp_par2; + volatile u_int32_t rsp_par3; +}; +#define RSP_FLAGS_TYPE_M 0x07 /* type mask */ +#define RSP_FLAGS_TYPE_FRAG 0x00 /* type: fragment */ +#define RSP_FLAGS_TYPE_DATA 0x01 /* type: data frame */ +#define RSP_FLAGS_TYPE_CMD 0x02 /* type: command frame */ +#define RSP_FLAGS_TYPE_OPT 0x03 /* type: options */ +#define RSP_FLAGS_TYPE_RX 0x04 /* type: command */ +#define RSP_FLAGS_TYPE_RESP 0x05 /* type: response */ +#define RSP_FLAGS_ERROR 0x40 /* response error */ + +struct txp_frag_desc { + volatile u_int8_t frag_flags; /* type/descriptor flags */ + volatile u_int8_t frag_rsvd1; + volatile u_int16_t frag_len; /* bytes in this fragment */ + volatile u_int32_t frag_addrlo; /* phys addr low word */ + volatile u_int32_t frag_addrhi; /* phys addr high word */ + volatile u_int32_t frag_rsvd2; +}; +#define FRAG_FLAGS_TYPE_M 0x07 /* type mask */ +#define FRAG_FLAGS_TYPE_FRAG 0x00 /* type: fragment */ +#define FRAG_FLAGS_TYPE_DATA 0x01 /* type: data frame */ +#define FRAG_FLAGS_TYPE_CMD 0x02 /* type: command frame */ +#define FRAG_FLAGS_TYPE_OPT 0x03 /* type: options */ +#define FRAG_FLAGS_TYPE_RX 0x04 /* type: command */ +#define FRAG_FLAGS_TYPE_RESP 0x05 /* type: response */ + +struct txp_opt_desc { + u_int8_t opt_desctype:3, + opt_rsvd:1, + opt_type:4; + + u_int8_t opt_num; + u_int16_t opt_dep1; + u_int32_t opt_dep2; + u_int32_t opt_dep3; + u_int32_t opt_dep4; +}; + +struct txp_ipsec_desc { + u_int8_t ipsec_desctpe:3, + ipsec_rsvd:1, + ipsec_type:4; + + u_int8_t ipsec_num; + u_int16_t ipsec_flags; + u_int16_t ipsec_ah1; + u_int16_t ipsec_esp1; + u_int16_t ipsec_ah2; + u_int16_t ipsec_esp2; + u_int32_t ipsec_rsvd1; +}; + +struct txp_tcpseg_desc { + u_int8_t tcpseg_desctype:3, + tcpseg_rsvd:1, + tcpseg_type:4; + + u_int8_t tcpseg_num; + + u_int16_t tcpseg_mss:12, + tcpseg_misc:4; + + u_int32_t tcpseg_respaddr; + u_int32_t tcpseg_txbytes; + u_int32_t tcpseg_lss; +}; + +/* + * Transceiver types + */ +#define TXP_XCVR_10_HDX 0 +#define TXP_XCVR_10_FDX 1 +#define TXP_XCVR_100_HDX 2 +#define TXP_XCVR_100_FDX 3 +#define TXP_XCVR_AUTO 4 + +#define TXP_MEDIA_CRC 0x0004 /* crc strip disable */ +#define TXP_MEDIA_CD 0x0010 /* collision detection */ +#define TXP_MEDIA_CS 0x0020 /* carrier sense */ +#define TXP_MEDIA_POL 0x0400 /* polarity reversed */ +#define TXP_MEDIA_NOLINK 0x0800 /* 0 = link, 1 = no link */ + +/* + * receive filter bits (par1 to TXP_CMD_RX_FILTER_{READ|WRITE} + */ +#define TXP_RXFILT_DIRECT 0x0001 /* directed packets */ +#define TXP_RXFILT_ALLMULTI 0x0002 /* all multicast packets */ +#define TXP_RXFILT_BROADCAST 0x0004 /* broadcast packets */ +#define TXP_RXFILT_PROMISC 0x0008 /* promiscuous mode */ +#define TXP_RXFILT_HASHMULTI 0x0010 /* use multicast filter */ + +/* multicast polynomial */ +#define TXP_POLYNOMIAL 0x04c11db7 + +/* + * boot record (pointers to rings) + */ +struct txp_boot_record { + volatile u_int32_t br_hostvar_lo; /* host ring pointer */ + volatile u_int32_t br_hostvar_hi; + volatile u_int32_t br_txlopri_lo; /* tx low pri ring */ + volatile u_int32_t br_txlopri_hi; + volatile u_int32_t br_txlopri_siz; + volatile u_int32_t br_txhipri_lo; /* tx high pri ring */ + volatile u_int32_t br_txhipri_hi; + volatile u_int32_t br_txhipri_siz; + volatile u_int32_t br_rxlopri_lo; /* rx low pri ring */ + volatile u_int32_t br_rxlopri_hi; + volatile u_int32_t br_rxlopri_siz; + volatile u_int32_t br_rxbuf_lo; /* rx buffer ring */ + volatile u_int32_t br_rxbuf_hi; + volatile u_int32_t br_rxbuf_siz; + volatile u_int32_t br_cmd_lo; /* command ring */ + volatile u_int32_t br_cmd_hi; + volatile u_int32_t br_cmd_siz; + volatile u_int32_t br_resp_lo; /* response ring */ + volatile u_int32_t br_resp_hi; + volatile u_int32_t br_resp_siz; + volatile u_int32_t br_zero_lo; /* zero word */ + volatile u_int32_t br_zero_hi; + volatile u_int32_t br_rxhipri_lo; /* rx high pri ring */ + volatile u_int32_t br_rxhipri_hi; + volatile u_int32_t br_rxhipri_siz; +}; + +/* + * hostvar structure (shared with typhoon) + */ +struct txp_hostvar { + volatile u_int32_t hv_rx_hi_read_idx; /* host->arm */ + volatile u_int32_t hv_rx_lo_read_idx; /* host->arm */ + volatile u_int32_t hv_rx_buf_write_idx; /* host->arm */ + volatile u_int32_t hv_resp_read_idx; /* host->arm */ + volatile u_int32_t hv_tx_lo_desc_read_idx; /* arm->host */ + volatile u_int32_t hv_tx_hi_desc_read_idx; /* arm->host */ + volatile u_int32_t hv_rx_lo_write_idx; /* arm->host */ + volatile u_int32_t hv_rx_buf_read_idx; /* arm->host */ + volatile u_int32_t hv_cmd_read_idx; /* arm->host */ + volatile u_int32_t hv_resp_write_idx; /* arm->host */ + volatile u_int32_t hv_rx_hi_write_idx; /* arm->host */ +}; + +/* + * TYPHOON status register state (in TXP_A2H_0) + */ +#define STAT_ROM_CODE 0x00000001 +#define STAT_ROM_EEPROM_LOAD 0x00000002 +#define STAT_WAITING_FOR_BOOT 0x00000007 +#define STAT_RUNNING 0x00000009 +#define STAT_WAITING_FOR_HOST_REQUEST 0x0000000d +#define STAT_WAITING_FOR_SEGMENT 0x00000010 +#define STAT_SLEEPING 0x00000011 +#define STAT_HALTED 0x00000014 + +#define TX_ENTRIES 256 +#define RX_ENTRIES 128 +#define RXBUF_ENTRIES 256 +#define CMD_ENTRIES 32 +#define RSP_ENTRIES 32 + +#define OFFLOAD_TCPCKSUM 0x00000002 /* tcp checksum */ +#define OFFLOAD_UDPCKSUM 0x00000004 /* udp checksum */ +#define OFFLOAD_IPCKSUM 0x00000008 /* ip checksum */ +#define OFFLOAD_IPSEC 0x00000010 /* ipsec enable */ +#define OFFLOAD_BCAST 0x00000020 /* broadcast throttle */ +#define OFFLOAD_DHCP 0x00000040 /* dhcp prevention */ +#define OFFLOAD_VLAN 0x00000080 /* vlan enable */ +#define OFFLOAD_FILTER 0x00000100 /* filter enable */ +#define OFFLOAD_TCPSEG 0x00000200 /* tcp segmentation */ +#define OFFLOAD_MASK 0xfffffffe /* mask off low bit */ + +/* + * Macros for converting array indices to offsets within the descriptor + * arrays. The chip operates on offsets, but it's much easier for us + * to operate on indices. Assumes descriptor entries are 16 bytes. + */ +#define TXP_IDX2OFFSET(idx) ((idx) << 4) +#define TXP_OFFSET2IDX(off) ((off) >> 4) + +struct txp_cmd_ring { + struct txp_cmd_desc *base; + u_int32_t lastwrite; + u_int32_t size; +}; + +struct txp_rsp_ring { + struct txp_rsp_desc *base; + u_int32_t lastwrite; + u_int32_t size; +}; + +struct txp_tx_ring { + struct txp_tx_desc *r_desc; /* base address of descs */ + u_int32_t r_reg; /* register to activate */ + u_int32_t r_prod; /* producer */ + u_int32_t r_cons; /* consumer */ + u_int32_t r_cnt; /* # descs in use */ + volatile u_int32_t *r_off; /* hostvar index pointer */ +}; + +struct txp_swdesc { + struct mbuf * sd_mbuf; + bus_dmamap_t sd_map; +}; + +struct txp_rx_ring { + struct txp_rx_desc *r_desc; /* base address of descs */ + volatile u_int32_t *r_roff; /* hv read offset ptr */ + volatile u_int32_t *r_woff; /* hv write offset ptr */ +}; + +struct txp_ldata { + struct txp_boot_record txp_boot; + struct txp_hostvar txp_hostvar; + struct txp_tx_desc txp_txhiring[TX_ENTRIES]; + struct txp_tx_desc txp_txloring[TX_ENTRIES]; + struct txp_rxbuf_desc txp_rxbufs[RXBUF_ENTRIES]; + struct txp_rx_desc txp_rxhiring[RX_ENTRIES]; + struct txp_rx_desc txp_rxloring[RX_ENTRIES]; + struct txp_cmd_desc txp_cmdring[CMD_ENTRIES]; + struct txp_rsp_desc txp_rspring[RSP_ENTRIES]; + u_int32_t txp_zero; +}; + +struct txp_softc { + struct arpcom sc_arpcom; /* ethernet common */ + device_t sc_dev; + struct txp_hostvar *sc_hostvar; + struct txp_boot_record *sc_boot; + bus_space_handle_t sc_bh; /* bus handle (regs) */ + bus_space_tag_t sc_bt; /* bus tag (regs) */ + struct resource *sc_res; + struct resource *sc_irq; + void *sc_intrhand; + struct mtx sc_mtx; + struct txp_ldata *sc_ldata; + void *sc_fwbuf; + struct txp_cmd_ring sc_cmdring; + struct txp_rsp_ring sc_rspring; + struct txp_swdesc sc_txd[TX_ENTRIES]; + struct callout_handle sc_tick; + struct ifmedia sc_ifmedia; + struct txp_tx_ring sc_txhir, sc_txlor; + struct txp_rxbuf_desc *sc_rxbufs; + struct txp_rx_ring sc_rxhir, sc_rxlor; + u_int16_t sc_xcvr; + u_int16_t sc_seq; + int sc_cold; + u_int32_t sc_rx_capability, sc_tx_capability; +}; + +#define TXP_DEVNAME(sc) ((sc)->sc_cold ? "" : (sc)->sc_dev.dv_xname) + +struct txp_fw_file_header { + u_int8_t magicid[8]; /* TYPHOON\0 */ + u_int32_t version; + u_int32_t nsections; + u_int32_t addr; +}; + +struct txp_fw_section_header { + u_int32_t nbytes; + u_int16_t cksum; + u_int16_t reserved; + u_int32_t addr; +}; + +#define TXP_MAX_SEGLEN 0xffff +#define TXP_MAX_PKTLEN 0x0800 + +#define WRITE_REG(sc,reg,val) \ + bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, reg, val) +#define READ_REG(sc,reg) \ + bus_space_read_4((sc)->sc_bt, (sc)->sc_bh, reg) + +/* + * 3Com PCI vendor ID. + */ +#define TXP_VENDORID_3COM 0x10B7 + +/* + * 3cR990 device IDs + */ +#define TXP_DEVICEID_3CR990_TX_95 0x9902 +#define TXP_DEVICEID_3CR990_TX_97 0x9903 +#define TXP_DEVICEID_3CR990B_TXM 0x9904 +#define TXP_DEVICEID_3CR990_SRV_95 0x9908 +#define TXP_DEVICEID_3CR990_SRV_97 0x9909 +#define TXP_DEVICEID_3CR990B_SRV 0x990A + +struct txp_type { + u_int16_t txp_vid; + u_int16_t txp_did; + char *txp_name; +}; diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 36efa13928c6..74af39beda2b 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -162,6 +162,7 @@ device ppi # Parallel port interface device # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') +device txp # 3Com 3cR990 (``Typhoon'') # PCI Ethernet NICs that use the common MII bus controller code. # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 52de4c85bde2..9fb2a554cbcd 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -1710,6 +1710,7 @@ device miibus # in several Compaq Prosignia, Proliant and Deskpro systems. It also # supports several Olicom 10Mbps and 10/100 boards. # tx: SMC 9432 TX, BTX and FTX cards. (SMC EtherPower II serie) +# txp: Support for 3Com 3cR990 cards with the "Typhoon" chipset # vr: Support for various fast ethernet adapters based on the VIA # Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips, # including the D-Link DFE530TX (see 'rl' for DFE530TX+), the Hawking diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 59d3d67ad783..407b42aa5df3 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -78,6 +78,7 @@ SUBDIR= 3dfx \ tl \ twe \ tx \ + txp \ udbp \ ugen \ uhid \ diff --git a/sys/modules/txp/Makefile b/sys/modules/txp/Makefile new file mode 100644 index 000000000000..adc66255cfd5 --- /dev/null +++ b/sys/modules/txp/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/txp + +KMOD= if_txp +SRCS= if_txp.c opt_bdg.h device_if.h bus_if.h pci_if.h +CLEANFILES= vlan.h + +vlan.h: + touch vlan.h + +.include diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c index aa98bd02810d..2366277d48bf 100644 --- a/usr.sbin/sade/devices.c +++ b/usr.sbin/sade/devices.c @@ -112,6 +112,7 @@ static struct _devname { { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" }, { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" }, + { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" }, { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" }, { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" }, diff --git a/usr.sbin/sysinstall/devices.c b/usr.sbin/sysinstall/devices.c index aa98bd02810d..2366277d48bf 100644 --- a/usr.sbin/sysinstall/devices.c +++ b/usr.sbin/sysinstall/devices.c @@ -112,6 +112,7 @@ static struct _devname { { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" }, { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" }, + { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" }, { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" }, { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },