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