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