if_txp.c revision 219902
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 219902 2011-03-23 13:10:15Z jhb $"); 39119418Sobrien 4080219Swpaul/* 4180219Swpaul * Driver for 3c990 (Typhoon) Ethernet ASIC 4280219Swpaul */ 4380219Swpaul#include <sys/param.h> 4480219Swpaul#include <sys/systm.h> 45189714Syongari#include <sys/bus.h> 46189714Syongari#include <sys/endian.h> 47189714Syongari#include <sys/kernel.h> 48189714Syongari#include <sys/lock.h> 49189714Syongari#include <sys/malloc.h> 5080219Swpaul#include <sys/mbuf.h> 51129879Sphk#include <sys/module.h> 52189714Syongari#include <sys/mutex.h> 53189714Syongari#include <sys/queue.h> 54189714Syongari#include <sys/rman.h> 5580219Swpaul#include <sys/socket.h> 56189714Syongari#include <sys/sockio.h> 57189714Syongari#include <sys/sysctl.h> 58189714Syongari#include <sys/taskqueue.h> 5980219Swpaul 60189714Syongari#include <net/bpf.h> 6180219Swpaul#include <net/if.h> 6280219Swpaul#include <net/if_arp.h> 6380219Swpaul#include <net/ethernet.h> 6480219Swpaul#include <net/if_dl.h> 65189714Syongari#include <net/if_media.h> 6680219Swpaul#include <net/if_types.h> 6783115Sbrooks#include <net/if_vlan_var.h> 6880219Swpaul 6980219Swpaul#include <netinet/in.h> 7080219Swpaul#include <netinet/in_systm.h> 7180219Swpaul#include <netinet/ip.h> 7280219Swpaul 73189714Syongari#include <dev/mii/mii.h> 7480219Swpaul 7580219Swpaul#include <dev/pci/pcireg.h> 7680219Swpaul#include <dev/pci/pcivar.h> 7780219Swpaul 78189714Syongari#include <machine/bus.h> 79189714Syongari#include <machine/in_cksum.h> 8080219Swpaul 8180219Swpaul#include <dev/txp/if_txpreg.h> 8280219Swpaul#include <dev/txp/3c990img.h> 8380219Swpaul 84189714SyongariMODULE_DEPEND(txp, pci, 1, 1, 1); 85189714SyongariMODULE_DEPEND(txp, ether, 1, 1, 1); 8680219Swpaul 8780219Swpaul/* 88189714Syongari * XXX Known Typhoon firmware issues. 89189714Syongari * 90189714Syongari * 1. It seems that firmware has Tx TCP/UDP checksum offloading bug. 91189714Syongari * The firmware hangs when it's told to compute TCP/UDP checksum. 92189714Syongari * I'm not sure whether the firmware requires special alignment to 93189714Syongari * do checksum offloading but datasheet says nothing about that. 94189714Syongari * 2. Datasheet says nothing for maximum number of fragmented 95189714Syongari * descriptors supported. Experimentation shows up to 16 fragment 96189714Syongari * descriptors are supported in the firmware. For TSO case, upper 97189714Syongari * stack can send 64KB sized IP datagram plus link header size( 98189714Syongari * ethernet header + VLAN tag) frame but controller can handle up 99189714Syongari * to 64KB frame given that PAGE_SIZE is 4KB(i.e. 16 * PAGE_SIZE). 100189714Syongari * Because frames that need TSO operation of hardware can be 101189714Syongari * larger than 64KB I disabled TSO capability. TSO operation for 102189714Syongari * less than or equal to 16 fragment descriptors works without 103189714Syongari * problems, though. 104189714Syongari * 3. VLAN hardware tag stripping is always enabled in the firmware 105189714Syongari * even if it's explicitly told to not strip the tag. It's 106189714Syongari * possible to add the tag back in Rx handler if VLAN hardware 107189714Syongari * tag is not active but I didn't try that as it would be 108189714Syongari * layering violation. 109189714Syongari * 4. TXP_CMD_RECV_BUFFER_CONTROL does not work as expected in 110189714Syongari * datasheet such that driver should handle the alignment 111189714Syongari * restriction by copying received frame to align the frame on 112189714Syongari * 32bit boundary on strict-alignment architectures. This adds a 113189714Syongari * lot of CPU burden and it effectively reduce Rx performance on 114189714Syongari * strict-alignment architectures(e.g. sparc64, arm, mips and ia64). 115189714Syongari * 116189714Syongari * Unfortunately it seems that 3Com have no longer interests in 117189714Syongari * releasing fixed firmware so we may have to live with these bugs. 118189714Syongari */ 119189714Syongari 120189714Syongari#define TXP_CSUM_FEATURES (CSUM_IP) 121189714Syongari 122189714Syongari/* 12380219Swpaul * Various supported device vendors/types and their names. 12480219Swpaul */ 12580219Swpaulstatic struct txp_type txp_devs[] = { 12680219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95, 12780219Swpaul "3Com 3cR990-TX-95 Etherlink with 3XP Processor" }, 12880219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97, 12980219Swpaul "3Com 3cR990-TX-97 Etherlink with 3XP Processor" }, 13080219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM, 13180219Swpaul "3Com 3cR990B-TXM Etherlink with 3XP Processor" }, 13280219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95, 13380219Swpaul "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" }, 13480219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97, 13580219Swpaul "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" }, 13680219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV, 13780219Swpaul "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" }, 13880219Swpaul { 0, 0, NULL } 13980219Swpaul}; 14080219Swpaul 141149678Sjhbstatic int txp_probe(device_t); 142149678Sjhbstatic int txp_attach(device_t); 143149678Sjhbstatic int txp_detach(device_t); 144189714Syongaristatic int txp_shutdown(device_t); 145189714Syongaristatic int txp_suspend(device_t); 146189714Syongaristatic int txp_resume(device_t); 147189714Syongaristatic int txp_intr(void *); 148189714Syongaristatic void txp_int_task(void *, int); 149149678Sjhbstatic void txp_tick(void *); 150149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t); 151149678Sjhbstatic void txp_start(struct ifnet *); 152151772Sjhbstatic void txp_start_locked(struct ifnet *); 153189714Syongaristatic int txp_encap(struct txp_softc *, struct txp_tx_ring *, struct mbuf **); 154149678Sjhbstatic void txp_stop(struct txp_softc *); 155149678Sjhbstatic void txp_init(void *); 156151772Sjhbstatic void txp_init_locked(struct txp_softc *); 157189714Syongaristatic void txp_watchdog(struct txp_softc *); 15880219Swpaul 159189714Syongaristatic int txp_reset(struct txp_softc *); 160189714Syongaristatic int txp_boot(struct txp_softc *, uint32_t); 161189714Syongaristatic int txp_sleep(struct txp_softc *, int); 162189714Syongaristatic int txp_wait(struct txp_softc *, uint32_t); 16392739Salfredstatic int txp_download_fw(struct txp_softc *); 16492739Salfredstatic int txp_download_fw_wait(struct txp_softc *); 165149678Sjhbstatic int txp_download_fw_section(struct txp_softc *, 16692739Salfred struct txp_fw_section_header *, int); 16792739Salfredstatic int txp_alloc_rings(struct txp_softc *); 168189714Syongaristatic void txp_init_rings(struct txp_softc *); 169189714Syongaristatic int txp_dma_alloc(struct txp_softc *, char *, bus_dma_tag_t *, 170189714Syongari bus_size_t, bus_size_t, bus_dmamap_t *, void **, bus_size_t, bus_addr_t *); 171189714Syongaristatic void txp_dma_free(struct txp_softc *, bus_dma_tag_t *, bus_dmamap_t *, 172189714Syongari void **); 173189714Syongaristatic void txp_free_rings(struct txp_softc *); 17492739Salfredstatic int txp_rxring_fill(struct txp_softc *); 17592739Salfredstatic void txp_rxring_empty(struct txp_softc *); 17692739Salfredstatic void txp_set_filter(struct txp_softc *); 17780219Swpaul 17892739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *); 179189689Syongaristatic int txp_command(struct txp_softc *, uint16_t, uint16_t, uint32_t, 180189689Syongari uint32_t, uint16_t *, uint32_t *, uint32_t *, int); 181189714Syongaristatic int txp_ext_command(struct txp_softc *, uint16_t, uint16_t, 182189689Syongari uint32_t, uint32_t, struct txp_ext_desc *, uint8_t, 18392739Salfred struct txp_rsp_desc **, int); 184189714Syongaristatic int txp_response(struct txp_softc *, uint16_t, uint16_t, 18592739Salfred struct txp_rsp_desc **); 186149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *, 18792739Salfred struct txp_rsp_desc *); 188189714Syongaristatic int txp_set_capabilities(struct txp_softc *); 18980219Swpaul 19092739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *); 19192739Salfredstatic int txp_ifmedia_upd(struct ifnet *); 19280219Swpaul#ifdef TXP_DEBUG 19392739Salfredstatic void txp_show_descriptor(void *); 19480219Swpaul#endif 19592739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *); 19692739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *); 197189714Syongari#ifndef __NO_STRICT_ALIGNMENT 198189714Syongaristatic __inline void txp_fixup_rx(struct mbuf *); 19980219Swpaul#endif 200189714Syongaristatic int txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *, int); 201189714Syongaristatic void txp_stats_save(struct txp_softc *); 202189714Syongaristatic void txp_stats_update(struct txp_softc *, struct txp_rsp_desc *); 203189714Syongaristatic void txp_sysctl_node(struct txp_softc *); 204189714Syongaristatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); 205189714Syongaristatic int sysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS); 20680219Swpaul 207189714Syongaristatic int prefer_iomap = 0; 208189714SyongariTUNABLE_INT("hw.txp.prefer_iomap", &prefer_iomap); 209189714Syongari 21080219Swpaulstatic device_method_t txp_methods[] = { 21180219Swpaul /* Device interface */ 21280219Swpaul DEVMETHOD(device_probe, txp_probe), 21380219Swpaul DEVMETHOD(device_attach, txp_attach), 21480219Swpaul DEVMETHOD(device_detach, txp_detach), 21580219Swpaul DEVMETHOD(device_shutdown, txp_shutdown), 216189714Syongari DEVMETHOD(device_suspend, txp_suspend), 217189714Syongari DEVMETHOD(device_resume, txp_resume), 218189714Syongari 219189714Syongari { NULL, NULL } 22080219Swpaul}; 22180219Swpaul 22280219Swpaulstatic driver_t txp_driver = { 22380219Swpaul "txp", 22480219Swpaul txp_methods, 22580219Swpaul sizeof(struct txp_softc) 22680219Swpaul}; 22780219Swpaul 22880219Swpaulstatic devclass_t txp_devclass; 22980219Swpaul 230113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0); 23180219Swpaul 23280219Swpaulstatic int 233189685Syongaritxp_probe(device_t dev) 23480219Swpaul{ 23580219Swpaul struct txp_type *t; 23680219Swpaul 23780219Swpaul t = txp_devs; 23880219Swpaul 239189688Syongari while (t->txp_name != NULL) { 24080219Swpaul if ((pci_get_vendor(dev) == t->txp_vid) && 24180219Swpaul (pci_get_device(dev) == t->txp_did)) { 24280219Swpaul device_set_desc(dev, t->txp_name); 243189688Syongari return (BUS_PROBE_DEFAULT); 24480219Swpaul } 24580219Swpaul t++; 24680219Swpaul } 24780219Swpaul 248189688Syongari return (ENXIO); 24980219Swpaul} 25080219Swpaul 25180219Swpaulstatic int 252189685Syongaritxp_attach(device_t dev) 25380219Swpaul{ 25480219Swpaul struct txp_softc *sc; 25580219Swpaul struct ifnet *ifp; 256189714Syongari struct txp_rsp_desc *rsp; 257189689Syongari uint16_t p1; 258189714Syongari uint32_t p2, reg; 259189714Syongari int error = 0, pmc, rid; 260189714Syongari uint8_t eaddr[ETHER_ADDR_LEN], *ver; 26180219Swpaul 26280219Swpaul sc = device_get_softc(dev); 26380219Swpaul sc->sc_dev = dev; 26480219Swpaul 26593818Sjhb mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 266151772Sjhb MTX_DEF); 267151772Sjhb callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0); 268189714Syongari TASK_INIT(&sc->sc_int_task, 0, txp_int_task, sc); 269189714Syongari TAILQ_INIT(&sc->sc_busy_list); 270189714Syongari TAILQ_INIT(&sc->sc_free_list); 271151772Sjhb 272189714Syongari ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts); 273189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL); 274189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL); 275189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); 276189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL); 277189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL); 278189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); 279189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); 280189714Syongari 28180219Swpaul pci_enable_busmaster(dev); 282189714Syongari /* Prefer memory space register mapping over IO space. */ 283189714Syongari if (prefer_iomap == 0) { 284189714Syongari sc->sc_res_id = PCIR_BAR(1); 285189714Syongari sc->sc_res_type = SYS_RES_MEMORY; 286189714Syongari } else { 287189714Syongari sc->sc_res_id = PCIR_BAR(0); 288189714Syongari sc->sc_res_type = SYS_RES_IOPORT; 289189714Syongari } 290189714Syongari sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type, 291189714Syongari &sc->sc_res_id, RF_ACTIVE); 292189714Syongari if (sc->sc_res == NULL && prefer_iomap == 0) { 293189714Syongari sc->sc_res_id = PCIR_BAR(0); 294189714Syongari sc->sc_res_type = SYS_RES_IOPORT; 295189714Syongari sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type, 296189714Syongari &sc->sc_res_id, RF_ACTIVE); 297189714Syongari } 29880219Swpaul if (sc->sc_res == NULL) { 29980219Swpaul device_printf(dev, "couldn't map ports/memory\n"); 300189714Syongari ifmedia_removeall(&sc->sc_ifmedia); 301189714Syongari mtx_destroy(&sc->sc_mtx); 302189714Syongari return (ENXIO); 30380219Swpaul } 30480219Swpaul 305189714Syongari /* Enable MWI. */ 306189714Syongari reg = pci_read_config(dev, PCIR_COMMAND, 2); 307189714Syongari reg |= PCIM_CMD_MWRICEN; 308189714Syongari pci_write_config(dev, PCIR_COMMAND, reg, 2); 309189714Syongari /* Check cache line size. */ 310189714Syongari reg = pci_read_config(dev, PCIR_CACHELNSZ, 1); 311189714Syongari reg <<= 4; 312189714Syongari if (reg == 0 || (reg % 16) != 0) 313189714Syongari device_printf(sc->sc_dev, 314189714Syongari "invalid cache line size : %u\n", reg); 31580219Swpaul 31680219Swpaul /* Allocate interrupt */ 31780219Swpaul rid = 0; 318127135Snjl sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 31980219Swpaul RF_SHAREABLE | RF_ACTIVE); 32080219Swpaul 32180219Swpaul if (sc->sc_irq == NULL) { 32280219Swpaul device_printf(dev, "couldn't map interrupt\n"); 32380219Swpaul error = ENXIO; 32480219Swpaul goto fail; 32580219Swpaul } 32680219Swpaul 327189714Syongari if ((error = txp_alloc_rings(sc)) != 0) 32880219Swpaul goto fail; 329189714Syongari txp_init_rings(sc); 330189714Syongari txp_sysctl_node(sc); 331189714Syongari /* Reset controller and make it reload sleep image. */ 332189714Syongari if (txp_reset(sc) != 0) { 333170596Syongari error = ENXIO; 334170596Syongari goto fail; 335170596Syongari } 33680219Swpaul 337189714Syongari /* Let controller boot from sleep image. */ 338189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) { 339189714Syongari device_printf(sc->sc_dev, "could not boot sleep image\n"); 340170596Syongari error = ENXIO; 341170596Syongari goto fail; 342170596Syongari } 34380219Swpaul 344189714Syongari /* Get station address. */ 34580219Swpaul if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0, 346189714Syongari &p1, &p2, NULL, TXP_CMD_WAIT)) { 347149678Sjhb error = ENXIO; 34880219Swpaul goto fail; 34980219Swpaul } 35080219Swpaul 351189714Syongari p1 = le16toh(p1); 352189689Syongari eaddr[0] = ((uint8_t *)&p1)[1]; 353189689Syongari eaddr[1] = ((uint8_t *)&p1)[0]; 354189714Syongari p2 = le32toh(p2); 355189689Syongari eaddr[2] = ((uint8_t *)&p2)[3]; 356189689Syongari eaddr[3] = ((uint8_t *)&p2)[2]; 357189689Syongari eaddr[4] = ((uint8_t *)&p2)[1]; 358189689Syongari eaddr[5] = ((uint8_t *)&p2)[0]; 35980219Swpaul 360189714Syongari ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 361189714Syongari if (ifp == NULL) { 362189714Syongari device_printf(dev, "can not allocate ifnet structure\n"); 363189714Syongari error = ENOSPC; 364189714Syongari goto fail; 365189714Syongari } 36680219Swpaul 367189714Syongari /* 368189714Syongari * Show sleep image version information which may help to 369189714Syongari * diagnose sleep image specific issues. 370189714Syongari */ 371189714Syongari rsp = NULL; 372189714Syongari if (txp_ext_command(sc, TXP_CMD_READ_VERSION, 0, 0, 0, NULL, 0, 373189714Syongari &rsp, TXP_CMD_WAIT)) { 374189714Syongari device_printf(dev, "can not read sleep image version\n"); 375189714Syongari error = ENXIO; 376189714Syongari goto fail; 377189714Syongari } 378189714Syongari if (rsp->rsp_numdesc == 0) { 379189714Syongari p2 = le32toh(rsp->rsp_par2) & 0xFFFF; 380189714Syongari device_printf(dev, "Typhoon 1.0 sleep image (2000/%02u/%02u)\n", 381189714Syongari p2 >> 8, p2 & 0xFF); 382189714Syongari } else if (rsp->rsp_numdesc == 2) { 383189714Syongari p2 = le32toh(rsp->rsp_par2); 384189714Syongari ver = (uint8_t *)(rsp + 1); 385189714Syongari /* 386189714Syongari * Even if datasheet says the command returns a NULL 387189714Syongari * terminated version string, explicitly terminate 388189714Syongari * the string. Given that several bugs of firmware 389189714Syongari * I can't trust this simple one. 390189714Syongari */ 391189714Syongari ver[25] = '\0'; 392189714Syongari device_printf(dev, 393189714Syongari "Typhoon 1.1+ sleep image %02u.%03u.%03u %s\n", 394189714Syongari p2 >> 24, (p2 >> 12) & 0xFFF, p2 & 0xFFF, ver); 395189714Syongari } else { 396189714Syongari p2 = le32toh(rsp->rsp_par2); 397189714Syongari device_printf(dev, 398189714Syongari "Unknown Typhoon sleep image version: %u:0x%08x\n", 399189714Syongari rsp->rsp_numdesc, p2); 400189714Syongari } 401189714Syongari if (rsp != NULL) 402189714Syongari free(rsp, M_DEVBUF); 40380219Swpaul 40480219Swpaul sc->sc_xcvr = TXP_XCVR_AUTO; 40580219Swpaul txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0, 406189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 407189714Syongari ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO); 40880219Swpaul 40980219Swpaul ifp->if_softc = sc; 410121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 411151772Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 41280219Swpaul ifp->if_ioctl = txp_ioctl; 41380219Swpaul ifp->if_start = txp_start; 41480219Swpaul ifp->if_init = txp_init; 415189714Syongari ifp->if_snd.ifq_drv_maxlen = TX_ENTRIES - 1; 416189714Syongari IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); 417189714Syongari IFQ_SET_READY(&ifp->if_snd); 41880219Swpaul /* 419189714Syongari * It's possible to read firmware's offload capability but 420189714Syongari * we have not downloaded the firmware yet so announce 421189714Syongari * working capability here. We're not interested in IPSec 422189714Syongari * capability and due to the lots of firmware bug we can't 423189714Syongari * advertise the whole capability anyway. 42480219Swpaul */ 425189714Syongari ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM; 426219902Sjhb if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0) 427189714Syongari ifp->if_capabilities |= IFCAP_WOL_MAGIC; 428189714Syongari /* Enable all capabilities. */ 429189714Syongari ifp->if_capenable = ifp->if_capabilities; 430189714Syongari 431147256Sbrooks ether_ifattach(ifp, eaddr); 432149678Sjhb 433189714Syongari /* VLAN capability setup. */ 434189714Syongari ifp->if_capabilities |= IFCAP_VLAN_MTU; 435189714Syongari ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; 436189714Syongari ifp->if_capenable = ifp->if_capabilities; 437189714Syongari /* Tell the upper layer(s) we support long frames. */ 438189714Syongari ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 439189714Syongari 440189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 441189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 442189714Syongari 443189714Syongari /* Create local taskq. */ 444189714Syongari sc->sc_tq = taskqueue_create_fast("txp_taskq", M_WAITOK, 445189714Syongari taskqueue_thread_enqueue, &sc->sc_tq); 446189714Syongari if (sc->sc_tq == NULL) { 447189714Syongari device_printf(dev, "could not create taskqueue.\n"); 448189714Syongari ether_ifdetach(ifp); 449189714Syongari error = ENXIO; 450189714Syongari goto fail; 451189714Syongari } 452189714Syongari taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", 453189714Syongari device_get_nameunit(sc->sc_dev)); 454189714Syongari 455189714Syongari /* Put controller into sleep. */ 456189714Syongari if (txp_sleep(sc, 0) != 0) { 457189714Syongari ether_ifdetach(ifp); 458189714Syongari error = ENXIO; 459189714Syongari goto fail; 460189714Syongari } 461189714Syongari 462151772Sjhb error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 463189714Syongari txp_intr, NULL, sc, &sc->sc_intrhand); 464149678Sjhb 465189714Syongari if (error != 0) { 466149678Sjhb ether_ifdetach(ifp); 467189714Syongari device_printf(dev, "couldn't set up interrupt handler.\n"); 468149678Sjhb goto fail; 469149678Sjhb } 470149678Sjhb 471189688Syongari return (0); 47280219Swpaul 47380219Swpaulfail: 474189714Syongari if (error != 0) 475189714Syongari txp_detach(dev); 476189688Syongari return (error); 47780219Swpaul} 47880219Swpaul 47980219Swpaulstatic int 480189685Syongaritxp_detach(device_t dev) 48180219Swpaul{ 48280219Swpaul struct txp_softc *sc; 48380219Swpaul struct ifnet *ifp; 48480219Swpaul 48580219Swpaul sc = device_get_softc(dev); 486189714Syongari 487147256Sbrooks ifp = sc->sc_ifp; 488189714Syongari if (device_is_attached(dev)) { 489189714Syongari TXP_LOCK(sc); 490189714Syongari sc->sc_flags |= TXP_FLAG_DETACH; 491189714Syongari txp_stop(sc); 492189714Syongari TXP_UNLOCK(sc); 493189714Syongari callout_drain(&sc->sc_tick); 494189714Syongari taskqueue_drain(sc->sc_tq, &sc->sc_int_task); 495189714Syongari ether_ifdetach(ifp); 496189714Syongari } 497189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 49880219Swpaul 49980219Swpaul ifmedia_removeall(&sc->sc_ifmedia); 50080219Swpaul if (sc->sc_intrhand != NULL) 50180219Swpaul bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand); 50280219Swpaul if (sc->sc_irq != NULL) 50380219Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq); 50480219Swpaul if (sc->sc_res != NULL) 505189714Syongari bus_release_resource(dev, sc->sc_res_type, sc->sc_res_id, 506189714Syongari sc->sc_res); 507189714Syongari if (sc->sc_ifp != NULL) { 508150306Simp if_free(sc->sc_ifp); 509189714Syongari sc->sc_ifp = NULL; 510189714Syongari } 511189714Syongari txp_free_rings(sc); 512189714Syongari mtx_destroy(&sc->sc_mtx); 51380219Swpaul 51480219Swpaul return (0); 51580219Swpaul} 51680219Swpaul 51780219Swpaulstatic int 518189714Syongaritxp_reset(struct txp_softc *sc) 51980219Swpaul{ 520189689Syongari uint32_t r; 52180219Swpaul int i; 52280219Swpaul 523189714Syongari /* Disable interrupts. */ 524189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 525189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 526189714Syongari /* Ack all pending interrupts. */ 527189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 528189714Syongari 52992643Sjeff r = 0; 53080219Swpaul WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL); 53180219Swpaul DELAY(1000); 53280219Swpaul WRITE_REG(sc, TXP_SRR, 0); 53380219Swpaul 534189714Syongari /* Should wait max 6 seconds. */ 53580219Swpaul for (i = 0; i < 6000; i++) { 53680219Swpaul r = READ_REG(sc, TXP_A2H_0); 53780219Swpaul if (r == STAT_WAITING_FOR_HOST_REQUEST) 53880219Swpaul break; 53980219Swpaul DELAY(1000); 54080219Swpaul } 54180219Swpaul 542189714Syongari if (r != STAT_WAITING_FOR_HOST_REQUEST) 54380219Swpaul device_printf(sc->sc_dev, "reset hung\n"); 544189714Syongari 545189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 546189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 547189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 548189714Syongari 549189714Syongari /* 550189714Syongari * Give more time to complete loading sleep image before 551189714Syongari * trying to boot from sleep image. 552189714Syongari */ 553189714Syongari DELAY(5000); 554189714Syongari 555189714Syongari return (0); 556189714Syongari} 557189714Syongari 558189714Syongaristatic int 559189714Syongaritxp_boot(struct txp_softc *sc, uint32_t state) 560189714Syongari{ 561189714Syongari 562189714Syongari /* See if it's waiting for boot, and try to boot it. */ 563189714Syongari if (txp_wait(sc, state) != 0) { 564189714Syongari device_printf(sc->sc_dev, "not waiting for boot\n"); 565189714Syongari return (ENXIO); 56680219Swpaul } 56780219Swpaul 568189714Syongari WRITE_REG(sc, TXP_H2A_2, TXP_ADDR_HI(sc->sc_ldata.txp_boot_paddr)); 569189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 570189714Syongari WRITE_REG(sc, TXP_H2A_1, TXP_ADDR_LO(sc->sc_ldata.txp_boot_paddr)); 571189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 572189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD); 573189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 574189714Syongari 575189714Syongari /* See if it booted. */ 576189714Syongari if (txp_wait(sc, STAT_RUNNING) != 0) { 577189714Syongari device_printf(sc->sc_dev, "firmware not running\n"); 578189714Syongari return (ENXIO); 579189714Syongari } 580189714Syongari 581189714Syongari /* Clear TX and CMD ring write registers. */ 582189714Syongari WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL); 583189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 584189714Syongari WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL); 585189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 586189714Syongari WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL); 587189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 588189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL); 589189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 590189714Syongari 59180219Swpaul return (0); 59280219Swpaul} 59380219Swpaul 59480219Swpaulstatic int 595189685Syongaritxp_download_fw(struct txp_softc *sc) 59680219Swpaul{ 59780219Swpaul struct txp_fw_file_header *fileheader; 59880219Swpaul struct txp_fw_section_header *secthead; 599189714Syongari int sect; 600189714Syongari uint32_t error, ier, imr; 60180219Swpaul 602189714Syongari TXP_LOCK_ASSERT(sc); 603189714Syongari 604189022Syongari error = 0; 60580219Swpaul ier = READ_REG(sc, TXP_IER); 60680219Swpaul WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0); 60780219Swpaul 60880219Swpaul imr = READ_REG(sc, TXP_IMR); 60980219Swpaul WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0); 61080219Swpaul 611189714Syongari if (txp_wait(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) { 61280219Swpaul device_printf(sc->sc_dev, "not waiting for host request\n"); 613189714Syongari error = ETIMEDOUT; 614189022Syongari goto fail; 61580219Swpaul } 61680219Swpaul 617189714Syongari /* Ack the status. */ 61880219Swpaul WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); 61980219Swpaul 62080219Swpaul fileheader = (struct txp_fw_file_header *)tc990image; 62180219Swpaul if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) { 622189714Syongari device_printf(sc->sc_dev, "firmware invalid magic\n"); 623189022Syongari goto fail; 62480219Swpaul } 62580219Swpaul 626189714Syongari /* Tell boot firmware to get ready for image. */ 627189714Syongari WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr)); 628189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 629189714Syongari WRITE_REG(sc, TXP_H2A_2, le32toh(fileheader->hmac[0])); 630189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 631189714Syongari WRITE_REG(sc, TXP_H2A_3, le32toh(fileheader->hmac[1])); 632189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 633189714Syongari WRITE_REG(sc, TXP_H2A_4, le32toh(fileheader->hmac[2])); 634189714Syongari TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE); 635189714Syongari WRITE_REG(sc, TXP_H2A_5, le32toh(fileheader->hmac[3])); 636189714Syongari TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE); 637189714Syongari WRITE_REG(sc, TXP_H2A_6, le32toh(fileheader->hmac[4])); 638189714Syongari TXP_BARRIER(sc, TXP_H2A_6, 4, BUS_SPACE_BARRIER_WRITE); 63980219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE); 640189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 64180219Swpaul 64280219Swpaul if (txp_download_fw_wait(sc)) { 643189714Syongari device_printf(sc->sc_dev, "firmware wait failed, initial\n"); 644189714Syongari error = ETIMEDOUT; 645189022Syongari goto fail; 64680219Swpaul } 64780219Swpaul 648189689Syongari secthead = (struct txp_fw_section_header *)(((uint8_t *)tc990image) + 64980219Swpaul sizeof(struct txp_fw_file_header)); 65080219Swpaul 651189714Syongari for (sect = 0; sect < le32toh(fileheader->nsections); sect++) { 652189714Syongari if ((error = txp_download_fw_section(sc, secthead, sect)) != 0) 653189022Syongari goto fail; 65480219Swpaul secthead = (struct txp_fw_section_header *) 655189714Syongari (((uint8_t *)secthead) + le32toh(secthead->nbytes) + 65680219Swpaul sizeof(*secthead)); 65780219Swpaul } 65880219Swpaul 65980219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE); 660189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 66180219Swpaul 662189714Syongari if (txp_wait(sc, STAT_WAITING_FOR_BOOT) != 0) { 66380219Swpaul device_printf(sc->sc_dev, "not waiting for boot\n"); 664189714Syongari error = ETIMEDOUT; 665189022Syongari goto fail; 66680219Swpaul } 66780219Swpaul 668189022Syongarifail: 66980219Swpaul WRITE_REG(sc, TXP_IER, ier); 67080219Swpaul WRITE_REG(sc, TXP_IMR, imr); 67180219Swpaul 672189022Syongari return (error); 67380219Swpaul} 67480219Swpaul 67580219Swpaulstatic int 676189685Syongaritxp_download_fw_wait(struct txp_softc *sc) 67780219Swpaul{ 678189714Syongari uint32_t i; 67980219Swpaul 680189714Syongari TXP_LOCK_ASSERT(sc); 681189714Syongari 682189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 683189714Syongari if ((READ_REG(sc, TXP_ISR) & TXP_INT_A2H_0) != 0) 68480219Swpaul break; 68580219Swpaul DELAY(50); 68680219Swpaul } 68780219Swpaul 688189714Syongari if (i == TXP_TIMEOUT) { 689189714Syongari device_printf(sc->sc_dev, "firmware wait failed comm0\n"); 690189714Syongari return (ETIMEDOUT); 69180219Swpaul } 69280219Swpaul 69380219Swpaul WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); 69480219Swpaul 695189714Syongari if (READ_REG(sc, TXP_A2H_0) != STAT_WAITING_FOR_SEGMENT) { 696189714Syongari device_printf(sc->sc_dev, "firmware not waiting for segment\n"); 697189714Syongari return (ETIMEDOUT); 69880219Swpaul } 69980219Swpaul return (0); 70080219Swpaul} 70180219Swpaul 70280219Swpaulstatic int 703189685Syongaritxp_download_fw_section(struct txp_softc *sc, 704189685Syongari struct txp_fw_section_header *sect, int sectnum) 70580219Swpaul{ 706189714Syongari bus_dma_tag_t sec_tag; 707189714Syongari bus_dmamap_t sec_map; 708189714Syongari bus_addr_t sec_paddr; 709189714Syongari uint8_t *sec_buf; 71080219Swpaul int rseg, err = 0; 71180219Swpaul struct mbuf m; 712189689Syongari uint16_t csum; 71380219Swpaul 714189714Syongari TXP_LOCK_ASSERT(sc); 715189714Syongari 716189714Syongari /* Skip zero length sections. */ 717189714Syongari if (le32toh(sect->nbytes) == 0) 71880219Swpaul return (0); 71980219Swpaul 720189714Syongari /* Make sure we aren't past the end of the image. */ 721189689Syongari rseg = ((uint8_t *)sect) - ((uint8_t *)tc990image); 72280219Swpaul if (rseg >= sizeof(tc990image)) { 723189714Syongari device_printf(sc->sc_dev, 724189714Syongari "firmware invalid section address, section %d\n", sectnum); 725189714Syongari return (EIO); 72680219Swpaul } 72780219Swpaul 728189714Syongari /* Make sure this section doesn't go past the end. */ 729189714Syongari rseg += le32toh(sect->nbytes); 73080219Swpaul if (rseg >= sizeof(tc990image)) { 731189714Syongari device_printf(sc->sc_dev, "firmware truncated section %d\n", 73280219Swpaul sectnum); 733189714Syongari return (EIO); 73480219Swpaul } 73580219Swpaul 736189714Syongari sec_tag = NULL; 737189714Syongari sec_map = NULL; 738189714Syongari sec_buf = NULL; 739189714Syongari /* XXX */ 740189714Syongari TXP_UNLOCK(sc); 741189714Syongari err = txp_dma_alloc(sc, "firmware sections", &sec_tag, sizeof(uint32_t), 742189714Syongari 0, &sec_map, (void **)&sec_buf, le32toh(sect->nbytes), &sec_paddr); 743189714Syongari TXP_LOCK(sc); 744189714Syongari if (err != 0) 745189714Syongari goto bail; 746189714Syongari bcopy(((uint8_t *)sect) + sizeof(*sect), sec_buf, 747189714Syongari le32toh(sect->nbytes)); 74880219Swpaul 74980219Swpaul /* 75080219Swpaul * dummy up mbuf and verify section checksum 75180219Swpaul */ 75280219Swpaul m.m_type = MT_DATA; 75380219Swpaul m.m_next = m.m_nextpkt = NULL; 754189714Syongari m.m_len = le32toh(sect->nbytes); 755189714Syongari m.m_data = sec_buf; 75680219Swpaul m.m_flags = 0; 757189714Syongari csum = in_cksum(&m, le32toh(sect->nbytes)); 75880219Swpaul if (csum != sect->cksum) { 759189714Syongari device_printf(sc->sc_dev, 760189714Syongari "firmware section %d, bad cksum (expected 0x%x got 0x%x)\n", 761189714Syongari sectnum, le16toh(sect->cksum), csum); 762189714Syongari err = EIO; 76380219Swpaul goto bail; 76480219Swpaul } 76580219Swpaul 766189714Syongari bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_PREWRITE); 767189714Syongari 768189714Syongari WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes)); 769189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 770189714Syongari WRITE_REG(sc, TXP_H2A_2, le16toh(sect->cksum)); 771189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 772189714Syongari WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr)); 773189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 774189714Syongari WRITE_REG(sc, TXP_H2A_4, TXP_ADDR_HI(sec_paddr)); 775189714Syongari TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE); 776189714Syongari WRITE_REG(sc, TXP_H2A_5, TXP_ADDR_LO(sec_paddr)); 777189714Syongari TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE); 77880219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE); 779189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 78080219Swpaul 78180219Swpaul if (txp_download_fw_wait(sc)) { 782189714Syongari device_printf(sc->sc_dev, 783189714Syongari "firmware wait failed, section %d\n", sectnum); 784189714Syongari err = ETIMEDOUT; 78580219Swpaul } 78680219Swpaul 787189714Syongari bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_POSTWRITE); 78880219Swpaulbail: 789189714Syongari txp_dma_free(sc, &sec_tag, &sec_map, (void **)&sec_buf); 79080219Swpaul return (err); 79180219Swpaul} 79280219Swpaul 793189714Syongaristatic int 794189685Syongaritxp_intr(void *vsc) 79580219Swpaul{ 796189714Syongari struct txp_softc *sc; 797189714Syongari uint32_t status; 798189714Syongari 799189714Syongari sc = vsc; 800189714Syongari status = READ_REG(sc, TXP_ISR); 801189714Syongari if ((status & TXP_INT_LATCH) == 0) 802189714Syongari return (FILTER_STRAY); 803189714Syongari WRITE_REG(sc, TXP_ISR, status); 804189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 805189714Syongari taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task); 806189714Syongari 807189714Syongari return (FILTER_HANDLED); 808189714Syongari} 809189714Syongari 810189714Syongaristatic void 811189714Syongaritxp_int_task(void *arg, int pending) 812189714Syongari{ 813189714Syongari struct txp_softc *sc; 814189714Syongari struct ifnet *ifp; 815189714Syongari struct txp_hostvar *hv; 816189689Syongari uint32_t isr; 817189714Syongari int more; 81880219Swpaul 819189714Syongari sc = (struct txp_softc *)arg; 820189714Syongari 821151772Sjhb TXP_LOCK(sc); 822189714Syongari ifp = sc->sc_ifp; 823189714Syongari hv = sc->sc_hostvar; 82480219Swpaul isr = READ_REG(sc, TXP_ISR); 825189714Syongari if ((isr & TXP_INT_LATCH) != 0) 82680219Swpaul WRITE_REG(sc, TXP_ISR, isr); 82780219Swpaul 828189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 829189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 830189714Syongari sc->sc_cdata.txp_hostvar_map, 831189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 832189714Syongari more = 0; 83380219Swpaul if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff)) 834189714Syongari more += txp_rx_reclaim(sc, &sc->sc_rxhir, 835189714Syongari sc->sc_process_limit); 83680219Swpaul if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff)) 837189714Syongari more += txp_rx_reclaim(sc, &sc->sc_rxlor, 838189714Syongari sc->sc_process_limit); 839189714Syongari /* 840189714Syongari * XXX 841189714Syongari * It seems controller is not smart enough to handle 842189714Syongari * FIFO overflow conditions under heavy network load. 843189714Syongari * No matter how often new Rx buffers are passed to 844189714Syongari * controller the situation didn't change. Maybe 845189714Syongari * flow-control would be the only way to mitigate the 846189714Syongari * issue but firmware does not have commands that 847189714Syongari * control the threshold of emitting pause frames. 848189714Syongari */ 84980219Swpaul if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx) 85080219Swpaul txp_rxbuf_reclaim(sc); 85180219Swpaul if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons != 852189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off))))) 85380219Swpaul txp_tx_reclaim(sc, &sc->sc_txhir); 85480219Swpaul if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons != 855189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off))))) 85680219Swpaul txp_tx_reclaim(sc, &sc->sc_txlor); 857189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 858189714Syongari sc->sc_cdata.txp_hostvar_map, 859189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 860189714Syongari if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 861189714Syongari txp_start_locked(sc->sc_ifp); 862189714Syongari if (more != 0 || READ_REG(sc, TXP_ISR & TXP_INT_LATCH) != 0) { 863189714Syongari taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task); 864189714Syongari TXP_UNLOCK(sc); 865189714Syongari return; 866189714Syongari } 86780219Swpaul } 86880219Swpaul 869189714Syongari /* Re-enable interrupts. */ 870189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE); 871151772Sjhb TXP_UNLOCK(sc); 87280219Swpaul} 87380219Swpaul 874189714Syongari#ifndef __NO_STRICT_ALIGNMENT 875189714Syongaristatic __inline void 876189714Syongaritxp_fixup_rx(struct mbuf *m) 87780219Swpaul{ 878189714Syongari int i; 879189714Syongari uint16_t *src, *dst; 880189714Syongari 881189714Syongari src = mtod(m, uint16_t *); 882189714Syongari dst = src - (TXP_RXBUF_ALIGN - ETHER_ALIGN) / sizeof *src; 883189714Syongari 884189714Syongari for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 885189714Syongari *dst++ = *src++; 886189714Syongari 887189714Syongari m->m_data -= TXP_RXBUF_ALIGN - ETHER_ALIGN; 888189714Syongari} 889189714Syongari#endif 890189714Syongari 891189714Syongaristatic int 892189714Syongaritxp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r, int count) 893189714Syongari{ 894189714Syongari struct ifnet *ifp; 89580219Swpaul struct txp_rx_desc *rxd; 89680219Swpaul struct mbuf *m; 897189714Syongari struct txp_rx_swdesc *sd; 898189714Syongari uint32_t roff, woff, rx_stat, prog; 89980219Swpaul 900151772Sjhb TXP_LOCK_ASSERT(sc); 90180219Swpaul 902189714Syongari ifp = sc->sc_ifp; 90380219Swpaul 904189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD | 905189714Syongari BUS_DMASYNC_POSTWRITE); 90680219Swpaul 907189714Syongari roff = le32toh(*r->r_roff); 908189714Syongari woff = le32toh(*r->r_woff); 909189714Syongari rxd = r->r_desc + roff / sizeof(struct txp_rx_desc); 910189714Syongari for (prog = 0; roff != woff; prog++, count--) { 911189714Syongari if (count <= 0) 912189714Syongari break; 913189714Syongari bcopy((u_long *)&rxd->rx_vaddrlo, &sd, sizeof(sd)); 914189714Syongari KASSERT(sd != NULL, ("%s: Rx desc ring corrupted", __func__)); 915189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 916189714Syongari BUS_DMASYNC_POSTREAD); 917189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map); 91880219Swpaul m = sd->sd_mbuf; 919189714Syongari KASSERT(m != NULL, ("%s: Rx buffer ring corrupted", __func__)); 92080219Swpaul sd->sd_mbuf = NULL; 921189714Syongari TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next); 922189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 923189714Syongari if ((rxd->rx_flags & RX_FLAGS_ERROR) != 0) { 924189714Syongari if (bootverbose) 925189714Syongari device_printf(sc->sc_dev, "Rx error %u\n", 926189714Syongari le32toh(rxd->rx_stat) & RX_ERROR_MASK); 927151772Sjhb m_freem(m); 928189714Syongari goto next; 92980219Swpaul } 930189714Syongari 931189714Syongari m->m_pkthdr.len = m->m_len = le16toh(rxd->rx_len); 932189714Syongari m->m_pkthdr.rcvif = ifp; 933189714Syongari#ifndef __NO_STRICT_ALIGNMENT 934189714Syongari txp_fixup_rx(m); 93580219Swpaul#endif 936189714Syongari rx_stat = le32toh(rxd->rx_stat); 937189714Syongari if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { 938189714Syongari if ((rx_stat & RX_STAT_IPCKSUMBAD) != 0) 939189714Syongari m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; 940189714Syongari else if ((rx_stat & RX_STAT_IPCKSUMGOOD) != 0) 941189714Syongari m->m_pkthdr.csum_flags |= 942189714Syongari CSUM_IP_CHECKED|CSUM_IP_VALID; 94380219Swpaul 944189714Syongari if ((rx_stat & RX_STAT_TCPCKSUMGOOD) != 0 || 945189714Syongari (rx_stat & RX_STAT_UDPCKSUMGOOD) != 0) { 946189714Syongari m->m_pkthdr.csum_flags |= 947189714Syongari CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 948189714Syongari m->m_pkthdr.csum_data = 0xffff; 949189714Syongari } 95080219Swpaul } 95180219Swpaul 952189714Syongari /* 953189714Syongari * XXX 954189714Syongari * Typhoon has a firmware bug that VLAN tag is always 955189714Syongari * stripped out even if it is told to not remove the tag. 956189714Syongari * Therefore don't check if_capenable here. 957189714Syongari */ 958189714Syongari if (/* (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && */ 959189714Syongari (rx_stat & RX_STAT_VLAN) != 0) { 960189714Syongari m->m_pkthdr.ether_vtag = 961189714Syongari bswap16((le32toh(rxd->rx_vlan) >> 16)); 962162375Sandre m->m_flags |= M_VLANTAG; 96380219Swpaul } 96483115Sbrooks 965151772Sjhb TXP_UNLOCK(sc); 966106937Ssam (*ifp->if_input)(ifp, m); 967151772Sjhb TXP_LOCK(sc); 96880219Swpaul 96980219Swpaulnext: 97080219Swpaul roff += sizeof(struct txp_rx_desc); 97180219Swpaul if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) { 97280219Swpaul roff = 0; 97380219Swpaul rxd = r->r_desc; 97480219Swpaul } else 97580219Swpaul rxd++; 976189714Syongari prog++; 97780219Swpaul } 97880219Swpaul 979189714Syongari if (prog == 0) 980189714Syongari return (0); 981189714Syongari 982189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD | 983189714Syongari BUS_DMASYNC_PREWRITE); 984189714Syongari *r->r_roff = le32toh(roff); 985189714Syongari 986189714Syongari return (count > 0 ? 0 : EAGAIN); 98780219Swpaul} 98880219Swpaul 98980219Swpaulstatic void 990189685Syongaritxp_rxbuf_reclaim(struct txp_softc *sc) 99180219Swpaul{ 992189714Syongari struct txp_hostvar *hv; 99380219Swpaul struct txp_rxbuf_desc *rbd; 994189714Syongari struct txp_rx_swdesc *sd; 995189714Syongari bus_dma_segment_t segs[1]; 996189714Syongari int nsegs, prod, prog; 997189714Syongari uint32_t cons; 99880219Swpaul 999151772Sjhb TXP_LOCK_ASSERT(sc); 1000189714Syongari 1001189714Syongari hv = sc->sc_hostvar; 1002189714Syongari cons = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_read_idx)); 1003189714Syongari prod = sc->sc_rxbufprod; 1004189714Syongari TXP_DESC_INC(prod, RXBUF_ENTRIES); 1005189714Syongari if (prod == cons) 100680219Swpaul return; 100780219Swpaul 1008189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1009189714Syongari sc->sc_cdata.txp_rxbufs_map, 1010189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 101180219Swpaul 1012189714Syongari for (prog = 0; prod != cons; prog++) { 1013189714Syongari sd = TAILQ_FIRST(&sc->sc_free_list); 1014189714Syongari if (sd == NULL) 101580219Swpaul break; 1016189714Syongari rbd = sc->sc_rxbufs + prod; 1017189714Syongari bcopy((u_long *)&rbd->rb_vaddrlo, &sd, sizeof(sd)); 1018151772Sjhb sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 101980219Swpaul if (sd->sd_mbuf == NULL) 1020189714Syongari break; 102180219Swpaul sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; 1022189714Syongari#ifndef __NO_STRICT_ALIGNMENT 1023189714Syongari m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN); 1024189714Syongari#endif 1025189714Syongari if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag, 1026189714Syongari sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0) != 0) { 1027189714Syongari m_freem(sd->sd_mbuf); 1028189714Syongari sd->sd_mbuf = NULL; 1029189714Syongari break; 1030189714Syongari } 1031189714Syongari KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__, 1032189714Syongari nsegs)); 1033189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1034189714Syongari TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next); 1035189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1036189714Syongari BUS_DMASYNC_PREREAD); 1037189714Syongari rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr)); 1038189714Syongari rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr)); 1039189714Syongari TXP_DESC_INC(prod, RXBUF_ENTRIES); 104080219Swpaul } 104180219Swpaul 1042189714Syongari if (prog == 0) 1043189714Syongari return; 1044189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1045189714Syongari sc->sc_cdata.txp_rxbufs_map, 1046189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1047189714Syongari prod = (prod + RXBUF_ENTRIES - 1) % RXBUF_ENTRIES; 1048189714Syongari sc->sc_rxbufprod = prod; 1049189714Syongari hv->hv_rx_buf_write_idx = htole32(TXP_IDX2OFFSET(prod)); 105080219Swpaul} 105180219Swpaul 105280219Swpaul/* 105380219Swpaul * Reclaim mbufs and entries from a transmit ring. 105480219Swpaul */ 105580219Swpaulstatic void 1056189685Syongaritxp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r) 105780219Swpaul{ 1058189714Syongari struct ifnet *ifp; 1059189714Syongari uint32_t idx; 1060189714Syongari uint32_t cons, cnt; 1061189714Syongari struct txp_tx_desc *txd; 1062189714Syongari struct txp_swdesc *sd; 106380219Swpaul 1064151772Sjhb TXP_LOCK_ASSERT(sc); 106580219Swpaul 1066189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD | 1067189714Syongari BUS_DMASYNC_POSTWRITE); 1068189714Syongari ifp = sc->sc_ifp; 1069189714Syongari idx = TXP_OFFSET2IDX(le32toh(*(r->r_off))); 1070189714Syongari cons = r->r_cons; 1071189714Syongari cnt = r->r_cnt; 1072189714Syongari txd = r->r_desc + cons; 1073189714Syongari sd = sc->sc_txd + cons; 1074189714Syongari 1075189714Syongari for (cnt = r->r_cnt; cons != idx && cnt > 0; cnt--) { 1076189714Syongari if ((txd->tx_flags & TX_FLAGS_TYPE_M) == TX_FLAGS_TYPE_DATA) { 1077189714Syongari if (sd->sd_mbuf != NULL) { 1078189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, 1079189714Syongari sd->sd_map, BUS_DMASYNC_POSTWRITE); 1080189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, 1081189714Syongari sd->sd_map); 1082189714Syongari m_freem(sd->sd_mbuf); 1083189714Syongari sd->sd_mbuf = NULL; 108480219Swpaul txd->tx_addrlo = 0; 108580219Swpaul txd->tx_addrhi = 0; 1086189714Syongari txd->tx_flags = 0; 108780219Swpaul } 108880219Swpaul } 1089148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 109080219Swpaul 109180219Swpaul if (++cons == TX_ENTRIES) { 109280219Swpaul txd = r->r_desc; 109380219Swpaul cons = 0; 109480219Swpaul sd = sc->sc_txd; 109580219Swpaul } else { 109680219Swpaul txd++; 109780219Swpaul sd++; 109880219Swpaul } 109980219Swpaul } 110080219Swpaul 1101189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD | 1102189714Syongari BUS_DMASYNC_PREWRITE); 110380219Swpaul r->r_cons = cons; 110480219Swpaul r->r_cnt = cnt; 110580219Swpaul if (cnt == 0) 1106189714Syongari sc->sc_watchdog_timer = 0; 110780219Swpaul} 110880219Swpaul 110980219Swpaulstatic int 1110189685Syongaritxp_shutdown(device_t dev) 111180219Swpaul{ 1112189714Syongari 1113189714Syongari return (txp_suspend(dev)); 1114189714Syongari} 1115189714Syongari 1116189714Syongaristatic int 1117189714Syongaritxp_suspend(device_t dev) 1118189714Syongari{ 111980219Swpaul struct txp_softc *sc; 1120189714Syongari struct ifnet *ifp; 1121189714Syongari uint8_t *eaddr; 1122189714Syongari uint16_t p1; 1123189714Syongari uint32_t p2; 1124189714Syongari int pmc; 1125189714Syongari uint16_t pmstat; 112680219Swpaul 112780219Swpaul sc = device_get_softc(dev); 112880219Swpaul 1129151772Sjhb TXP_LOCK(sc); 1130189714Syongari ifp = sc->sc_ifp; 1131189714Syongari txp_stop(sc); 1132189714Syongari txp_init_rings(sc); 1133189714Syongari /* Reset controller and make it reload sleep image. */ 1134189714Syongari txp_reset(sc); 1135189714Syongari /* Let controller boot from sleep image. */ 1136189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) 1137189714Syongari device_printf(sc->sc_dev, "couldn't boot sleep image\n"); 1138151772Sjhb 1139189714Syongari /* Set station address. */ 1140189714Syongari eaddr = IF_LLADDR(sc->sc_ifp); 1141189714Syongari p1 = 0; 1142189714Syongari ((uint8_t *)&p1)[1] = eaddr[0]; 1143189714Syongari ((uint8_t *)&p1)[0] = eaddr[1]; 1144189714Syongari p1 = le16toh(p1); 1145189714Syongari ((uint8_t *)&p2)[3] = eaddr[2]; 1146189714Syongari ((uint8_t *)&p2)[2] = eaddr[3]; 1147189714Syongari ((uint8_t *)&p2)[1] = eaddr[4]; 1148189714Syongari ((uint8_t *)&p2)[0] = eaddr[5]; 1149189714Syongari p2 = le32toh(p2); 1150189714Syongari txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, NULL, NULL, 1151189714Syongari NULL, TXP_CMD_WAIT); 1152189714Syongari txp_set_filter(sc); 1153189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 1154189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 1155189714Syongari txp_sleep(sc, sc->sc_ifp->if_capenable); 1156219902Sjhb if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) { 1157189714Syongari /* Request PME. */ 1158189714Syongari pmstat = pci_read_config(sc->sc_dev, 1159189714Syongari pmc + PCIR_POWER_STATUS, 2); 1160189714Syongari pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); 1161189714Syongari if ((ifp->if_capenable & IFCAP_WOL) != 0) 1162189714Syongari pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; 1163189714Syongari pci_write_config(sc->sc_dev, 1164189714Syongari pmc + PCIR_POWER_STATUS, pmstat, 2); 1165189714Syongari } 1166189714Syongari TXP_UNLOCK(sc); 116780219Swpaul 1168189714Syongari return (0); 1169189714Syongari} 1170189714Syongari 1171189714Syongaristatic int 1172189714Syongaritxp_resume(device_t dev) 1173189714Syongari{ 1174189714Syongari struct txp_softc *sc; 1175189714Syongari int pmc; 1176189714Syongari uint16_t pmstat; 1177189714Syongari 1178189714Syongari sc = device_get_softc(dev); 1179189714Syongari 1180189714Syongari TXP_LOCK(sc); 1181219902Sjhb if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) { 1182189714Syongari /* Disable PME and clear PME status. */ 1183189714Syongari pmstat = pci_read_config(sc->sc_dev, 1184189714Syongari pmc + PCIR_POWER_STATUS, 2); 1185189714Syongari if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { 1186189714Syongari pmstat &= ~PCIM_PSTAT_PMEENABLE; 1187189714Syongari pci_write_config(sc->sc_dev, 1188189714Syongari pmc + PCIR_POWER_STATUS, pmstat, 2); 1189189714Syongari } 1190189714Syongari } 1191189714Syongari if ((sc->sc_ifp->if_flags & IFF_UP) != 0) 1192189714Syongari txp_init_locked(sc); 1193151772Sjhb TXP_UNLOCK(sc); 119480219Swpaul 1195189688Syongari return (0); 119680219Swpaul} 119780219Swpaul 1198189714Syongaristruct txp_dmamap_arg { 1199189714Syongari bus_addr_t txp_busaddr; 1200189714Syongari}; 1201189714Syongari 1202189714Syongaristatic void 1203189714Syongaritxp_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1204189714Syongari{ 1205189714Syongari struct txp_dmamap_arg *ctx; 1206189714Syongari 1207189714Syongari if (error != 0) 1208189714Syongari return; 1209189714Syongari 1210189714Syongari KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1211189714Syongari 1212189714Syongari ctx = (struct txp_dmamap_arg *)arg; 1213189714Syongari ctx->txp_busaddr = segs[0].ds_addr; 1214189714Syongari} 1215189714Syongari 121680219Swpaulstatic int 1217189714Syongaritxp_dma_alloc(struct txp_softc *sc, char *type, bus_dma_tag_t *tag, 1218189714Syongari bus_size_t alignment, bus_size_t boundary, bus_dmamap_t *map, void **buf, 1219189714Syongari bus_size_t size, bus_addr_t *paddr) 1220189714Syongari{ 1221189714Syongari struct txp_dmamap_arg ctx; 1222189714Syongari int error; 1223189714Syongari 1224189714Syongari /* Create DMA block tag. */ 1225189714Syongari error = bus_dma_tag_create( 1226189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1227189714Syongari alignment, boundary, /* algnmnt, boundary */ 1228189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1229189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1230189714Syongari NULL, NULL, /* filter, filterarg */ 1231189714Syongari size, /* maxsize */ 1232189714Syongari 1, /* nsegments */ 1233189714Syongari size, /* maxsegsize */ 1234189714Syongari 0, /* flags */ 1235189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1236189714Syongari tag); 1237189714Syongari if (error != 0) { 1238189714Syongari device_printf(sc->sc_dev, 1239189714Syongari "could not create DMA tag for %s.\n", type); 1240189714Syongari return (error); 1241189714Syongari } 1242189714Syongari 1243189714Syongari *paddr = 0; 1244189714Syongari /* Allocate DMA'able memory and load the DMA map. */ 1245189714Syongari error = bus_dmamem_alloc(*tag, buf, BUS_DMA_WAITOK | BUS_DMA_ZERO | 1246189714Syongari BUS_DMA_COHERENT, map); 1247189714Syongari if (error != 0) { 1248189714Syongari device_printf(sc->sc_dev, 1249189714Syongari "could not allocate DMA'able memory for %s.\n", type); 1250189714Syongari return (error); 1251189714Syongari } 1252189714Syongari 1253189714Syongari ctx.txp_busaddr = 0; 1254189714Syongari error = bus_dmamap_load(*tag, *map, *(uint8_t **)buf, 1255189714Syongari size, txp_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 1256189714Syongari if (error != 0 || ctx.txp_busaddr == 0) { 1257189714Syongari device_printf(sc->sc_dev, 1258189714Syongari "could not load DMA'able memory for %s.\n", type); 1259189714Syongari return (error); 1260189714Syongari } 1261189714Syongari *paddr = ctx.txp_busaddr; 1262189714Syongari 1263189714Syongari return (0); 1264189714Syongari} 1265189714Syongari 1266189714Syongaristatic void 1267189714Syongaritxp_dma_free(struct txp_softc *sc, bus_dma_tag_t *tag, bus_dmamap_t *map, 1268189714Syongari void **buf) 1269189714Syongari{ 1270189714Syongari 1271189714Syongari if (*tag != NULL) { 1272189714Syongari if (*map != NULL) 1273189714Syongari bus_dmamap_unload(*tag, *map); 1274189714Syongari if (*map != NULL && buf != NULL) 1275189714Syongari bus_dmamem_free(*tag, *(uint8_t **)buf, *map); 1276189714Syongari *(uint8_t **)buf = NULL; 1277189714Syongari *map = NULL; 1278189714Syongari bus_dma_tag_destroy(*tag); 1279189714Syongari *tag = NULL; 1280189714Syongari } 1281189714Syongari} 1282189714Syongari 1283189714Syongaristatic int 1284189685Syongaritxp_alloc_rings(struct txp_softc *sc) 128580219Swpaul{ 128680219Swpaul struct txp_boot_record *boot; 128780219Swpaul struct txp_ldata *ld; 1288189714Syongari struct txp_swdesc *txd; 1289189714Syongari struct txp_rxbuf_desc *rbd; 1290189714Syongari struct txp_rx_swdesc *sd; 1291189714Syongari int error, i; 129280219Swpaul 1293189714Syongari ld = &sc->sc_ldata; 1294189714Syongari boot = ld->txp_boot; 129580219Swpaul 129680219Swpaul /* boot record */ 129780219Swpaul sc->sc_boot = boot; 129880219Swpaul 1299189714Syongari /* 1300189714Syongari * Create parent ring/DMA block tag. 1301189714Syongari * Datasheet says that all ring addresses and descriptors 1302189714Syongari * support 64bits addressing. However the controller is 1303189714Syongari * known to have no support DAC so limit DMA address space 1304189714Syongari * to 32bits. 1305189714Syongari */ 1306189714Syongari error = bus_dma_tag_create( 1307189714Syongari bus_get_dma_tag(sc->sc_dev), /* parent */ 1308189714Syongari 1, 0, /* algnmnt, boundary */ 1309189714Syongari BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1310189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1311189714Syongari NULL, NULL, /* filter, filterarg */ 1312189714Syongari BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 1313189714Syongari 0, /* nsegments */ 1314189714Syongari BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1315189714Syongari 0, /* flags */ 1316189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1317189714Syongari &sc->sc_cdata.txp_parent_tag); 1318189714Syongari if (error != 0) { 1319189714Syongari device_printf(sc->sc_dev, "could not create parent DMA tag.\n"); 1320189714Syongari return (error); 1321189714Syongari } 132280219Swpaul 1323189714Syongari /* Boot record. */ 1324189714Syongari error = txp_dma_alloc(sc, "boot record", 1325189714Syongari &sc->sc_cdata.txp_boot_tag, sizeof(uint32_t), 0, 1326189714Syongari &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot, 1327189714Syongari sizeof(struct txp_boot_record), 1328189714Syongari &sc->sc_ldata.txp_boot_paddr); 1329189714Syongari if (error != 0) 1330189714Syongari return (error); 1331189714Syongari boot = sc->sc_ldata.txp_boot; 1332189714Syongari sc->sc_boot = boot; 1333189714Syongari 1334189714Syongari /* Host variables. */ 1335189714Syongari error = txp_dma_alloc(sc, "host variables", 1336189714Syongari &sc->sc_cdata.txp_hostvar_tag, sizeof(uint32_t), 0, 1337189714Syongari &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar, 1338189714Syongari sizeof(struct txp_hostvar), 1339189714Syongari &sc->sc_ldata.txp_hostvar_paddr); 1340189714Syongari if (error != 0) 1341189714Syongari return (error); 1342189714Syongari boot->br_hostvar_lo = 1343189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_hostvar_paddr)); 1344189714Syongari boot->br_hostvar_hi = 1345189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_hostvar_paddr)); 1346189714Syongari sc->sc_hostvar = sc->sc_ldata.txp_hostvar; 1347189714Syongari 1348189714Syongari /* Hi priority tx ring. */ 1349189714Syongari error = txp_dma_alloc(sc, "hi priority tx ring", 1350189714Syongari &sc->sc_cdata.txp_txhiring_tag, sizeof(struct txp_tx_desc), 0, 1351189714Syongari &sc->sc_cdata.txp_txhiring_map, (void **)&sc->sc_ldata.txp_txhiring, 1352189714Syongari sizeof(struct txp_tx_desc) * TX_ENTRIES, 1353189714Syongari &sc->sc_ldata.txp_txhiring_paddr); 1354189714Syongari if (error != 0) 1355189714Syongari return (error); 1356189714Syongari boot->br_txhipri_lo = 1357189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txhiring_paddr)); 1358189714Syongari boot->br_txhipri_hi = 1359189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txhiring_paddr)); 1360189714Syongari boot->br_txhipri_siz = 1361189714Syongari htole32(TX_ENTRIES * sizeof(struct txp_tx_desc)); 1362189714Syongari sc->sc_txhir.r_tag = sc->sc_cdata.txp_txhiring_tag; 1363189714Syongari sc->sc_txhir.r_map = sc->sc_cdata.txp_txhiring_map; 136480219Swpaul sc->sc_txhir.r_reg = TXP_H2A_1; 1365189714Syongari sc->sc_txhir.r_desc = sc->sc_ldata.txp_txhiring; 136680219Swpaul sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0; 136780219Swpaul sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx; 136880219Swpaul 1369189714Syongari /* Low priority tx ring. */ 1370189714Syongari error = txp_dma_alloc(sc, "low priority tx ring", 1371189714Syongari &sc->sc_cdata.txp_txloring_tag, sizeof(struct txp_tx_desc), 0, 1372189714Syongari &sc->sc_cdata.txp_txloring_map, (void **)&sc->sc_ldata.txp_txloring, 1373189714Syongari sizeof(struct txp_tx_desc) * TX_ENTRIES, 1374189714Syongari &sc->sc_ldata.txp_txloring_paddr); 1375189714Syongari if (error != 0) 1376189714Syongari return (error); 1377189714Syongari boot->br_txlopri_lo = 1378189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txloring_paddr)); 1379189714Syongari boot->br_txlopri_hi = 1380189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txloring_paddr)); 1381189714Syongari boot->br_txlopri_siz = 1382189714Syongari htole32(TX_ENTRIES * sizeof(struct txp_tx_desc)); 1383189714Syongari sc->sc_txlor.r_tag = sc->sc_cdata.txp_txloring_tag; 1384189714Syongari sc->sc_txlor.r_map = sc->sc_cdata.txp_txloring_map; 138580219Swpaul sc->sc_txlor.r_reg = TXP_H2A_3; 1386189714Syongari sc->sc_txlor.r_desc = sc->sc_ldata.txp_txloring; 138780219Swpaul sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0; 138880219Swpaul sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx; 138980219Swpaul 1390189714Syongari /* High priority rx ring. */ 1391189714Syongari error = txp_dma_alloc(sc, "hi priority rx ring", 1392196721Syongari &sc->sc_cdata.txp_rxhiring_tag, 1393196721Syongari roundup(sizeof(struct txp_rx_desc), 16), 0, 1394189714Syongari &sc->sc_cdata.txp_rxhiring_map, (void **)&sc->sc_ldata.txp_rxhiring, 1395189714Syongari sizeof(struct txp_rx_desc) * RX_ENTRIES, 1396189714Syongari &sc->sc_ldata.txp_rxhiring_paddr); 1397189714Syongari if (error != 0) 1398189714Syongari return (error); 1399189714Syongari boot->br_rxhipri_lo = 1400189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxhiring_paddr)); 1401189714Syongari boot->br_rxhipri_hi = 1402189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxhiring_paddr)); 1403189714Syongari boot->br_rxhipri_siz = 1404189714Syongari htole32(RX_ENTRIES * sizeof(struct txp_rx_desc)); 1405189714Syongari sc->sc_rxhir.r_tag = sc->sc_cdata.txp_rxhiring_tag; 1406189714Syongari sc->sc_rxhir.r_map = sc->sc_cdata.txp_rxhiring_map; 1407189714Syongari sc->sc_rxhir.r_desc = sc->sc_ldata.txp_rxhiring; 140880219Swpaul sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx; 140980219Swpaul sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx; 141080219Swpaul 1411189714Syongari /* Low priority rx ring. */ 1412189714Syongari error = txp_dma_alloc(sc, "low priority rx ring", 1413196721Syongari &sc->sc_cdata.txp_rxloring_tag, 1414196721Syongari roundup(sizeof(struct txp_rx_desc), 16), 0, 1415189714Syongari &sc->sc_cdata.txp_rxloring_map, (void **)&sc->sc_ldata.txp_rxloring, 1416189714Syongari sizeof(struct txp_rx_desc) * RX_ENTRIES, 1417189714Syongari &sc->sc_ldata.txp_rxloring_paddr); 1418189714Syongari if (error != 0) 1419189714Syongari return (error); 1420189714Syongari boot->br_rxlopri_lo = 1421189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxloring_paddr)); 1422189714Syongari boot->br_rxlopri_hi = 1423189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxloring_paddr)); 1424189714Syongari boot->br_rxlopri_siz = 1425189714Syongari htole32(RX_ENTRIES * sizeof(struct txp_rx_desc)); 1426189714Syongari sc->sc_rxlor.r_tag = sc->sc_cdata.txp_rxloring_tag; 1427189714Syongari sc->sc_rxlor.r_map = sc->sc_cdata.txp_rxloring_map; 1428189714Syongari sc->sc_rxlor.r_desc = sc->sc_ldata.txp_rxloring; 142980219Swpaul sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx; 143080219Swpaul sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx; 143180219Swpaul 1432189714Syongari /* Command ring. */ 1433189714Syongari error = txp_dma_alloc(sc, "command ring", 1434189714Syongari &sc->sc_cdata.txp_cmdring_tag, sizeof(struct txp_cmd_desc), 0, 1435189714Syongari &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring, 1436189714Syongari sizeof(struct txp_cmd_desc) * CMD_ENTRIES, 1437189714Syongari &sc->sc_ldata.txp_cmdring_paddr); 1438189714Syongari if (error != 0) 1439189714Syongari return (error); 1440189714Syongari boot->br_cmd_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_cmdring_paddr)); 1441189714Syongari boot->br_cmd_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_cmdring_paddr)); 1442189714Syongari boot->br_cmd_siz = htole32(CMD_ENTRIES * sizeof(struct txp_cmd_desc)); 1443189714Syongari sc->sc_cmdring.base = sc->sc_ldata.txp_cmdring; 144480219Swpaul sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc); 144580219Swpaul sc->sc_cmdring.lastwrite = 0; 144680219Swpaul 1447189714Syongari /* Response ring. */ 1448189714Syongari error = txp_dma_alloc(sc, "response ring", 1449189714Syongari &sc->sc_cdata.txp_rspring_tag, sizeof(struct txp_rsp_desc), 0, 1450189714Syongari &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring, 1451189714Syongari sizeof(struct txp_rsp_desc) * RSP_ENTRIES, 1452189714Syongari &sc->sc_ldata.txp_rspring_paddr); 1453189714Syongari if (error != 0) 1454189714Syongari return (error); 1455189714Syongari boot->br_resp_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rspring_paddr)); 1456189714Syongari boot->br_resp_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rspring_paddr)); 1457189714Syongari boot->br_resp_siz = htole32(RSP_ENTRIES * sizeof(struct txp_rsp_desc)); 1458189714Syongari sc->sc_rspring.base = sc->sc_ldata.txp_rspring; 145980219Swpaul sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc); 146080219Swpaul sc->sc_rspring.lastwrite = 0; 146180219Swpaul 1462189714Syongari /* Receive buffer ring. */ 1463189714Syongari error = txp_dma_alloc(sc, "receive buffer ring", 1464189714Syongari &sc->sc_cdata.txp_rxbufs_tag, sizeof(struct txp_rxbuf_desc), 0, 1465189714Syongari &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs, 1466189714Syongari sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES, 1467189714Syongari &sc->sc_ldata.txp_rxbufs_paddr); 1468189714Syongari if (error != 0) 1469189714Syongari return (error); 1470189714Syongari boot->br_rxbuf_lo = 1471189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxbufs_paddr)); 1472189714Syongari boot->br_rxbuf_hi = 1473189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxbufs_paddr)); 1474189714Syongari boot->br_rxbuf_siz = 1475189714Syongari htole32(RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc)); 1476189714Syongari sc->sc_rxbufs = sc->sc_ldata.txp_rxbufs; 147780219Swpaul 1478189714Syongari /* Zero ring. */ 1479189714Syongari error = txp_dma_alloc(sc, "zero buffer", 1480189714Syongari &sc->sc_cdata.txp_zero_tag, sizeof(uint32_t), 0, 1481189714Syongari &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero, 1482189714Syongari sizeof(uint32_t), &sc->sc_ldata.txp_zero_paddr); 1483189714Syongari if (error != 0) 1484189714Syongari return (error); 1485189714Syongari boot->br_zero_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_zero_paddr)); 1486189714Syongari boot->br_zero_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_zero_paddr)); 1487189714Syongari 1488189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_boot_tag, sc->sc_cdata.txp_boot_map, 1489189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1490189714Syongari 1491189714Syongari /* Create Tx buffers. */ 1492189714Syongari error = bus_dma_tag_create( 1493189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1494189714Syongari 1, 0, /* algnmnt, boundary */ 1495189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1496189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1497189714Syongari NULL, NULL, /* filter, filterarg */ 1498189714Syongari MCLBYTES * TXP_MAXTXSEGS, /* maxsize */ 1499189714Syongari TXP_MAXTXSEGS, /* nsegments */ 1500189714Syongari MCLBYTES, /* maxsegsize */ 1501189714Syongari 0, /* flags */ 1502189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1503189714Syongari &sc->sc_cdata.txp_tx_tag); 1504189714Syongari if (error != 0) { 1505189714Syongari device_printf(sc->sc_dev, "could not create Tx DMA tag.\n"); 1506189714Syongari goto fail; 1507189714Syongari } 1508189714Syongari 1509189714Syongari /* Create tag for Rx buffers. */ 1510189714Syongari error = bus_dma_tag_create( 1511189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1512189714Syongari TXP_RXBUF_ALIGN, 0, /* algnmnt, boundary */ 1513189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1514189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1515189714Syongari NULL, NULL, /* filter, filterarg */ 1516189714Syongari MCLBYTES, /* maxsize */ 1517189714Syongari 1, /* nsegments */ 1518189714Syongari MCLBYTES, /* maxsegsize */ 1519189714Syongari 0, /* flags */ 1520189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1521189714Syongari &sc->sc_cdata.txp_rx_tag); 1522189714Syongari if (error != 0) { 1523189714Syongari device_printf(sc->sc_dev, "could not create Rx DMA tag.\n"); 1524189714Syongari goto fail; 1525189714Syongari } 1526189714Syongari 1527189714Syongari /* Create DMA maps for Tx buffers. */ 1528189714Syongari for (i = 0; i < TX_ENTRIES; i++) { 1529189714Syongari txd = &sc->sc_txd[i]; 1530189714Syongari txd->sd_mbuf = NULL; 1531189714Syongari txd->sd_map = NULL; 1532189714Syongari error = bus_dmamap_create(sc->sc_cdata.txp_tx_tag, 0, 1533189714Syongari &txd->sd_map); 1534189714Syongari if (error != 0) { 1535189714Syongari device_printf(sc->sc_dev, 1536189714Syongari "could not create Tx dmamap.\n"); 1537189714Syongari goto fail; 1538189714Syongari } 1539189714Syongari } 1540189714Syongari 1541189714Syongari /* Create DMA maps for Rx buffers. */ 154280219Swpaul for (i = 0; i < RXBUF_ENTRIES; i++) { 1543189714Syongari sd = malloc(sizeof(struct txp_rx_swdesc), M_DEVBUF, 1544189714Syongari M_NOWAIT | M_ZERO); 1545189714Syongari if (sd == NULL) { 1546189714Syongari error = ENOMEM; 1547189714Syongari goto fail; 1548189714Syongari } 1549189714Syongari /* 1550189714Syongari * The virtual address part of descriptor is not used 1551189714Syongari * by hardware so use that to save an ring entry. We 1552189714Syongari * need bcopy here otherwise the address wouldn't be 1553189714Syongari * valid on big-endian architectures. 1554189714Syongari */ 1555189714Syongari rbd = sc->sc_rxbufs + i; 1556189714Syongari bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd)); 155780457Swpaul sd->sd_mbuf = NULL; 1558189714Syongari sd->sd_map = NULL; 1559189714Syongari error = bus_dmamap_create(sc->sc_cdata.txp_rx_tag, 0, 1560189714Syongari &sd->sd_map); 1561189714Syongari if (error != 0) { 1562189714Syongari device_printf(sc->sc_dev, 1563189714Syongari "could not create Rx dmamap.\n"); 1564189714Syongari goto fail; 1565189714Syongari } 1566189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 156780219Swpaul } 1568189714Syongari 1569189714Syongarifail: 1570189714Syongari return (error); 1571189714Syongari} 1572189714Syongari 1573189714Syongaristatic void 1574189714Syongaritxp_init_rings(struct txp_softc *sc) 1575189714Syongari{ 1576189714Syongari 1577189714Syongari bzero(sc->sc_ldata.txp_hostvar, sizeof(struct txp_hostvar)); 1578189714Syongari bzero(sc->sc_ldata.txp_zero, sizeof(uint32_t)); 1579189714Syongari sc->sc_txhir.r_cons = 0; 1580189714Syongari sc->sc_txhir.r_prod = 0; 1581189714Syongari sc->sc_txhir.r_cnt = 0; 1582189714Syongari sc->sc_txlor.r_cons = 0; 1583189714Syongari sc->sc_txlor.r_prod = 0; 1584189714Syongari sc->sc_txlor.r_cnt = 0; 1585189714Syongari sc->sc_cmdring.lastwrite = 0; 1586189714Syongari sc->sc_rspring.lastwrite = 0; 158780219Swpaul sc->sc_rxbufprod = 0; 1588189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1589189714Syongari sc->sc_cdata.txp_hostvar_map, 1590189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1591189714Syongari} 159280219Swpaul 1593189714Syongaristatic int 1594189714Syongaritxp_wait(struct txp_softc *sc, uint32_t state) 1595189714Syongari{ 1596189714Syongari uint32_t reg; 1597189714Syongari int i; 159880219Swpaul 1599189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 1600189714Syongari reg = READ_REG(sc, TXP_A2H_0); 1601189714Syongari if (reg == state) 160280219Swpaul break; 160380219Swpaul DELAY(50); 160480219Swpaul } 160580219Swpaul 1606189714Syongari return (i == TXP_TIMEOUT ? ETIMEDOUT : 0); 1607189714Syongari} 160880219Swpaul 1609189714Syongaristatic void 1610189714Syongaritxp_free_rings(struct txp_softc *sc) 1611189714Syongari{ 1612189714Syongari struct txp_swdesc *txd; 1613189714Syongari struct txp_rx_swdesc *sd; 1614189714Syongari int i; 161580219Swpaul 1616189714Syongari /* Tx buffers. */ 1617189714Syongari if (sc->sc_cdata.txp_tx_tag != NULL) { 1618189714Syongari for (i = 0; i < TX_ENTRIES; i++) { 1619189714Syongari txd = &sc->sc_txd[i]; 1620189714Syongari if (txd->sd_map != NULL) { 1621189714Syongari bus_dmamap_destroy(sc->sc_cdata.txp_tx_tag, 1622189714Syongari txd->sd_map); 1623189714Syongari txd->sd_map = NULL; 1624189714Syongari } 1625189714Syongari } 1626189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_tx_tag); 1627189714Syongari sc->sc_cdata.txp_tx_tag = NULL; 162880219Swpaul } 1629189714Syongari /* Rx buffers. */ 1630189714Syongari if (sc->sc_cdata.txp_rx_tag != NULL) { 1631189714Syongari if (sc->sc_rxbufs != NULL) { 1632189714Syongari KASSERT(TAILQ_FIRST(&sc->sc_busy_list) == NULL, 1633189714Syongari ("%s : still have busy Rx buffers", __func__)); 1634189714Syongari while ((sd = TAILQ_FIRST(&sc->sc_free_list)) != NULL) { 1635189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1636189714Syongari if (sd->sd_map != NULL) { 1637189714Syongari bus_dmamap_destroy( 1638189714Syongari sc->sc_cdata.txp_rx_tag, 1639189714Syongari sd->sd_map); 1640189714Syongari sd->sd_map = NULL; 1641189714Syongari } 1642189714Syongari free(sd, M_DEVBUF); 1643189714Syongari } 1644189714Syongari } 1645189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_rx_tag); 1646189714Syongari sc->sc_cdata.txp_rx_tag = NULL; 164780219Swpaul } 164880219Swpaul 1649189714Syongari /* Hi priority Tx ring. */ 1650189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_txhiring_tag, 1651189714Syongari &sc->sc_cdata.txp_txhiring_map, 1652189714Syongari (void **)&sc->sc_ldata.txp_txhiring); 1653189714Syongari /* Low priority Tx ring. */ 1654189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_txloring_tag, 1655189714Syongari &sc->sc_cdata.txp_txloring_map, 1656189714Syongari (void **)&sc->sc_ldata.txp_txloring); 1657189714Syongari /* Hi priority Rx ring. */ 1658189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxhiring_tag, 1659189714Syongari &sc->sc_cdata.txp_rxhiring_map, 1660189714Syongari (void **)&sc->sc_ldata.txp_rxhiring); 1661189714Syongari /* Low priority Rx ring. */ 1662189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxloring_tag, 1663189714Syongari &sc->sc_cdata.txp_rxloring_map, 1664189714Syongari (void **)&sc->sc_ldata.txp_rxloring); 1665189714Syongari /* Receive buffer ring. */ 1666189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxbufs_tag, 1667189714Syongari &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs); 1668189714Syongari /* Command ring. */ 1669189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_cmdring_tag, 1670189714Syongari &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring); 1671189714Syongari /* Response ring. */ 1672189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rspring_tag, 1673189714Syongari &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring); 1674189714Syongari /* Zero ring. */ 1675189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_zero_tag, 1676189714Syongari &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero); 1677189714Syongari /* Host variables. */ 1678189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_hostvar_tag, 1679189714Syongari &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar); 1680189714Syongari /* Boot record. */ 1681189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_boot_tag, 1682189714Syongari &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot); 168380219Swpaul 1684189714Syongari if (sc->sc_cdata.txp_parent_tag != NULL) { 1685189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_parent_tag); 1686189714Syongari sc->sc_cdata.txp_parent_tag = NULL; 1687189714Syongari } 1688189714Syongari 168980219Swpaul} 169080219Swpaul 169180219Swpaulstatic int 1692189685Syongaritxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 169380219Swpaul{ 169480219Swpaul struct txp_softc *sc = ifp->if_softc; 169580219Swpaul struct ifreq *ifr = (struct ifreq *)data; 1696189714Syongari int capenable, error = 0, mask; 169780219Swpaul 1698189714Syongari switch(command) { 169980219Swpaul case SIOCSIFFLAGS: 1700151772Sjhb TXP_LOCK(sc); 1701189714Syongari if ((ifp->if_flags & IFF_UP) != 0) { 1702189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 1703189714Syongari if (((ifp->if_flags ^ sc->sc_if_flags) 1704189714Syongari & (IFF_PROMISC | IFF_ALLMULTI)) != 0) 1705189714Syongari txp_set_filter(sc); 1706189714Syongari } else { 1707189714Syongari if ((sc->sc_flags & TXP_FLAG_DETACH) == 0) 1708189714Syongari txp_init_locked(sc); 1709189714Syongari } 171080219Swpaul } else { 1711189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 171280219Swpaul txp_stop(sc); 171380219Swpaul } 1714189714Syongari sc->sc_if_flags = ifp->if_flags; 1715151772Sjhb TXP_UNLOCK(sc); 171680219Swpaul break; 171780219Swpaul case SIOCADDMULTI: 171880219Swpaul case SIOCDELMULTI: 171980219Swpaul /* 172080219Swpaul * Multicast list has changed; set the hardware 172180219Swpaul * filter accordingly. 172280219Swpaul */ 1723151772Sjhb TXP_LOCK(sc); 1724189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1725189714Syongari txp_set_filter(sc); 1726151772Sjhb TXP_UNLOCK(sc); 172780219Swpaul break; 1728189714Syongari case SIOCSIFCAP: 1729189714Syongari TXP_LOCK(sc); 1730189714Syongari capenable = ifp->if_capenable; 1731189714Syongari mask = ifr->ifr_reqcap ^ ifp->if_capenable; 1732189714Syongari if ((mask & IFCAP_TXCSUM) != 0 && 1733189714Syongari (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { 1734189714Syongari ifp->if_capenable ^= IFCAP_TXCSUM; 1735189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) 1736189714Syongari ifp->if_hwassist |= TXP_CSUM_FEATURES; 1737189714Syongari else 1738189714Syongari ifp->if_hwassist &= ~TXP_CSUM_FEATURES; 1739189714Syongari } 1740189714Syongari if ((mask & IFCAP_RXCSUM) != 0 && 1741189714Syongari (ifp->if_capabilities & IFCAP_RXCSUM) != 0) 1742189714Syongari ifp->if_capenable ^= IFCAP_RXCSUM; 1743189714Syongari if ((mask & IFCAP_WOL_MAGIC) != 0 && 1744189714Syongari (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) 1745189714Syongari ifp->if_capenable ^= IFCAP_WOL_MAGIC; 1746189714Syongari if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && 1747189714Syongari (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) 1748189714Syongari ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 1749189714Syongari if ((mask & IFCAP_VLAN_HWCSUM) != 0 && 1750189714Syongari (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) 1751189714Syongari ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; 1752189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) == 0) 1753189714Syongari ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; 1754189714Syongari if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) 1755189714Syongari ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; 1756189714Syongari if (capenable != ifp->if_capenable) 1757189714Syongari txp_set_capabilities(sc); 1758189714Syongari TXP_UNLOCK(sc); 1759189714Syongari VLAN_CAPABILITIES(ifp); 1760189714Syongari break; 176180219Swpaul case SIOCGIFMEDIA: 176280219Swpaul case SIOCSIFMEDIA: 176380219Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command); 176480219Swpaul break; 176580219Swpaul default: 1766106937Ssam error = ether_ioctl(ifp, command, data); 176780219Swpaul break; 176880219Swpaul } 176980219Swpaul 1770189688Syongari return (error); 177180219Swpaul} 177280219Swpaul 177380219Swpaulstatic int 1774189685Syongaritxp_rxring_fill(struct txp_softc *sc) 177580219Swpaul{ 1776189714Syongari struct txp_rxbuf_desc *rbd; 1777189714Syongari struct txp_rx_swdesc *sd; 1778189714Syongari bus_dma_segment_t segs[1]; 1779189714Syongari int error, i, nsegs; 178080219Swpaul 1781151772Sjhb TXP_LOCK_ASSERT(sc); 178280219Swpaul 1783189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1784189714Syongari sc->sc_cdata.txp_rxbufs_map, 1785189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1786189714Syongari 178780219Swpaul for (i = 0; i < RXBUF_ENTRIES; i++) { 1788189714Syongari sd = TAILQ_FIRST(&sc->sc_free_list); 1789189714Syongari if (sd == NULL) 1790189714Syongari return (ENOMEM); 1791189714Syongari rbd = sc->sc_rxbufs + i; 1792189714Syongari bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd)); 1793189714Syongari KASSERT(sd->sd_mbuf == NULL, 1794189714Syongari ("%s : Rx buffer ring corrupted", __func__)); 1795151772Sjhb sd->sd_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 179680219Swpaul if (sd->sd_mbuf == NULL) 1797189714Syongari return (ENOMEM); 179880219Swpaul sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; 1799189714Syongari#ifndef __NO_STRICT_ALIGNMENT 1800189714Syongari m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN); 1801189714Syongari#endif 1802189714Syongari if ((error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag, 1803189714Syongari sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0)) != 0) { 1804189714Syongari m_freem(sd->sd_mbuf); 1805189714Syongari sd->sd_mbuf = NULL; 1806189714Syongari return (error); 1807189714Syongari } 1808189714Syongari KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__, 1809189714Syongari nsegs)); 1810189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1811189714Syongari TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next); 1812189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1813189714Syongari BUS_DMASYNC_PREREAD); 1814189714Syongari rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr)); 1815189714Syongari rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr)); 181680219Swpaul } 181780219Swpaul 1818189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1819189714Syongari sc->sc_cdata.txp_rxbufs_map, 1820189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1821189714Syongari sc->sc_rxbufprod = RXBUF_ENTRIES - 1; 1822189714Syongari sc->sc_hostvar->hv_rx_buf_write_idx = 1823189714Syongari htole32(TXP_IDX2OFFSET(RXBUF_ENTRIES - 1)); 182480219Swpaul 1825189688Syongari return (0); 182680219Swpaul} 182780219Swpaul 182880219Swpaulstatic void 1829189685Syongaritxp_rxring_empty(struct txp_softc *sc) 183080219Swpaul{ 1831189714Syongari struct txp_rx_swdesc *sd; 1832189714Syongari int cnt; 183380219Swpaul 1834151772Sjhb TXP_LOCK_ASSERT(sc); 1835189714Syongari 183680219Swpaul if (sc->sc_rxbufs == NULL) 183780219Swpaul return; 1838189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1839189714Syongari sc->sc_cdata.txp_hostvar_map, 1840189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 184180219Swpaul 1842189714Syongari /* Release allocated Rx buffers. */ 1843189714Syongari cnt = 0; 1844189714Syongari while ((sd = TAILQ_FIRST(&sc->sc_busy_list)) != NULL) { 1845189714Syongari TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next); 1846189714Syongari KASSERT(sd->sd_mbuf != NULL, 1847189714Syongari ("%s : Rx buffer ring corrupted", __func__)); 1848189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1849189714Syongari BUS_DMASYNC_POSTREAD); 1850189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map); 1851189714Syongari m_freem(sd->sd_mbuf); 1852189714Syongari sd->sd_mbuf = NULL; 1853189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 1854189714Syongari cnt++; 185580219Swpaul } 185680219Swpaul} 185780219Swpaul 185880219Swpaulstatic void 1859189685Syongaritxp_init(void *xsc) 186080219Swpaul{ 186180219Swpaul struct txp_softc *sc; 1862151772Sjhb 1863151772Sjhb sc = xsc; 1864151772Sjhb TXP_LOCK(sc); 1865151772Sjhb txp_init_locked(sc); 1866151772Sjhb TXP_UNLOCK(sc); 1867151772Sjhb} 1868151772Sjhb 1869151772Sjhbstatic void 1870189685Syongaritxp_init_locked(struct txp_softc *sc) 1871151772Sjhb{ 187280219Swpaul struct ifnet *ifp; 1873189714Syongari uint8_t *eaddr; 1874189689Syongari uint16_t p1; 1875189689Syongari uint32_t p2; 1876189714Syongari int error; 187780219Swpaul 1878151772Sjhb TXP_LOCK_ASSERT(sc); 1879147256Sbrooks ifp = sc->sc_ifp; 188080219Swpaul 1881189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 188280219Swpaul return; 188380219Swpaul 1884189714Syongari /* Initialize ring structure. */ 1885189714Syongari txp_init_rings(sc); 1886189714Syongari /* Wakeup controller. */ 1887189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_WAKEUP); 1888189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 1889189714Syongari /* 1890189714Syongari * It seems that earlier NV image can go back to online from 1891189714Syongari * wakeup command but newer ones require controller reset. 1892189714Syongari * So jut reset controller again. 1893189714Syongari */ 1894189714Syongari if (txp_reset(sc) != 0) 1895189714Syongari goto init_fail; 1896189714Syongari /* Download firmware. */ 1897189714Syongari error = txp_download_fw(sc); 1898189714Syongari if (error != 0) { 1899189714Syongari device_printf(sc->sc_dev, "could not download firmware.\n"); 1900189714Syongari goto init_fail; 1901189714Syongari } 1902189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1903189714Syongari sc->sc_cdata.txp_hostvar_map, 1904189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1905189714Syongari if ((error = txp_rxring_fill(sc)) != 0) { 1906189714Syongari device_printf(sc->sc_dev, "no memory for Rx buffers.\n"); 1907189714Syongari goto init_fail; 1908189714Syongari } 1909189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1910189714Syongari sc->sc_cdata.txp_hostvar_map, 1911189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1912189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_BOOT) != 0) { 1913189714Syongari device_printf(sc->sc_dev, "could not boot firmware.\n"); 1914189714Syongari goto init_fail; 1915189714Syongari } 191680219Swpaul 1917189714Syongari /* 1918189714Syongari * Quite contrary to Typhoon T2 software functional specification, 1919189714Syongari * it seems that TXP_CMD_RECV_BUFFER_CONTROL command is not 1920189714Syongari * implemented in the firmware. This means driver should have to 1921189714Syongari * handle misaligned frames on alignment architectures. AFAIK this 1922189714Syongari * is the only controller manufactured by 3Com that has this stupid 1923189714Syongari * bug. 3Com should fix this. 1924189714Syongari */ 1925189714Syongari if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0, 1926189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1927189714Syongari goto init_fail; 1928189714Syongari /* Undocumented command(interrupt coalescing disable?) - From Linux. */ 1929189714Syongari if (txp_command(sc, TXP_CMD_FILTER_DEFINE, 0, 0, 0, NULL, NULL, NULL, 1930189714Syongari TXP_CMD_NOWAIT) != 0) 1931189714Syongari goto init_fail; 193280219Swpaul 193380219Swpaul /* Set station address. */ 1934189714Syongari eaddr = IF_LLADDR(sc->sc_ifp); 1935189714Syongari p1 = 0; 1936189714Syongari ((uint8_t *)&p1)[1] = eaddr[0]; 1937189714Syongari ((uint8_t *)&p1)[0] = eaddr[1]; 1938189714Syongari p1 = le16toh(p1); 1939189714Syongari ((uint8_t *)&p2)[3] = eaddr[2]; 1940189714Syongari ((uint8_t *)&p2)[2] = eaddr[3]; 1941189714Syongari ((uint8_t *)&p2)[1] = eaddr[4]; 1942189714Syongari ((uint8_t *)&p2)[0] = eaddr[5]; 1943189714Syongari p2 = le32toh(p2); 1944189714Syongari if (txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, 1945189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1946189714Syongari goto init_fail; 194780219Swpaul 194880219Swpaul txp_set_filter(sc); 1949189714Syongari txp_set_capabilities(sc); 195080219Swpaul 1951189714Syongari if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0, 1952189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT)) 1953189714Syongari goto init_fail; 1954189714Syongari if (txp_command(sc, TXP_CMD_XCVR_SELECT, sc->sc_xcvr, 0, 0, 1955189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1956189714Syongari goto init_fail; 1957189714Syongari if (txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1958189714Syongari TXP_CMD_NOWAIT) != 0) 1959189714Syongari goto init_fail; 1960189714Syongari if (txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1961189714Syongari TXP_CMD_NOWAIT) != 0) 1962189714Syongari goto init_fail; 196380219Swpaul 1964189714Syongari /* Ack all pending interrupts and enable interrupts. */ 1965189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 1966189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTRS); 1967189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE); 196880219Swpaul 1969148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 1970148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 197180219Swpaul 1972151772Sjhb callout_reset(&sc->sc_tick, hz, txp_tick, sc); 1973189714Syongari return; 1974189714Syongari 1975189714Syongariinit_fail: 1976189714Syongari txp_rxring_empty(sc); 1977189714Syongari txp_init_rings(sc); 1978189714Syongari txp_reset(sc); 1979189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 198080219Swpaul} 198180219Swpaul 198280219Swpaulstatic void 1983189685Syongaritxp_tick(void *vsc) 198480219Swpaul{ 1985189714Syongari struct txp_softc *sc; 1986189714Syongari struct ifnet *ifp; 1987189714Syongari struct txp_rsp_desc *rsp; 198880219Swpaul struct txp_ext_desc *ext; 1989189714Syongari int link; 199080219Swpaul 1991189714Syongari sc = vsc; 1992151772Sjhb TXP_LOCK_ASSERT(sc); 1993189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1994189714Syongari sc->sc_cdata.txp_hostvar_map, 1995189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 199680219Swpaul txp_rxbuf_reclaim(sc); 1997189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1998189714Syongari sc->sc_cdata.txp_hostvar_map, 1999189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 200080219Swpaul 2001189714Syongari ifp = sc->sc_ifp; 2002189714Syongari rsp = NULL; 2003189714Syongari 2004189714Syongari link = sc->sc_flags & TXP_FLAG_LINK; 2005189714Syongari if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0, 2006189714Syongari &rsp, TXP_CMD_WAIT)) 200780219Swpaul goto out; 200880219Swpaul if (rsp->rsp_numdesc != 6) 200980219Swpaul goto out; 2010189714Syongari txp_stats_update(sc, rsp); 2011189714Syongari if (link == 0 && (sc->sc_flags & TXP_FLAG_LINK) != 0) { 2012189714Syongari ext = (struct txp_ext_desc *)(rsp + 1); 2013189714Syongari /* Update baudrate with resolved speed. */ 2014189714Syongari if ((ext[5].ext_2 & 0x02) != 0) 2015189714Syongari ifp->if_baudrate = IF_Mbps(100); 2016189714Syongari else 2017189714Syongari ifp->if_baudrate = IF_Mbps(10); 2018189714Syongari } 201980219Swpaul 202080219Swpaulout: 202180219Swpaul if (rsp != NULL) 202280219Swpaul free(rsp, M_DEVBUF); 2023189714Syongari txp_watchdog(sc); 2024151772Sjhb callout_reset(&sc->sc_tick, hz, txp_tick, sc); 202580219Swpaul} 202680219Swpaul 202780219Swpaulstatic void 2028189685Syongaritxp_start(struct ifnet *ifp) 202980219Swpaul{ 2030151772Sjhb struct txp_softc *sc; 2031151772Sjhb 2032151772Sjhb sc = ifp->if_softc; 2033151772Sjhb TXP_LOCK(sc); 2034151772Sjhb txp_start_locked(ifp); 2035151772Sjhb TXP_UNLOCK(sc); 2036151772Sjhb} 2037151772Sjhb 2038151772Sjhbstatic void 2039189685Syongaritxp_start_locked(struct ifnet *ifp) 2040151772Sjhb{ 2041189714Syongari struct txp_softc *sc; 2042189714Syongari struct mbuf *m_head; 2043189714Syongari int enq; 204480219Swpaul 2045189714Syongari sc = ifp->if_softc; 2046151772Sjhb TXP_LOCK_ASSERT(sc); 2047189714Syongari 2048148887Srwatson if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 2049189714Syongari IFF_DRV_RUNNING || (sc->sc_flags & TXP_FLAG_LINK) == 0) 205080219Swpaul return; 205180219Swpaul 2052189714Syongari for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { 2053189714Syongari IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 2054189714Syongari if (m_head == NULL) 205580219Swpaul break; 2056189714Syongari /* 2057189714Syongari * Pack the data into the transmit ring. If we 2058189714Syongari * don't have room, set the OACTIVE flag and wait 2059189714Syongari * for the NIC to drain the ring. 2060189714Syongari * ATM only Hi-ring is used. 2061189714Syongari */ 2062189714Syongari if (txp_encap(sc, &sc->sc_txhir, &m_head)) { 2063189714Syongari if (m_head == NULL) 2064189714Syongari break; 2065189714Syongari IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 2066189714Syongari ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2067189714Syongari break; 2068189714Syongari } 206980219Swpaul 2070189714Syongari /* 2071189714Syongari * If there's a BPF listener, bounce a copy of this frame 2072189714Syongari * to him. 2073189714Syongari */ 2074189714Syongari ETHER_BPF_MTAP(ifp, m_head); 207580219Swpaul 2076189714Syongari /* Send queued frame. */ 2077189714Syongari WRITE_REG(sc, sc->sc_txhir.r_reg, 2078189714Syongari TXP_IDX2OFFSET(sc->sc_txhir.r_prod)); 2079189714Syongari } 208080219Swpaul 2081189714Syongari if (enq > 0) { 2082189714Syongari /* Set a timeout in case the chip goes out to lunch. */ 2083189714Syongari sc->sc_watchdog_timer = TXP_TX_TIMEOUT; 2084189714Syongari } 2085189714Syongari} 208680219Swpaul 2087189714Syongaristatic int 2088189714Syongaritxp_encap(struct txp_softc *sc, struct txp_tx_ring *r, struct mbuf **m_head) 2089189714Syongari{ 2090189714Syongari struct txp_tx_desc *first_txd; 2091189714Syongari struct txp_frag_desc *fxd; 2092189714Syongari struct txp_swdesc *sd; 2093189714Syongari struct mbuf *m; 2094189714Syongari bus_dma_segment_t txsegs[TXP_MAXTXSEGS]; 2095189714Syongari int error, i, nsegs; 209680219Swpaul 2097189714Syongari TXP_LOCK_ASSERT(sc); 209880219Swpaul 2099189714Syongari M_ASSERTPKTHDR((*m_head)); 210080219Swpaul 2101189714Syongari m = *m_head; 2102189714Syongari first_txd = r->r_desc + r->r_prod; 2103189714Syongari sd = sc->sc_txd + r->r_prod; 210480219Swpaul 2105189714Syongari error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, sd->sd_map, 2106189714Syongari *m_head, txsegs, &nsegs, 0); 2107189714Syongari if (error == EFBIG) { 2108189714Syongari m = m_collapse(*m_head, M_DONTWAIT, TXP_MAXTXSEGS); 2109189714Syongari if (m == NULL) { 2110189714Syongari m_freem(*m_head); 2111189714Syongari *m_head = NULL; 2112189714Syongari return (ENOMEM); 211380219Swpaul } 2114189714Syongari *m_head = m; 2115189714Syongari error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, 2116189714Syongari sd->sd_map, *m_head, txsegs, &nsegs, 0); 2117189714Syongari if (error != 0) { 2118189714Syongari m_freem(*m_head); 2119189714Syongari *m_head = NULL; 2120189714Syongari return (error); 2121189714Syongari } 2122189714Syongari } else if (error != 0) 2123189714Syongari return (error); 2124189714Syongari if (nsegs == 0) { 2125189714Syongari m_freem(*m_head); 2126189714Syongari *m_head = NULL; 2127189714Syongari return (EIO); 2128189714Syongari } 212983115Sbrooks 2130189714Syongari /* Check descriptor overrun. */ 2131189714Syongari if (r->r_cnt + nsegs >= TX_ENTRIES - TXP_TXD_RESERVED) { 2132189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, sd->sd_map); 2133189714Syongari return (ENOBUFS); 2134189714Syongari } 2135189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, sd->sd_map, 2136189714Syongari BUS_DMASYNC_PREWRITE); 2137189714Syongari sd->sd_mbuf = m; 213880219Swpaul 2139189714Syongari first_txd->tx_flags = TX_FLAGS_TYPE_DATA; 2140189714Syongari first_txd->tx_numdesc = 0; 2141189714Syongari first_txd->tx_addrlo = 0; 2142189714Syongari first_txd->tx_addrhi = 0; 2143189714Syongari first_txd->tx_totlen = 0; 2144189714Syongari first_txd->tx_pflags = 0; 2145189714Syongari r->r_cnt++; 2146189714Syongari TXP_DESC_INC(r->r_prod, TX_ENTRIES); 2147189714Syongari 2148189714Syongari /* Configure Tx IP/TCP/UDP checksum offload. */ 2149189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) 2150189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_IPCKSUM); 2151189714Syongari#ifdef notyet 2152189714Syongari /* XXX firmware bug. */ 2153189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0) 2154189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_TCPCKSUM); 2155189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0) 2156189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_UDPCKSUM); 215780219Swpaul#endif 215880219Swpaul 2159189714Syongari /* Configure VLAN hardware tag insertion. */ 2160189714Syongari if ((m->m_flags & M_VLANTAG) != 0) 2161189714Syongari first_txd->tx_pflags |= 2162189714Syongari htole32(TX_PFLAGS_VLAN | TX_PFLAGS_PRIO | 2163189714Syongari (bswap16(m->m_pkthdr.ether_vtag) << TX_PFLAGS_VLANTAG_S)); 216480219Swpaul 2165189714Syongari for (i = 0; i < nsegs; i++) { 2166189714Syongari fxd = (struct txp_frag_desc *)(r->r_desc + r->r_prod); 2167189714Syongari fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG | TX_FLAGS_VALID; 2168189714Syongari fxd->frag_rsvd1 = 0; 2169189714Syongari fxd->frag_len = htole16(txsegs[i].ds_len); 2170189714Syongari fxd->frag_addrhi = htole32(TXP_ADDR_HI(txsegs[i].ds_addr)); 2171189714Syongari fxd->frag_addrlo = htole32(TXP_ADDR_LO(txsegs[i].ds_addr)); 2172189714Syongari fxd->frag_rsvd2 = 0; 2173189714Syongari first_txd->tx_numdesc++; 2174189714Syongari r->r_cnt++; 2175189714Syongari TXP_DESC_INC(r->r_prod, TX_ENTRIES); 2176189714Syongari } 217780219Swpaul 2178189714Syongari /* Lastly set valid flag. */ 2179189714Syongari first_txd->tx_flags |= TX_FLAGS_VALID; 218080219Swpaul 2181189714Syongari /* Sync descriptors. */ 2182189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, 2183189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 218480219Swpaul 2185189714Syongari return (0); 218680219Swpaul} 218780219Swpaul 218880219Swpaul/* 218980219Swpaul * Handle simple commands sent to the typhoon 219080219Swpaul */ 219180219Swpaulstatic int 2192189689Syongaritxp_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2, 2193189689Syongari uint32_t in3, uint16_t *out1, uint32_t *out2, uint32_t *out3, int wait) 219480219Swpaul{ 2195189714Syongari struct txp_rsp_desc *rsp; 219680219Swpaul 2197189714Syongari rsp = NULL; 2198189714Syongari if (txp_ext_command(sc, id, in1, in2, in3, NULL, 0, &rsp, wait) != 0) { 2199189714Syongari device_printf(sc->sc_dev, "command 0x%02x failed\n", id); 220080219Swpaul return (-1); 2201189714Syongari } 220280219Swpaul 2203189714Syongari if (wait == TXP_CMD_NOWAIT) 220480219Swpaul return (0); 220580219Swpaul 2206189714Syongari KASSERT(rsp != NULL, ("rsp is NULL!\n")); 220780219Swpaul if (out1 != NULL) 2208189714Syongari *out1 = le16toh(rsp->rsp_par1); 220980219Swpaul if (out2 != NULL) 2210189714Syongari *out2 = le32toh(rsp->rsp_par2); 221180219Swpaul if (out3 != NULL) 2212189714Syongari *out3 = le32toh(rsp->rsp_par3); 221380219Swpaul free(rsp, M_DEVBUF); 221480219Swpaul return (0); 221580219Swpaul} 221680219Swpaul 221780219Swpaulstatic int 2218189714Syongaritxp_ext_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2, 2219189689Syongari uint32_t in3, struct txp_ext_desc *in_extp, uint8_t in_extn, 2220189004Srdivacky struct txp_rsp_desc **rspp, int wait) 222180219Swpaul{ 2222189714Syongari struct txp_hostvar *hv; 222380219Swpaul struct txp_cmd_desc *cmd; 222480219Swpaul struct txp_ext_desc *ext; 2225189689Syongari uint32_t idx, i; 2226189689Syongari uint16_t seq; 2227189714Syongari int error; 222880219Swpaul 2229189714Syongari error = 0; 2230189714Syongari hv = sc->sc_hostvar; 223180219Swpaul if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) { 2232189714Syongari device_printf(sc->sc_dev, 2233189714Syongari "%s : out of free cmd descriptors for command 0x%02x\n", 2234189714Syongari __func__, id); 2235189714Syongari return (ENOBUFS); 223680219Swpaul } 223780219Swpaul 2238189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag, 2239189714Syongari sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_POSTWRITE); 224080219Swpaul idx = sc->sc_cmdring.lastwrite; 2241189689Syongari cmd = (struct txp_cmd_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx); 224280219Swpaul bzero(cmd, sizeof(*cmd)); 224380219Swpaul 224480219Swpaul cmd->cmd_numdesc = in_extn; 2245189714Syongari seq = sc->sc_seq++; 2246189714Syongari cmd->cmd_seq = htole16(seq); 2247189714Syongari cmd->cmd_id = htole16(id); 2248189714Syongari cmd->cmd_par1 = htole16(in1); 2249189714Syongari cmd->cmd_par2 = htole32(in2); 2250189714Syongari cmd->cmd_par3 = htole32(in3); 225180219Swpaul cmd->cmd_flags = CMD_FLAGS_TYPE_CMD | 2252189714Syongari (wait == TXP_CMD_WAIT ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID; 225380219Swpaul 225480219Swpaul idx += sizeof(struct txp_cmd_desc); 225580219Swpaul if (idx == sc->sc_cmdring.size) 225680219Swpaul idx = 0; 225780219Swpaul 225880219Swpaul for (i = 0; i < in_extn; i++) { 2259189689Syongari ext = (struct txp_ext_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx); 226080219Swpaul bcopy(in_extp, ext, sizeof(struct txp_ext_desc)); 226180219Swpaul in_extp++; 226280219Swpaul idx += sizeof(struct txp_cmd_desc); 226380219Swpaul if (idx == sc->sc_cmdring.size) 226480219Swpaul idx = 0; 226580219Swpaul } 226680219Swpaul 226780219Swpaul sc->sc_cmdring.lastwrite = idx; 2268189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag, 2269189714Syongari sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_PREWRITE); 2270189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2271189714Syongari sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_PREREAD | 2272189714Syongari BUS_DMASYNC_PREWRITE); 227380219Swpaul WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite); 2274189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 227580219Swpaul 2276189714Syongari if (wait == TXP_CMD_NOWAIT) 227780219Swpaul return (0); 227880219Swpaul 2279189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 2280189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2281189714Syongari sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_POSTREAD | 2282189714Syongari BUS_DMASYNC_POSTWRITE); 2283189714Syongari if (le32toh(hv->hv_resp_read_idx) != 2284189714Syongari le32toh(hv->hv_resp_write_idx)) { 2285189714Syongari error = txp_response(sc, id, seq, rspp); 2286189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2287189714Syongari sc->sc_cdata.txp_hostvar_map, 2288189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2289189714Syongari if (error != 0) 2290189714Syongari return (error); 2291189714Syongari if (*rspp != NULL) 229280219Swpaul break; 229380219Swpaul } 229480219Swpaul DELAY(50); 229580219Swpaul } 2296189714Syongari if (i == TXP_TIMEOUT) { 2297189714Syongari device_printf(sc->sc_dev, "command 0x%02x timedout\n", id); 2298189714Syongari error = ETIMEDOUT; 229980219Swpaul } 230080219Swpaul 2301189714Syongari return (error); 230280219Swpaul} 230380219Swpaul 230480219Swpaulstatic int 2305189714Syongaritxp_response(struct txp_softc *sc, uint16_t id, uint16_t seq, 2306189004Srdivacky struct txp_rsp_desc **rspp) 230780219Swpaul{ 2308189714Syongari struct txp_hostvar *hv; 230980219Swpaul struct txp_rsp_desc *rsp; 2310189714Syongari uint32_t ridx; 231180219Swpaul 2312189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rspring_tag, 2313189714Syongari sc->sc_cdata.txp_rspring_map, BUS_DMASYNC_POSTREAD); 2314189714Syongari hv = sc->sc_hostvar; 2315189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 2316189714Syongari while (ridx != le32toh(hv->hv_resp_write_idx)) { 2317189689Syongari rsp = (struct txp_rsp_desc *)(((uint8_t *)sc->sc_rspring.base) + ridx); 231880219Swpaul 2319189714Syongari if (id == le16toh(rsp->rsp_id) && 2320189714Syongari le16toh(rsp->rsp_seq) == seq) { 232180219Swpaul *rspp = (struct txp_rsp_desc *)malloc( 232280219Swpaul sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1), 232380219Swpaul M_DEVBUF, M_NOWAIT); 2324189714Syongari if (*rspp == NULL) { 2325189714Syongari device_printf(sc->sc_dev,"%s : command 0x%02x " 2326189714Syongari "memory allocation failure\n", 2327189714Syongari __func__, id); 2328189714Syongari return (ENOMEM); 2329189714Syongari } 233080219Swpaul txp_rsp_fixup(sc, rsp, *rspp); 233180219Swpaul return (0); 233280219Swpaul } 233380219Swpaul 2334189714Syongari if ((rsp->rsp_flags & RSP_FLAGS_ERROR) != 0) { 2335189714Syongari device_printf(sc->sc_dev, 2336189714Syongari "%s : command 0x%02x response error!\n", __func__, 2337189714Syongari le16toh(rsp->rsp_id)); 233880219Swpaul txp_rsp_fixup(sc, rsp, NULL); 2339189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 234080219Swpaul continue; 234180219Swpaul } 234280219Swpaul 2343189714Syongari /* 2344189714Syongari * The following unsolicited responses are handled during 2345189714Syongari * processing of TXP_CMD_READ_STATISTICS which requires 2346189714Syongari * response. Driver abuses the command to detect media 2347189714Syongari * status change. 2348189714Syongari * TXP_CMD_FILTER_DEFINE is not an unsolicited response 2349189714Syongari * but we don't process response ring in interrupt handler 2350189714Syongari * so we have to ignore this command here, otherwise 2351189714Syongari * unknown command message would be printed. 2352189714Syongari */ 2353189714Syongari switch (le16toh(rsp->rsp_id)) { 235480219Swpaul case TXP_CMD_CYCLE_STATISTICS: 2355189714Syongari case TXP_CMD_FILTER_DEFINE: 2356189714Syongari break; 235780219Swpaul case TXP_CMD_MEDIA_STATUS_READ: 2358189714Syongari if ((le16toh(rsp->rsp_par1) & 0x0800) == 0) { 2359189714Syongari sc->sc_flags |= TXP_FLAG_LINK; 2360189714Syongari if_link_state_change(sc->sc_ifp, 2361189714Syongari LINK_STATE_UP); 2362189714Syongari } else { 2363189714Syongari sc->sc_flags &= ~TXP_FLAG_LINK; 2364189714Syongari if_link_state_change(sc->sc_ifp, 2365189714Syongari LINK_STATE_DOWN); 2366189714Syongari } 236780219Swpaul break; 236880219Swpaul case TXP_CMD_HELLO_RESPONSE: 2369189714Syongari /* 2370189714Syongari * Driver should repsond to hello message but 2371189714Syongari * TXP_CMD_READ_STATISTICS is issued for every 2372189714Syongari * hz, therefore there is no need to send an 2373189714Syongari * explicit command here. 2374189714Syongari */ 2375189714Syongari device_printf(sc->sc_dev, "%s : hello\n", __func__); 237680219Swpaul break; 237780219Swpaul default: 2378189714Syongari device_printf(sc->sc_dev, 2379189714Syongari "%s : unknown command 0x%02x\n", __func__, 2380189714Syongari le16toh(rsp->rsp_id)); 238180219Swpaul } 238280219Swpaul txp_rsp_fixup(sc, rsp, NULL); 2383189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 238480219Swpaul } 238580219Swpaul 238680219Swpaul return (0); 238780219Swpaul} 238880219Swpaul 238980219Swpaulstatic void 2390189685Syongaritxp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp, 2391189685Syongari struct txp_rsp_desc *dst) 239280219Swpaul{ 2393189714Syongari struct txp_rsp_desc *src; 2394189714Syongari struct txp_hostvar *hv; 2395189689Syongari uint32_t i, ridx; 239680219Swpaul 2397189714Syongari src = rsp; 2398189714Syongari hv = sc->sc_hostvar; 2399189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 240080219Swpaul 240180219Swpaul for (i = 0; i < rsp->rsp_numdesc + 1; i++) { 240280219Swpaul if (dst != NULL) 240380219Swpaul bcopy(src, dst++, sizeof(struct txp_rsp_desc)); 240480219Swpaul ridx += sizeof(struct txp_rsp_desc); 240580219Swpaul if (ridx == sc->sc_rspring.size) { 240680219Swpaul src = sc->sc_rspring.base; 240780219Swpaul ridx = 0; 240880219Swpaul } else 240980219Swpaul src++; 2410189714Syongari sc->sc_rspring.lastwrite = ridx; 241180219Swpaul } 2412189686Syongari 2413189714Syongari hv->hv_resp_read_idx = htole32(ridx); 241480219Swpaul} 241580219Swpaul 241680219Swpaulstatic int 2417189685Syongaritxp_cmd_desc_numfree(struct txp_softc *sc) 241880219Swpaul{ 2419189714Syongari struct txp_hostvar *hv; 2420189714Syongari struct txp_boot_record *br; 2421189689Syongari uint32_t widx, ridx, nfree; 242280219Swpaul 2423189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2424189714Syongari sc->sc_cdata.txp_hostvar_map, 2425189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2426189714Syongari hv = sc->sc_hostvar; 2427189714Syongari br = sc->sc_boot; 242880219Swpaul widx = sc->sc_cmdring.lastwrite; 2429189714Syongari ridx = le32toh(hv->hv_cmd_read_idx); 243080219Swpaul 243180219Swpaul if (widx == ridx) { 243280219Swpaul /* Ring is completely free */ 2433189714Syongari nfree = le32toh(br->br_cmd_siz) - sizeof(struct txp_cmd_desc); 243480219Swpaul } else { 243580219Swpaul if (widx > ridx) 2436189714Syongari nfree = le32toh(br->br_cmd_siz) - 243780219Swpaul (widx - ridx + sizeof(struct txp_cmd_desc)); 243880219Swpaul else 243980219Swpaul nfree = ridx - widx - sizeof(struct txp_cmd_desc); 244080219Swpaul } 244180219Swpaul 244280219Swpaul return (nfree / sizeof(struct txp_cmd_desc)); 244380219Swpaul} 244480219Swpaul 2445189714Syongaristatic int 2446189714Syongaritxp_sleep(struct txp_softc *sc, int capenable) 2447189714Syongari{ 2448189714Syongari uint16_t events; 2449189714Syongari int error; 2450189714Syongari 2451189714Syongari events = 0; 2452189714Syongari if ((capenable & IFCAP_WOL_MAGIC) != 0) 2453189714Syongari events |= 0x01; 2454189714Syongari error = txp_command(sc, TXP_CMD_ENABLE_WAKEUP_EVENTS, events, 0, 0, 2455189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 2456189714Syongari if (error == 0) { 2457189714Syongari /* Goto sleep. */ 2458189714Syongari error = txp_command(sc, TXP_CMD_GOTO_SLEEP, 0, 0, 0, NULL, 2459189714Syongari NULL, NULL, TXP_CMD_NOWAIT); 2460189714Syongari if (error == 0) { 2461189714Syongari error = txp_wait(sc, STAT_SLEEPING); 2462189714Syongari if (error != 0) 2463189714Syongari device_printf(sc->sc_dev, 2464189714Syongari "unable to enter into sleep\n"); 2465189714Syongari } 2466189714Syongari } 2467189714Syongari 2468189714Syongari return (error); 2469189714Syongari} 2470189714Syongari 247180219Swpaulstatic void 2472189685Syongaritxp_stop(struct txp_softc *sc) 247380219Swpaul{ 247480219Swpaul struct ifnet *ifp; 247580219Swpaul 2476151772Sjhb TXP_LOCK_ASSERT(sc); 2477147256Sbrooks ifp = sc->sc_ifp; 247880219Swpaul 2479189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2480189714Syongari return; 2481189714Syongari 2482189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 2483189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 2484189714Syongari 2485148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2486189714Syongari sc->sc_flags &= ~TXP_FLAG_LINK; 248780219Swpaul 2488151772Sjhb callout_stop(&sc->sc_tick); 248980219Swpaul 2490189714Syongari txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 2491189714Syongari TXP_CMD_NOWAIT); 2492189714Syongari txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 2493189714Syongari TXP_CMD_NOWAIT); 2494189714Syongari /* Save statistics for later use. */ 2495189714Syongari txp_stats_save(sc); 2496189714Syongari /* Halt controller. */ 2497189714Syongari txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 2498189714Syongari TXP_CMD_NOWAIT); 249980219Swpaul 2500189714Syongari if (txp_wait(sc, STAT_HALTED) != 0) 2501189714Syongari device_printf(sc->sc_dev, "controller halt timedout!\n"); 2502189714Syongari /* Reclaim Tx/Rx buffers. */ 2503189714Syongari if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons != 2504189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off))))) 2505189714Syongari txp_tx_reclaim(sc, &sc->sc_txhir); 2506189714Syongari if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons != 2507189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off))))) 2508189714Syongari txp_tx_reclaim(sc, &sc->sc_txlor); 250980219Swpaul txp_rxring_empty(sc); 2510189714Syongari 2511189714Syongari txp_init_rings(sc); 2512189714Syongari /* Reset controller and make it reload sleep image. */ 2513189714Syongari txp_reset(sc); 2514189714Syongari /* Let controller boot from sleep image. */ 2515189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) 2516189714Syongari device_printf(sc->sc_dev, "could not boot sleep image\n"); 2517189714Syongari txp_sleep(sc, 0); 251880219Swpaul} 251980219Swpaul 252080219Swpaulstatic void 2521189714Syongaritxp_watchdog(struct txp_softc *sc) 252280219Swpaul{ 2523189714Syongari struct ifnet *ifp; 2524189687Syongari 2525189714Syongari TXP_LOCK_ASSERT(sc); 2526189714Syongari 2527189714Syongari if (sc->sc_watchdog_timer == 0 || --sc->sc_watchdog_timer) 2528189714Syongari return; 2529189714Syongari 2530189714Syongari ifp = sc->sc_ifp; 2531189714Syongari if_printf(ifp, "watchdog timeout -- resetting\n"); 2532189714Syongari ifp->if_oerrors++; 2533189714Syongari txp_stop(sc); 2534189714Syongari txp_init_locked(sc); 253580219Swpaul} 253680219Swpaul 253780219Swpaulstatic int 2538189685Syongaritxp_ifmedia_upd(struct ifnet *ifp) 253980219Swpaul{ 254080219Swpaul struct txp_softc *sc = ifp->if_softc; 254180219Swpaul struct ifmedia *ifm = &sc->sc_ifmedia; 2542189689Syongari uint16_t new_xcvr; 254380219Swpaul 2544151772Sjhb TXP_LOCK(sc); 2545151772Sjhb if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) { 2546151772Sjhb TXP_UNLOCK(sc); 254780219Swpaul return (EINVAL); 2548151772Sjhb } 254980219Swpaul 255080219Swpaul if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) { 255180219Swpaul if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) 255280219Swpaul new_xcvr = TXP_XCVR_10_FDX; 255380219Swpaul else 255480219Swpaul new_xcvr = TXP_XCVR_10_HDX; 255580219Swpaul } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) { 255680219Swpaul if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) 255780219Swpaul new_xcvr = TXP_XCVR_100_FDX; 255880219Swpaul else 255980219Swpaul new_xcvr = TXP_XCVR_100_HDX; 256080219Swpaul } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { 256180219Swpaul new_xcvr = TXP_XCVR_AUTO; 2562151772Sjhb } else { 2563151772Sjhb TXP_UNLOCK(sc); 256480219Swpaul return (EINVAL); 2565151772Sjhb } 256680219Swpaul 256780219Swpaul /* nothing to do */ 2568151772Sjhb if (sc->sc_xcvr == new_xcvr) { 2569151772Sjhb TXP_UNLOCK(sc); 257080219Swpaul return (0); 2571151772Sjhb } 257280219Swpaul 257380219Swpaul txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0, 2574189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 257580219Swpaul sc->sc_xcvr = new_xcvr; 2576151772Sjhb TXP_UNLOCK(sc); 257780219Swpaul 257880219Swpaul return (0); 257980219Swpaul} 258080219Swpaul 258180219Swpaulstatic void 2582189685Syongaritxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 258380219Swpaul{ 258480219Swpaul struct txp_softc *sc = ifp->if_softc; 258580219Swpaul struct ifmedia *ifm = &sc->sc_ifmedia; 2586189689Syongari uint16_t bmsr, bmcr, anar, anlpar; 258780219Swpaul 258880219Swpaul ifmr->ifm_status = IFM_AVALID; 258980219Swpaul ifmr->ifm_active = IFM_ETHER; 259080219Swpaul 2591151772Sjhb TXP_LOCK(sc); 2592189714Syongari /* Check whether firmware is running. */ 2593189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2594189714Syongari goto bail; 259580219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, 2596189714Syongari &bmsr, NULL, NULL, TXP_CMD_WAIT)) 259780219Swpaul goto bail; 259880219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, 2599189714Syongari &bmsr, NULL, NULL, TXP_CMD_WAIT)) 260080219Swpaul goto bail; 260180219Swpaul 260280219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0, 2603189714Syongari &bmcr, NULL, NULL, TXP_CMD_WAIT)) 260480219Swpaul goto bail; 260580219Swpaul 260680219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0, 2607189714Syongari &anlpar, NULL, NULL, TXP_CMD_WAIT)) 260880219Swpaul goto bail; 2609173666Syongari 2610173666Syongari if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANAR, 0, 2611189714Syongari &anar, NULL, NULL, TXP_CMD_WAIT)) 2612173666Syongari goto bail; 2613151772Sjhb TXP_UNLOCK(sc); 261480219Swpaul 261580219Swpaul if (bmsr & BMSR_LINK) 261680219Swpaul ifmr->ifm_status |= IFM_ACTIVE; 261780219Swpaul 261880219Swpaul if (bmcr & BMCR_ISO) { 261980219Swpaul ifmr->ifm_active |= IFM_NONE; 262080219Swpaul ifmr->ifm_status = 0; 262180219Swpaul return; 262280219Swpaul } 262380219Swpaul 262480219Swpaul if (bmcr & BMCR_LOOP) 262580219Swpaul ifmr->ifm_active |= IFM_LOOP; 262680219Swpaul 262780219Swpaul if (bmcr & BMCR_AUTOEN) { 262880219Swpaul if ((bmsr & BMSR_ACOMP) == 0) { 262980219Swpaul ifmr->ifm_active |= IFM_NONE; 263080219Swpaul return; 263180219Swpaul } 263280219Swpaul 2633173666Syongari anlpar &= anar; 2634173665Syongari if (anlpar & ANLPAR_TX_FD) 2635173665Syongari ifmr->ifm_active |= IFM_100_TX|IFM_FDX; 2636173665Syongari else if (anlpar & ANLPAR_T4) 263780219Swpaul ifmr->ifm_active |= IFM_100_T4; 263880219Swpaul else if (anlpar & ANLPAR_TX) 263980219Swpaul ifmr->ifm_active |= IFM_100_TX; 264080219Swpaul else if (anlpar & ANLPAR_10_FD) 264180219Swpaul ifmr->ifm_active |= IFM_10_T|IFM_FDX; 264280219Swpaul else if (anlpar & ANLPAR_10) 264380219Swpaul ifmr->ifm_active |= IFM_10_T; 264480219Swpaul else 264580219Swpaul ifmr->ifm_active |= IFM_NONE; 264680219Swpaul } else 264780219Swpaul ifmr->ifm_active = ifm->ifm_cur->ifm_media; 264880219Swpaul return; 264980219Swpaul 265080219Swpaulbail: 2651151772Sjhb TXP_UNLOCK(sc); 265280219Swpaul ifmr->ifm_active |= IFM_NONE; 265380219Swpaul ifmr->ifm_status &= ~IFM_AVALID; 265480219Swpaul} 265580219Swpaul 265680219Swpaul#ifdef TXP_DEBUG 265780219Swpaulstatic void 2658189685Syongaritxp_show_descriptor(void *d) 265980219Swpaul{ 266080219Swpaul struct txp_cmd_desc *cmd = d; 266180219Swpaul struct txp_rsp_desc *rsp = d; 266280219Swpaul struct txp_tx_desc *txd = d; 266380219Swpaul struct txp_frag_desc *frgd = d; 266480219Swpaul 266580219Swpaul switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) { 266680219Swpaul case CMD_FLAGS_TYPE_CMD: 266780219Swpaul /* command descriptor */ 266880219Swpaul printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 2669189714Syongari cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id), 2670189714Syongari le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1), 2671189714Syongari le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3)); 267280219Swpaul break; 267380219Swpaul case CMD_FLAGS_TYPE_RESP: 267480219Swpaul /* response descriptor */ 267580219Swpaul printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 2676189714Syongari rsp->rsp_flags, rsp->rsp_numdesc, le16toh(rsp->rsp_id), 2677189714Syongari le16toh(rsp->rsp_seq), le16toh(rsp->rsp_par1), 2678189714Syongari le32toh(rsp->rsp_par2), le32toh(rsp->rsp_par3)); 267980219Swpaul break; 268080219Swpaul case CMD_FLAGS_TYPE_DATA: 268180219Swpaul /* data header (assuming tx for now) */ 268280219Swpaul printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]", 2683189714Syongari txd->tx_flags, txd->tx_numdesc, le16toh(txd->tx_totlen), 2684189714Syongari le32toh(txd->tx_addrlo), le32toh(txd->tx_addrhi), 2685189714Syongari le32toh(txd->tx_pflags)); 268680219Swpaul break; 268780219Swpaul case CMD_FLAGS_TYPE_FRAG: 268880219Swpaul /* fragment descriptor */ 268980219Swpaul printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]", 2690189714Syongari frgd->frag_flags, frgd->frag_rsvd1, le16toh(frgd->frag_len), 2691189714Syongari le32toh(frgd->frag_addrlo), le32toh(frgd->frag_addrhi), 2692189714Syongari le32toh(frgd->frag_rsvd2)); 269380219Swpaul break; 269480219Swpaul default: 269580219Swpaul printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 269680219Swpaul cmd->cmd_flags & CMD_FLAGS_TYPE_M, 2697189714Syongari cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id), 2698189714Syongari le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1), 2699189714Syongari le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3)); 270080219Swpaul break; 270180219Swpaul } 270280219Swpaul} 270380219Swpaul#endif 270480219Swpaul 270580219Swpaulstatic void 2706189685Syongaritxp_set_filter(struct txp_softc *sc) 270780219Swpaul{ 2708189690Syongari struct ifnet *ifp; 2709189690Syongari uint32_t crc, mchash[2]; 2710189689Syongari uint16_t filter; 271180219Swpaul struct ifmultiaddr *ifma; 2712189690Syongari int mcnt; 271380219Swpaul 2714189690Syongari TXP_LOCK_ASSERT(sc); 271580219Swpaul 2716189690Syongari ifp = sc->sc_ifp; 271780219Swpaul filter = TXP_RXFILT_DIRECT; 2718189690Syongari if ((ifp->if_flags & IFF_BROADCAST) != 0) 271980219Swpaul filter |= TXP_RXFILT_BROADCAST; 2720189690Syongari if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 2721189690Syongari if ((ifp->if_flags & IFF_ALLMULTI) != 0) 2722189690Syongari filter |= TXP_RXFILT_ALLMULTI; 2723189690Syongari if ((ifp->if_flags & IFF_PROMISC) != 0) 2724189690Syongari filter = TXP_RXFILT_PROMISC; 2725189690Syongari goto setit; 2726189690Syongari } 272780219Swpaul 2728189690Syongari mchash[0] = mchash[1] = 0; 2729189690Syongari mcnt = 0; 2730195049Srwatson if_maddr_rlock(ifp); 2731189690Syongari TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2732189690Syongari if (ifma->ifma_addr->sa_family != AF_LINK) 2733189690Syongari continue; 2734189690Syongari crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) 2735189690Syongari ifma->ifma_addr), ETHER_ADDR_LEN); 2736189690Syongari crc &= 0x3f; 2737189690Syongari mchash[crc >> 5] |= 1 << (crc & 0x1f); 2738189690Syongari mcnt++; 2739189690Syongari } 2740195049Srwatson if_maddr_runlock(ifp); 274180219Swpaul 2742189690Syongari if (mcnt > 0) { 2743189690Syongari filter |= TXP_RXFILT_HASHMULTI; 2744189690Syongari txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE, 2, mchash[0], 2745189714Syongari mchash[1], NULL, NULL, NULL, TXP_CMD_NOWAIT); 274680219Swpaul } 274780219Swpaul 274880219Swpaulsetit: 274980219Swpaul txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0, 2750189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 275180219Swpaul} 275280219Swpaul 2753189714Syongaristatic int 2754189714Syongaritxp_set_capabilities(struct txp_softc *sc) 2755189714Syongari{ 2756189714Syongari struct ifnet *ifp; 2757189714Syongari uint32_t rxcap, txcap; 2758189714Syongari 2759189714Syongari TXP_LOCK_ASSERT(sc); 2760189714Syongari 2761189714Syongari rxcap = txcap = 0; 2762189714Syongari ifp = sc->sc_ifp; 2763189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) { 2764189714Syongari if ((ifp->if_hwassist & CSUM_IP) != 0) 2765189714Syongari txcap |= OFFLOAD_IPCKSUM; 2766189714Syongari if ((ifp->if_hwassist & CSUM_TCP) != 0) 2767189714Syongari txcap |= OFFLOAD_TCPCKSUM; 2768189714Syongari if ((ifp->if_hwassist & CSUM_UDP) != 0) 2769189714Syongari txcap |= OFFLOAD_UDPCKSUM; 2770189714Syongari rxcap = txcap; 2771189714Syongari } 2772189714Syongari if ((ifp->if_capenable & IFCAP_RXCSUM) == 0) 2773189714Syongari rxcap &= ~(OFFLOAD_IPCKSUM | OFFLOAD_TCPCKSUM | 2774189714Syongari OFFLOAD_UDPCKSUM); 2775189714Syongari if ((ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { 2776189714Syongari rxcap |= OFFLOAD_VLAN; 2777189714Syongari txcap |= OFFLOAD_VLAN; 2778189714Syongari } 2779189714Syongari 2780189714Syongari /* Tell firmware new offload configuration. */ 2781189714Syongari return (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0, txcap, rxcap, NULL, 2782189714Syongari NULL, NULL, TXP_CMD_NOWAIT)); 2783189714Syongari} 2784189714Syongari 278580219Swpaulstatic void 2786189714Syongaritxp_stats_save(struct txp_softc *sc) 278780219Swpaul{ 2788189714Syongari struct txp_rsp_desc *rsp; 278980219Swpaul 2790189714Syongari TXP_LOCK_ASSERT(sc); 2791189714Syongari 2792189714Syongari rsp = NULL; 2793189714Syongari if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0, 2794189714Syongari &rsp, TXP_CMD_WAIT)) 279580219Swpaul goto out; 2796189714Syongari if (rsp->rsp_numdesc != 6) 2797189714Syongari goto out; 2798189714Syongari txp_stats_update(sc, rsp); 2799189714Syongariout: 2800189714Syongari if (rsp != NULL) 2801189714Syongari free(rsp, M_DEVBUF); 2802189714Syongari bcopy(&sc->sc_stats, &sc->sc_ostats, sizeof(struct txp_hw_stats)); 2803189714Syongari} 280480219Swpaul 2805189714Syongaristatic void 2806189714Syongaritxp_stats_update(struct txp_softc *sc, struct txp_rsp_desc *rsp) 2807189714Syongari{ 2808189714Syongari struct ifnet *ifp; 2809189714Syongari struct txp_hw_stats *ostats, *stats; 2810189714Syongari struct txp_ext_desc *ext; 2811189714Syongari 2812189714Syongari TXP_LOCK_ASSERT(sc); 2813189714Syongari 2814189714Syongari ifp = sc->sc_ifp; 281580219Swpaul ext = (struct txp_ext_desc *)(rsp + 1); 2816189714Syongari ostats = &sc->sc_ostats; 2817189714Syongari stats = &sc->sc_stats; 2818189714Syongari stats->tx_frames = ostats->tx_frames + le32toh(rsp->rsp_par2); 2819189714Syongari stats->tx_bytes = ostats->tx_bytes + (uint64_t)le32toh(rsp->rsp_par3) + 2820189714Syongari ((uint64_t)le32toh(ext[0].ext_1) << 32); 2821189714Syongari stats->tx_deferred = ostats->tx_deferred + le32toh(ext[0].ext_2); 2822189714Syongari stats->tx_late_colls = ostats->tx_late_colls + le32toh(ext[0].ext_3); 2823189714Syongari stats->tx_colls = ostats->tx_colls + le32toh(ext[0].ext_4); 2824189714Syongari stats->tx_carrier_lost = ostats->tx_carrier_lost + 2825189714Syongari le32toh(ext[1].ext_1); 2826189714Syongari stats->tx_multi_colls = ostats->tx_multi_colls + 2827189714Syongari le32toh(ext[1].ext_2); 2828189714Syongari stats->tx_excess_colls = ostats->tx_excess_colls + 2829189714Syongari le32toh(ext[1].ext_3); 2830189714Syongari stats->tx_fifo_underruns = ostats->tx_fifo_underruns + 2831189714Syongari le32toh(ext[1].ext_4); 2832189714Syongari stats->tx_mcast_oflows = ostats->tx_mcast_oflows + 2833189714Syongari le32toh(ext[2].ext_1); 2834189714Syongari stats->tx_filtered = ostats->tx_filtered + le32toh(ext[2].ext_2); 2835189714Syongari stats->rx_frames = ostats->rx_frames + le32toh(ext[2].ext_3); 2836189714Syongari stats->rx_bytes = ostats->rx_bytes + (uint64_t)le32toh(ext[2].ext_4) + 2837189714Syongari ((uint64_t)le32toh(ext[3].ext_1) << 32); 2838189714Syongari stats->rx_fifo_oflows = ostats->rx_fifo_oflows + le32toh(ext[3].ext_2); 2839189714Syongari stats->rx_badssd = ostats->rx_badssd + le32toh(ext[3].ext_3); 2840189714Syongari stats->rx_crcerrs = ostats->rx_crcerrs + le32toh(ext[3].ext_4); 2841189714Syongari stats->rx_lenerrs = ostats->rx_lenerrs + le32toh(ext[4].ext_1); 2842189714Syongari stats->rx_bcast_frames = ostats->rx_bcast_frames + 2843189714Syongari le32toh(ext[4].ext_2); 2844189714Syongari stats->rx_mcast_frames = ostats->rx_mcast_frames + 2845189714Syongari le32toh(ext[4].ext_3); 2846189714Syongari stats->rx_oflows = ostats->rx_oflows + le32toh(ext[4].ext_4); 2847189714Syongari stats->rx_filtered = ostats->rx_filtered + le32toh(ext[5].ext_1); 284880219Swpaul 2849189714Syongari ifp->if_ierrors = stats->rx_fifo_oflows + stats->rx_badssd + 2850189714Syongari stats->rx_crcerrs + stats->rx_lenerrs + stats->rx_oflows; 2851189714Syongari ifp->if_oerrors = stats->tx_deferred + stats->tx_carrier_lost + 2852189714Syongari stats->tx_fifo_underruns + stats->tx_mcast_oflows; 2853189714Syongari ifp->if_collisions = stats->tx_late_colls + stats->tx_multi_colls + 2854189714Syongari stats->tx_excess_colls; 2855189714Syongari ifp->if_opackets = stats->tx_frames; 2856189714Syongari ifp->if_ipackets = stats->rx_frames; 2857189714Syongari} 285880219Swpaul 2859189714Syongari#define TXP_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 2860189714Syongari SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 286180219Swpaul 2862217323Smdf#if __FreeBSD_version >= 900030 2863189714Syongari#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2864217323Smdf SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 2865217323Smdf#elif __FreeBSD_version > 800000 2866217323Smdf#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2867189714Syongari SYSCTL_ADD_QUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 2868189714Syongari#else 2869189714Syongari#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2870189714Syongari SYSCTL_ADD_ULONG(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 287180219Swpaul#endif 287280219Swpaul 2873189714Syongaristatic void 2874189714Syongaritxp_sysctl_node(struct txp_softc *sc) 2875189714Syongari{ 2876189714Syongari struct sysctl_ctx_list *ctx; 2877189714Syongari struct sysctl_oid_list *child, *parent; 2878189714Syongari struct sysctl_oid *tree; 2879189714Syongari struct txp_hw_stats *stats; 2880189714Syongari int error; 288180219Swpaul 2882189714Syongari stats = &sc->sc_stats; 2883189714Syongari ctx = device_get_sysctl_ctx(sc->sc_dev); 2884189714Syongari child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); 2885189714Syongari SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit", 2886189714Syongari CTLTYPE_INT | CTLFLAG_RW, &sc->sc_process_limit, 0, 2887189714Syongari sysctl_hw_txp_proc_limit, "I", 2888189714Syongari "max number of Rx events to process"); 2889189714Syongari /* Pull in device tunables. */ 2890189714Syongari sc->sc_process_limit = TXP_PROC_DEFAULT; 2891189714Syongari error = resource_int_value(device_get_name(sc->sc_dev), 2892189714Syongari device_get_unit(sc->sc_dev), "process_limit", 2893189714Syongari &sc->sc_process_limit); 2894189714Syongari if (error == 0) { 2895189714Syongari if (sc->sc_process_limit < TXP_PROC_MIN || 2896189714Syongari sc->sc_process_limit > TXP_PROC_MAX) { 2897189714Syongari device_printf(sc->sc_dev, 2898189714Syongari "process_limit value out of range; " 2899189714Syongari "using default: %d\n", TXP_PROC_DEFAULT); 2900189714Syongari sc->sc_process_limit = TXP_PROC_DEFAULT; 2901189714Syongari } 290280219Swpaul } 2903189714Syongari tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, 2904189714Syongari NULL, "TXP statistics"); 2905189714Syongari parent = SYSCTL_CHILDREN(tree); 290680219Swpaul 2907189714Syongari /* Tx statistics. */ 2908189714Syongari tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, 2909189714Syongari NULL, "Tx MAC statistics"); 2910189714Syongari child = SYSCTL_CHILDREN(tree); 291180219Swpaul 2912189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "frames", 2913189714Syongari &stats->tx_frames, "Frames"); 2914189714Syongari TXP_SYSCTL_STAT_ADD64(ctx, child, "octets", 2915189714Syongari &stats->tx_bytes, "Octets"); 2916189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "deferred", 2917189714Syongari &stats->tx_deferred, "Deferred frames"); 2918189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 2919189714Syongari &stats->tx_late_colls, "Late collisions"); 2920189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "colls", 2921189714Syongari &stats->tx_colls, "Collisions"); 2922189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "carrier_lost", 2923189714Syongari &stats->tx_carrier_lost, "Carrier lost"); 2924189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "multi_colls", 2925189714Syongari &stats->tx_multi_colls, "Multiple collisions"); 2926189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "excess_colls", 2927189714Syongari &stats->tx_excess_colls, "Excessive collisions"); 2928189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_underruns", 2929189714Syongari &stats->tx_fifo_underruns, "FIFO underruns"); 2930189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_oflows", 2931189714Syongari &stats->tx_mcast_oflows, "Multicast overflows"); 2932189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered", 2933189714Syongari &stats->tx_filtered, "Filtered frames"); 293480219Swpaul 2935189714Syongari /* Rx statistics. */ 2936189714Syongari tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, 2937189714Syongari NULL, "Rx MAC statistics"); 2938189714Syongari child = SYSCTL_CHILDREN(tree); 2939189714Syongari 2940189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "frames", 2941189714Syongari &stats->rx_frames, "Frames"); 2942189714Syongari TXP_SYSCTL_STAT_ADD64(ctx, child, "octets", 2943189714Syongari &stats->rx_bytes, "Octets"); 2944189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows", 2945189714Syongari &stats->rx_fifo_oflows, "FIFO overflows"); 2946189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "badssd", 2947189714Syongari &stats->rx_badssd, "Bad SSD"); 2948189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", 2949189714Syongari &stats->rx_crcerrs, "CRC errors"); 2950189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "lenerrs", 2951189714Syongari &stats->rx_lenerrs, "Length errors"); 2952189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "bcast_frames", 2953189714Syongari &stats->rx_bcast_frames, "Broadcast frames"); 2954189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_frames", 2955189714Syongari &stats->rx_mcast_frames, "Multicast frames"); 2956189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "oflows", 2957189714Syongari &stats->rx_oflows, "Overflows"); 2958189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered", 2959189714Syongari &stats->rx_filtered, "Filtered frames"); 296080219Swpaul} 2961189714Syongari 2962189714Syongari#undef TXP_SYSCTL_STAT_ADD32 2963189714Syongari#undef TXP_SYSCTL_STAT_ADD64 2964189714Syongari 2965189714Syongaristatic int 2966189714Syongarisysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) 2967189714Syongari{ 2968189714Syongari int error, value; 2969189714Syongari 2970189714Syongari if (arg1 == NULL) 2971189714Syongari return (EINVAL); 2972189714Syongari value = *(int *)arg1; 2973189714Syongari error = sysctl_handle_int(oidp, &value, 0, req); 2974189714Syongari if (error || req->newptr == NULL) 2975189714Syongari return (error); 2976189714Syongari if (value < low || value > high) 2977189714Syongari return (EINVAL); 2978189714Syongari *(int *)arg1 = value; 2979189714Syongari 2980189714Syongari return (0); 2981189714Syongari} 2982189714Syongari 2983189714Syongaristatic int 2984189714Syongarisysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS) 2985189714Syongari{ 2986189714Syongari return (sysctl_int_range(oidp, arg1, arg2, req, 2987189714Syongari TXP_PROC_MIN, TXP_PROC_MAX)); 2988189714Syongari} 2989