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